MySensors 2.1.0 release

This commit is contained in:
tekka007
2016-12-30 17:10:19 +01:00
216 changed files with 27512 additions and 26265 deletions
+19
View File
@@ -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
+1 -1
View File
@@ -1,5 +1,5 @@
# Set default behaviour, in case users don't have core.autocrlf set. # 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 # Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout. # to native line endings on checkout.
+4
View File
@@ -4,3 +4,7 @@ stino.settings
MyPrivateConfig.h MyPrivateConfig.h
Documentation/html Documentation/html
doxygen_sqlite3.db doxygen_sqlite3.db
Makefile.inc
build
bin
*.sublime-workspace
+96
View File
@@ -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)"
+260
View File
@@ -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.
### <a name="installtools"></a>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
```
##### <a name="buildFromSource"></a>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 sha of bootstrap-dev.sh used to detect an outdated environment>
#####git aliases
* mystoolspath = *returns the absolute path to the tools dir*
* \<bundle name> = *(e.g. cppcheck) runs the tool bundle*
NOTE: The \<bundle> aliases are auto-generated by enumerating the bundles located
in *mystoolspath*.
While the ```git <tool> 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.
+23
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
OPTIONS="--options="${TOOLCONFIG}"/style.cfg -n"
echo $OPTIONS
+19
View File
@@ -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 <no arg> 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
+90
View File
@@ -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!"
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1"?>
<platform>
<char_bit>8</char_bit>
<default-sign>signed</default-sign>
<sizeof>
<short>2</short>
<int>2</int>
<long>4</long>
<long-long>8</long-long>
<float>4</float>
<double>4</double>
<long-double>8</long-double>
<pointer>2</pointer>
<size_t>2</size_t>
<wchar_t>2</wchar_t>
</sizeof>
</platform>
+4
View File
@@ -0,0 +1,4 @@
.
drivers/Linux
drivers/AES
drivers/RPi
@@ -0,0 +1,5 @@
toomanyconfigs
missingInclude
missingIncludeSystem
ConfigurationNotChecked
unmatchedSuppression
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<platform>
<char_bit>8</char_bit>
<default-sign>unsigned</default-sign>
<sizeof>
<short>2</short>
<int>4</int>
<long>4</long>
<long-long>8</long-long>
<float>4</float>
<double>8</double>
<long-double>12</long-double>
<pointer>4</pointer>
<size_t>4</size_t>
<wchar_t>2</wchar_t>
</sizeof>
</platform>
+12
View File
@@ -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
+19
View File
@@ -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 <no arg> 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
+25
View File
@@ -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
+1 -1
View File
@@ -1 +1 @@
To get started, <a href="http://www.clahub.com/agreements/mysensors/Arduino">sign the Contributor License Agreement</a>. To get started, <a href="http://www.clahub.com/agreements/mysensors/MySensors">sign the Contributor License Agreement</a>.
View File
+154
View File
@@ -0,0 +1,154 @@
#############################################################################
#
# Makefile for MySensors
#
#
# The arduino library build part were inspired by
# Arduino-Makefile project, Copyright (C) 2012 Sudar <http://sudarmuthu.com>
#
# 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
+338 -81
View File
@@ -2,7 +2,7 @@
* The MySensors Arduino library handles the wireless radio link and protocol * The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice. * between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each * 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. * network topology allowing messages to be routed to nodes.
* *
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org> * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
@@ -67,6 +67,15 @@
#ifndef MY_BAUD_RATE #ifndef MY_BAUD_RATE
#define MY_BAUD_RATE 115200 #define MY_BAUD_RATE 115200
#endif #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 // Disables over-the-air reset of node
//#define MY_DISABLE_REMOTE_RESET //#define MY_DISABLE_REMOTE_RESET
@@ -79,54 +88,98 @@
//#define MY_RADIO_NRF24 //#define MY_RADIO_NRF24
//#define MY_RADIO_RFM69 //#define MY_RADIO_RFM69
//#define MY_RADIO_RFM95
//#define MY_RS485 //#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 * @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 //#define MY_TRANSPORT_SANITY_CHECK
/** /**
* @def MY_TRANSPORT_SANITY_CHECK_INTERVAL * @def MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS
* @brief Interval (in ms) of transport sanity checks * @brief Interval (in ms) for transport sanity checks
*/ */
#ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL #ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS
#define MY_TRANSPORT_SANITY_CHECK_INTERVAL ((uint32_t)60000) #define MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS (15*60*1000ul)
#endif #endif
/**
* @def MY_TRANSPORT_DISCOVERY_INTERVAL_MS
* @brief This is a gateway-only feature: Interval (in ms) to issue network discovery checks
*/
#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 * @def MY_REGISTRATION_FEATURE
* @brief If enabled, node has to register to gateway/controller before allowed to send sensor data. * @brief If enabled, node has to register to gateway/controller before allowed to send sensor data.
*/ */
#define MY_REGISTRATION_FEATURE #define MY_REGISTRATION_FEATURE
/** /**
* @def MY_REGISTRATION_RETRIES * @def MY_REGISTRATION_RETRIES
* @brief Number of registration retries if no reply received from GW/controller * @brief Number of registration retries if no reply received from GW/controller
*/ */
#ifndef MY_REGISTRATION_RETRIES #ifndef MY_REGISTRATION_RETRIES
#define MY_REGISTRATION_RETRIES 3 #define MY_REGISTRATION_RETRIES (3u)
#endif #endif
/** /**
* @def MY_REGISTRATION_DEFAULT * @def MY_REGISTRATION_DEFAULT
* @brief Node registration default - this applies if no registration response is recieved from controller * @brief Node registration default - this applies if no registration response is received from controller
*/ */
#define MY_REGISTRATION_DEFAULT true #define MY_REGISTRATION_DEFAULT true
/** /**
* @def MY_REGISTRATION_CONTROLLER * @def MY_REGISTRATION_CONTROLLER
* @brief If enabled, node registration request has to be handled by controller * @brief If enabled, node registration request has to be handled by controller
*/ */
// #define MY_REGISTRATION_CONTROLLER // #define MY_REGISTRATION_CONTROLLER
/** /**
* @def MY_CORE_COMPATIBILITY_CHECK * @def MY_CORE_COMPATIBILITY_CHECK
* @brief If enabled, library compatibility is checked during node registration. Incompatible libraries are unable to send sensor data. * @brief If enabled, library compatibility is checked during node registration. Incompatible libraries are unable to send sensor data.
*/ */
#define MY_CORE_COMPATIBILITY_CHECK #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 * @def MY_NODE_ID
* @brief Node id defaults to AUTO (tries to fetch id from controller). * @brief Node id defaults to AUTO (tries to fetch id from controller).
@@ -149,18 +202,29 @@
*/ */
//#define MY_PARENT_NODE_IS_STATIC //#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_SMART_SLEEP_WAIT_DURATION * @def MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS
* @brief The wait period before going to sleep when using smartSleep-functions. * @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_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 * This period has to be long enough for controller to be able to send out
* potential buffered messages. * potential buffered messages.
*/ */
#ifndef MY_SMART_SLEEP_WAIT_DURATION #ifndef MY_SMART_SLEEP_WAIT_DURATION_MS
#define MY_SMART_SLEEP_WAIT_DURATION 500 #define MY_SMART_SLEEP_WAIT_DURATION_MS (500ul)
#endif #endif
/********************************** /**********************************
@@ -199,7 +263,7 @@
* @brief Max buffersize needed for messages coming from controller. * @brief Max buffersize needed for messages coming from controller.
*/ */
#ifndef MY_GATEWAY_MAX_RECEIVE_LENGTH #ifndef MY_GATEWAY_MAX_RECEIVE_LENGTH
#define MY_GATEWAY_MAX_RECEIVE_LENGTH 100 #define MY_GATEWAY_MAX_RECEIVE_LENGTH (100u)
#endif #endif
/** /**
@@ -207,7 +271,7 @@
* @brief Max buffer size when sending messages. * @brief Max buffer size when sending messages.
*/ */
#ifndef MY_GATEWAY_MAX_SEND_LENGTH #ifndef MY_GATEWAY_MAX_SEND_LENGTH
#define MY_GATEWAY_MAX_SEND_LENGTH 120 #define MY_GATEWAY_MAX_SEND_LENGTH (120u)
#endif #endif
/** /**
@@ -215,7 +279,7 @@
* @brief Max number of parallel clients (sever mode). * @brief Max number of parallel clients (sever mode).
*/ */
#ifndef MY_GATEWAY_MAX_CLIENTS #ifndef MY_GATEWAY_MAX_CLIENTS
#define MY_GATEWAY_MAX_CLIENTS 1 #define MY_GATEWAY_MAX_CLIENTS (1u)
#endif #endif
@@ -223,25 +287,22 @@
/********************************** /**********************************
* Information LEDs blinking * Information LEDs blinking
***********************************/ ***********************************/
// This feature enables LEDs blinking on message receive, transmit // If one of the following is defined here, or in the sketch, the pin will be used for the
// or if some error occurred. This was commonly used only in gateways, // corresponding led function.
// but now can be used in any sensor node. Also the LEDs can now be // They have to be enabled here (or in your sketch). Replace x with the pin number you have the LED on.
// disabled in the gateway. //
// 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
//#define MY_LEDS_BLINKING_FEATURE // The following setting allows you to inverse the LED blinking
// The following setting allows you to inverse the blinking feature MY_LEDS_BLINKING_FEATURE
// When MY_WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches // When MY_WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches
// off when blinking // off when blinking
//#define MY_WITH_LEDS_BLINKING_INVERSE //#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 * Gateway inclusion button/mode configuration
@@ -262,11 +323,11 @@
* @brief The default input pin used for the inclusion mode button. * @brief The default input pin used for the inclusion mode button.
*/ */
#ifndef MY_INCLUSION_MODE_BUTTON_PIN #ifndef MY_INCLUSION_MODE_BUTTON_PIN
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
#define MY_INCLUSION_MODE_BUTTON_PIN 5 #define MY_INCLUSION_MODE_BUTTON_PIN 5
#else #else
#define MY_INCLUSION_MODE_BUTTON_PIN 3 #define MY_INCLUSION_MODE_BUTTON_PIN 3
#endif #endif
#endif #endif
/** /**
@@ -316,6 +377,15 @@
*/ */
//#define MY_SIGNING_REQUEST_SIGNATURES //#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 * @def MY_VERIFICATION_TIMEOUT_MS
* @brief Define a suitable timeout for a signature verification session * @brief Define a suitable timeout for a signature verification session
@@ -382,6 +452,18 @@
#define MY_RS485_MAX_MESSAGE_LENGTH 40 #define MY_RS485_MAX_MESSAGE_LENGTH 40
#endif #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 * NRF24L01P Driver Defaults
***********************************/ ***********************************/
@@ -406,13 +488,15 @@
* @brief Default RF24 chip enable pin setting. Override in sketch if needed. * @brief Default RF24 chip enable pin setting. Override in sketch if needed.
*/ */
#ifndef MY_RF24_CE_PIN #ifndef MY_RF24_CE_PIN
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
#define MY_RF24_CE_PIN 4 #define MY_RF24_CE_PIN 4
#elif defined(ARDUINO_ARCH_SAMD) #elif defined(ARDUINO_ARCH_SAMD)
#define MY_RF24_CE_PIN 27 #define MY_RF24_CE_PIN 27
#else #elif defined(LINUX_ARCH_RASPBERRYPI)
#define MY_RF24_CE_PIN 9 #define MY_RF24_CE_PIN 22
#endif #else
#define MY_RF24_CE_PIN 9
#endif
#endif #endif
/** /**
@@ -420,13 +504,33 @@
* @brief Default RF24 chip select pin setting. Override in sketch if needed. * @brief Default RF24 chip select pin setting. Override in sketch if needed.
*/ */
#ifndef MY_RF24_CS_PIN #ifndef MY_RF24_CS_PIN
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
#define MY_RF24_CS_PIN 15 #define MY_RF24_CS_PIN 15
#elif defined(ARDUINO_ARCH_SAMD) #elif defined(ARDUINO_ARCH_SAMD)
#define MY_RF24_CS_PIN 3 #define MY_RF24_CS_PIN 3
#else #elif defined(LINUX_ARCH_RASPBERRYPI)
#define MY_RF24_CS_PIN 10 #define MY_RF24_CS_PIN 24
#endif #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 #endif
/** /**
@@ -483,14 +587,6 @@
#define MY_RF24_ADDR_WIDTH 5 #define MY_RF24_ADDR_WIDTH 5
#endif #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 // Enable SOFTSPI for NRF24L01, useful for the W5100 Ethernet module
//#define MY_SOFTSPI //#define MY_SOFTSPI
@@ -543,9 +639,9 @@
* @brief Set to true if @ref MY_IS_RFM69HW is set. * @brief Set to true if @ref MY_IS_RFM69HW is set.
*/ */
#ifdef MY_IS_RFM69HW #ifdef MY_IS_RFM69HW
#define MY_RFM69HW true #define MY_RFM69HW true
#else #else
#define MY_RFM69HW false #define MY_RFM69HW false
#endif #endif
/** /**
@@ -577,16 +673,97 @@
* @brief RF69 IRQ pin number. * @brief RF69 IRQ pin number.
*/ */
#ifndef MY_RF69_IRQ_NUM #ifndef MY_RF69_IRQ_NUM
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
#define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN #define MY_RF69_IRQ_NUM RF69_IRQ_PIN
#else #else
#define MY_RF69_IRQ_NUM RF69_IRQ_NUM #define MY_RF69_IRQ_NUM RF69_IRQ_NUM
#endif #endif
#endif #endif
// Enables RFM69 encryption (all nodes and gateway must have this enabled, and all must be personalized with the same AES key) // 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 //#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 * Ethernet Gateway Transport Defaults
***************************************/ ***************************************/
@@ -595,14 +772,19 @@
//#define MY_GATEWAY_W5100 //#define MY_GATEWAY_W5100
//#define MY_GATEWAY_ENC28J60 //#define MY_GATEWAY_ENC28J60
//#define MY_GATEWAY_ESP8266 //#define MY_GATEWAY_ESP8266
//#define MY_GATEWAY_LINUX
/** /**
* @def MY_PORT * @def MY_PORT
* @brief The Ethernet TCP/UDP port to open on controller or gateway. * @brief The Ethernet TCP/UDP port to open on controller or gateway.
*/ */
#ifndef MY_PORT #ifndef MY_PORT
#ifdef MY_GATEWAY_MQTT_CLIENT
#define MY_PORT 1883
#else
#define MY_PORT 5003 #define MY_PORT 5003
#endif #endif
#endif
// Static ip address of gateway (if this is disabled, DHCP will be used) // Static ip address of gateway (if this is disabled, DHCP will be used)
//#define MY_IP_ADDRESS 192,168,178,66 //#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. // 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 //#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254
/**************************************
* Node Locking
***************************************/
/** /**
* @defgroup MyLockgrp MyNodeLock * @defgroup MyLockgrp MyNodeLock
* @ingroup internals * @ingroup internals
@@ -688,18 +874,89 @@
#endif #endif
/** @}*/ // Node lock group /** @}*/ // 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 #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 // Doxygen specific constructs, not included when built normally
// This is used to enable disabled macros/definitions to be included in the documentation as well. // This is used to enable disabled macros/definitions to be included in the documentation as well.
#if DOXYGEN #if DOXYGEN
#define MY_SIGNING_ATSHA204 #define MY_SIGNING_ATSHA204
#define MY_SIGNING_SOFT #define MY_SIGNING_SOFT
#define MY_SIGNING_REQUEST_SIGNATURES #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_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_IS_RFM69HW
#define MY_PARENT_NODE_IS_STATIC #define MY_PARENT_NODE_IS_STATIC
#define MY_REGISTRATION_CONTROLLER #define MY_REGISTRATION_CONTROLLER
#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
#define MY_DEBUG_VERBOSE_RF24 #define MY_DEBUG_VERBOSE_RF24
#define MY_TRANSPORT_SANITY_CHECK #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 #endif
+221 -173
View File
@@ -40,61 +40,58 @@
* @def MY_NODE_TYPE * @def MY_NODE_TYPE
* @brief Contain a string describing the class of sketch/node (gateway/repeater/sensor). * @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) #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_GATEWAY_FEATURE
#define MY_IS_GATEWAY (true) #define MY_IS_GATEWAY (true)
#define MY_NODE_TYPE "gateway" #define MY_NODE_TYPE "GW"
#elif defined(MY_REPEATER_FEATURE) #elif defined(MY_REPEATER_FEATURE)
#define MY_IS_GATEWAY (false) #define MY_IS_GATEWAY (false)
#define MY_NODE_TYPE "repeater" #define MY_NODE_TYPE "REPEATER"
#else #else
#define MY_IS_GATEWAY (false) #define MY_IS_GATEWAY (false)
#define MY_NODE_TYPE "sensor" #define MY_NODE_TYPE "NODE"
#endif #endif
// Enable radio "feature" if one of the radio types was enabled // Enable radio "feature" if one of the radio types was enabled
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) || defined(MY_RS485)
#define MY_RADIO_FEATURE #define MY_SENSOR_NETWORK
#endif #endif
// HARDWARE // HARDWARE
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
// Remove PSTR macros from debug prints #include "core/MyHwESP8266.cpp"
#undef PSTR
#define PSTR(x) (x)
//#undef F
//#define F(x) (x)
#include "core/MyHwESP8266.cpp"
#elif defined(ARDUINO_ARCH_AVR) #elif defined(ARDUINO_ARCH_AVR)
#include "core/MyHwATMega328.cpp" #include "drivers/AVR/DigitalWriteFast/digitalWriteFast.h"
#include "core/MyHwATMega328.cpp"
#elif defined(ARDUINO_ARCH_SAMD) #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 #endif
// LEDS // LEDS
#if !defined(MY_DEFAULT_ERR_LED_PIN) & defined(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 #define MY_DEFAULT_ERR_LED_PIN MY_HW_ERR_LED_PIN
#endif #endif
#if !defined(MY_DEFAULT_TX_LED_PIN) && defined(MY_HW_TX_LED_PIN) #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 #endif
#if !defined(MY_DEFAULT_RX_LED_PIN) && defined(MY_HW_TX_LED_PIN) #if !defined(MY_DEFAULT_RX_LED_PIN) && defined(MY_HW_TX_LED_PIN)
#define MY_DEFAULT_RX_LED_PIN MY_HW_TX_LED_PIN #define MY_DEFAULT_RX_LED_PIN MY_HW_TX_LED_PIN
#endif #endif
// Not necessary to include blinking feature if no LED's are defined! #if defined(MY_LEDS_BLINKING_FEATURE)
#if defined(MY_LEDS_BLINKING_FEATURE) && !defined(MY_DEFAULT_RX_LED_PIN) && !defined(MY_DEFAULT_TX_LED_PIN) && !defined(MY_ERR_LED_PIN) #error MY_LEDS_BLINKING_FEATURE is now removed from MySensors core,\
#undef MY_LEDS_BLINKING_FEATURE 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 #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
#endif
/** /**
* @def MY_DEFAULT_LED_BLINK_PERIOD * @def MY_DEFAULT_LED_BLINK_PERIOD
* @brief Default LEDs blinking period in milliseconds. * @brief Default LEDs blinking period in milliseconds.
@@ -102,44 +99,11 @@
#ifndef MY_DEFAULT_LED_BLINK_PERIOD #ifndef MY_DEFAULT_LED_BLINK_PERIOD
#define MY_DEFAULT_LED_BLINK_PERIOD 300 #define MY_DEFAULT_LED_BLINK_PERIOD 300
#endif #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) #if defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
#include "core/MyLeds.cpp" #include "core/MyLeds.cpp"
#else #else
#include "core/MyLeds.h" #include "core/MyLeds.h"
#endif #endif
#include "core/MyIndication.cpp" #include "core/MyIndication.cpp"
@@ -147,145 +111,226 @@
// INCLUSION MODE // INCLUSION MODE
#if defined(MY_INCLUSION_MODE_FEATURE) #if defined(MY_INCLUSION_MODE_FEATURE)
#include "core/MyInclusionMode.cpp" #include "core/MyInclusionMode.cpp"
#endif #endif
// SIGNING // SIGNING
#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT) #if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT)
#define MY_SIGNING_FEATURE #define MY_SIGNING_FEATURE
#endif #endif
#include "core/MySigning.cpp" #include "core/MySigning.cpp"
#include "drivers/ATSHA204/sha256.cpp" #include "drivers/ATSHA204/sha256.cpp"
#if defined(MY_SIGNING_FEATURE) #if defined(MY_SIGNING_FEATURE)
// SIGNING COMMON FUNCTIONS // SIGNING COMMON FUNCTIONS
#if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT) #if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT)
#error Only one signing engine can be activated #error Only one signing engine can be activated
#endif #endif
#if defined(MY_SIGNING_ATSHA204) && defined(__linux__)
#error No support for ATSHA204 on this platform
#endif
#if defined(MY_SIGNING_ATSHA204) #if defined(MY_SIGNING_ATSHA204)
#include "core/MySigningAtsha204.cpp" #include "core/MySigningAtsha204.cpp"
#include "drivers/ATSHA204/ATSHA204.cpp" #include "drivers/ATSHA204/ATSHA204.cpp"
#elif defined(MY_SIGNING_SOFT) #elif defined(MY_SIGNING_SOFT)
#include "core/MySigningAtsha204Soft.cpp" #include "core/MySigningAtsha204Soft.cpp"
#endif #endif
#endif #endif
// FLASH // FLASH
#if defined(MY_OTA_FIRMWARE_FEATURE) #if defined(MY_OTA_FIRMWARE_FEATURE)
#include "drivers/SPIFlash/SPIFlash.cpp" #include "drivers/SPIFlash/SPIFlash.cpp"
#include "core/MyOTAFirmwareUpdate.cpp" #include "core/MyOTAFirmwareUpdate.cpp"
#endif #endif
// GATEWAY - TRANSPORT // 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_GATEWAY_MQTT_CLIENT)
#if defined(MY_RADIO_FEATURE) #if defined(MY_SENSOR_NETWORK)
// We assume that a gateway having a radio also should act as repeater // We assume that a gateway having a radio also should act as repeater
#define MY_REPEATER_FEATURE #define MY_REPEATER_FEATURE
#endif #endif
// GATEWAY - COMMON FUNCTIONS // GATEWAY - COMMON FUNCTIONS
// We only support MQTT Client using W5100 and ESP8266 at the moment // We support MQTT Client using W5100, ESP8266 and Linux
#if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) #if !defined(MY_GATEWAY_CLIENT_MODE)
#error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS
#endif #endif
#if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX) #if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX)
#error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client #error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client
#endif #endif
#if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX)
#error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client
#endif #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" #if !defined(MY_MQTT_CLIENT_ID)
#include "core/MyGatewayTransport.cpp" #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client
#include "core/MyGatewayTransportMQTTClient.cpp" #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) #elif defined(MY_GATEWAY_FEATURE)
// GATEWAY - COMMON FUNCTIONS // GATEWAY - COMMON FUNCTIONS
#include "core/MyGatewayTransport.cpp" #include "core/MyGatewayTransport.cpp"
// We currently only support one protocol at the moment, enable it. #include "core/MyProtocolMySensors.cpp"
#include "core/MyProtocolMySensors.cpp"
// GATEWAY - CONFIGURATION // GATEWAY - CONFIGURATION
#if defined(MY_RADIO_FEATURE) #if defined(MY_SENSOR_NETWORK)
// We assume that a gateway having a radio also should act as repeater // We assume that a gateway having a radio also should act as repeater
#define MY_REPEATER_FEATURE #define MY_REPEATER_FEATURE
#endif #endif
#if defined(MY_CONTROLLER_IP_ADDRESS) #if !defined(MY_PORT)
#define MY_GATEWAY_CLIENT_MODE #error You must define MY_PORT (controller or gatway port to open)
#endif #endif
#if !defined(MY_PORT) #if defined(MY_GATEWAY_ESP8266)
#error You must define MY_PORT (controller or gatway port to open) // GATEWAY - ESP8266
#endif #include "core/MyGatewayTransportEthernet.cpp"
#if defined(MY_GATEWAY_ESP8266) #elif defined(MY_GATEWAY_LINUX)
// GATEWAY - ESP8266 // GATEWAY - Generic Linux
#include "core/MyGatewayTransportEthernet.cpp" #include "drivers/Linux/EthernetClient.h"
#elif defined(MY_GATEWAY_W5100) #include "drivers/Linux/EthernetServer.h"
// GATEWAY - W5100 #include "drivers/Linux/IPAddress.h"
#include "core/MyGatewayTransportEthernet.cpp" #include "core/MyGatewayTransportEthernet.cpp"
#elif defined(MY_GATEWAY_ENC28J60) #elif defined(MY_GATEWAY_W5100)
// GATEWAY - ENC28J60 // GATEWAY - W5100
#if defined(MY_USE_UDP) #include "core/MyGatewayTransportEthernet.cpp"
#error UDP mode is not available for ENC28J60 #elif defined(MY_GATEWAY_ENC28J60)
#endif // GATEWAY - ENC28J60
#include "core/MyGatewayTransportEthernet.cpp" #if defined(MY_USE_UDP)
#elif defined(MY_GATEWAY_SERIAL) #error UDP mode is not available for ENC28J60
// GATEWAY - SERIAL #endif
#include "core/MyGatewayTransportSerial.cpp" #include "core/MyGatewayTransportEthernet.cpp"
#endif #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 #endif
// RADIO // RADIO
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) ||defined(MY_RS485)
// SOFTSPI // SOFTSPI
#ifdef MY_SOFTSPI #ifdef MY_SOFTSPI
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
#error Soft SPI is not available on ESP8266 #error Soft SPI is not available on ESP8266
#endif #endif
#include "drivers/AVR/DigitalIO/DigitalIO.h" #include "drivers/AVR/DigitalIO/DigitalIO.h"
#endif #endif
#include "core/MyTransport.cpp" #if defined(MY_RADIO_NRF24) && defined(__linux__) && !defined(LINUX_ARCH_RASPBERRYPI)
#if (defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485)) #error No support for nRF24 radio on this platform
#error Only one forward link driver can be activated #endif
#endif
#if defined(MY_RADIO_NRF24) #include "core/MyTransport.cpp"
#if defined(MY_RF24_ENABLE_ENCRYPTION)
#include "drivers/AES/AES.cpp" // count enabled transports
#endif #if defined(MY_RADIO_NRF24)
#include "drivers/RF24/RF24.cpp" #define __RF24CNT 1
#include "core/MyTransportNRF24.cpp" #else
#elif defined(MY_RS485) #define __RF24CNT 0
#include "drivers/AltSoftSerial/AltSoftSerial.cpp" #endif
#include "core/MyTransportRS485.cpp" #if defined(MY_RADIO_RFM69)
#elif defined(MY_RADIO_RFM69) #define __RFM69CNT 1
#include "drivers/RFM69/RFM69.cpp" #else
#include "core/MyTransportRFM69.cpp" #define __RFM69CNT 0
#endif #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 #endif
// Make sure to disable child features when parent feature is disabled // Make sure to disable child features when parent feature is disabled
#if !defined(MY_RADIO_FEATURE) #if !defined(MY_SENSOR_NETWORK)
#undef MY_OTA_FIRMWARE_FEATURE #undef MY_OTA_FIRMWARE_FEATURE
#undef MY_REPEATER_FEATURE #undef MY_REPEATER_FEATURE
#undef MY_SIGNING_NODE_WHITELISTING #undef MY_SIGNING_NODE_WHITELISTING
#undef MY_SIGNING_FEATURE #undef MY_SIGNING_FEATURE
#endif #endif
#if !defined(MY_GATEWAY_FEATURE) #if !defined(MY_GATEWAY_FEATURE)
#undef MY_INCLUSION_MODE_FEATURE #undef MY_INCLUSION_MODE_FEATURE
#undef MY_INCLUSION_BUTTON_FEATURE #undef MY_INCLUSION_BUTTON_FEATURE
#endif #endif
#if !defined(MY_CORE_ONLY) #if !defined(MY_CORE_ONLY)
#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE) #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. #error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless.
#endif #endif
#endif #endif
#include "core/MyCapabilities.h" #include "core/MyCapabilities.h"
@@ -295,11 +340,13 @@
#include <Arduino.h> #include <Arduino.h>
#if !defined(MY_CORE_ONLY) #if !defined(MY_CORE_ONLY)
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
#include "core/MyMainESP8266.cpp" #include "core/MyMainESP8266.cpp"
#else #elif defined(__linux__)
#include "core/MyMainDefault.cpp" #include "core/MyMainLinux.cpp"
#endif #else
#include "core/MyMainDefault.cpp"
#endif
#endif #endif
#endif #endif
@@ -307,4 +354,5 @@
// This is used to enable disabled macros/definitions to be included in the documentation as well. // This is used to enable disabled macros/definitions to be included in the documentation as well.
#if DOXYGEN #if DOXYGEN
#define MY_GATEWAY_FEATURE #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 #endif
+5 -12
View File
@@ -1,24 +1,17 @@
Arduino MySensors Library v2.1.0
=======
MySensors Arduino Library v2.0.0
Please visit www.mysensors.org for more information Please visit www.mysensors.org for more information
Doxygen 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 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 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: [![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 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): [![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 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 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 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/)
Vendored Executable
+497
View File
@@ -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 <<EOF
configure script for MySensors gateway.
Options:
Help:
-h, --help print this message
Building options:
--soc=[BCM2835|BCM2836|AM33XX|A10|A13|A20|H3]
SoC type to be used. [configure autodetected]
--cpu-flags=<CPUFLAGS> CPU defining/optimizing flags to be used. [configure autodetected]
--extra-cflags=<CFLAGS> Extra C flags passed to C compilation. []
--extra-cxxflags=<CXXFLAGS> Extra C++ flags passed to C++ compilation. []
--extra-ldflags=<LDFLAGS> Extra C flags passed to linking. []
--c_compiler=<CC> C compiler. [arm-linux-gnueabihf-gcc][gcc]
--cxx_compiler=<CXX> C++ compiler [arm-linux-gnueabihf-g++][g++]
--build-dir=<DIR> Compiler directory to store object files. [build]
--bin-dir=<DIR> Compiler directory to store binary files. [bin]
--arduino-lib-dir=<DIR> Arduino library directory.
--no-clean Don't clean previous build artifacts.
Installation options:
--prefix=<PREFIX> Installation prefix path. [/usr/local]
--gateway-dir=<DIR> Gateway files installation directory. [PREFIX/bin]
MySensors options:
--my-debug=[enable|disable] Enables or disables MySensors core debugging. [enable]
--my-config-file=<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=<ID> Disable gateway feature and run as a node with given id.
--my-controller-url-address=<URL>
Controller or MQTT broker url.
--my-controller-ip-address=<IP>
Controller or MQTT broker ip.
--my-port=<PORT> The port to keep open on gateway mode.
If gateway is set to mqtt, it sets the broker port.
--my-serial-port=<PORT> Serial port. [/dev/ttyACM0]
--my-serial-baudrate=<BAUD> 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=<NAME> Symlink name for the PTY device. [/dev/ttyMySensorsGateway]
--my-serial-groupname=<GROUP>
Grant access to the specified system group for the serial device.
--my-mqtt-client-id=<ID> MQTT client id.
--my-mqtt-publish-topic-prefix=<PREFIX>
MQTT publish topic prefix.
--my-mqtt-subscribe-topic-prefix=<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> 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=<SIZE>
Buffer size for incoming messages when using rf24 interrupts. [20]
--my-rs485-serial-port=<PORT>
RS485 serial port. You must provide a port.
--my-rs485-baudrate=<BAUD> RS485 baudrate. [9600]
--my-rs485-de-pin=<PIN> Pin number connected to RS485 driver enable pin.
--my-rs485-max-msg-length=<LENGTH>
The maximum message length used for RS485.
--my-leds-err-pin=<PIN> Error LED pin.
--my-leds-rx-pin=<PIN> Receive LED pin.
--my-leds-tx-pin=<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."
+28 -20
View File
@@ -22,55 +22,63 @@
#define MyCapabilities_h #define MyCapabilities_h
#if defined(MY_DISABLE_REMOTE_RESET) #if defined(MY_DISABLE_REMOTE_RESET)
#define MY_CAP_RESET "N" #define MY_CAP_RESET "N"
#else #else
#define MY_CAP_RESET "R" #define MY_CAP_RESET "R"
#endif #endif
#if defined(MY_OTA_FIRMWARE_FEATURE) #if defined(MY_OTA_FIRMWARE_FEATURE)
#define MY_CAP_OTA_FW "O" #define MY_CAP_OTA_FW "O"
#else #else
#define MY_CAP_OTA_FW "N" #define MY_CAP_OTA_FW "N"
#endif #endif
#if defined(MY_RADIO_NRF24) #if defined(MY_RADIO_NRF24)
#define MY_CAP_RADIO "N" #define MY_CAP_RADIO "N"
#elif defined(MY_RADIO_RFM69) #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) #elif defined(MY_RS485)
#define MY_CAP_RADIO "S" #define MY_CAP_RADIO "S"
#else #else
#define MY_CAP_RADIO "-" #define MY_CAP_RADIO "-"
#endif #endif
#if defined(MY_GATEWAY_FEATURE) #if defined(MY_GATEWAY_FEATURE)
#define MY_CAP_TYPE "G" #define MY_CAP_TYPE "G"
#elif defined(MY_REPEATER_FEATURE) #elif defined(MY_REPEATER_FEATURE)
#define MY_CAP_TYPE "R" #define MY_CAP_TYPE "R"
#else #else
#define MY_CAP_TYPE "N" #define MY_CAP_TYPE "N"
#endif #endif
#if defined(ARDUINO_ARCH_SAMD) #if defined(ARDUINO_ARCH_SAMD)
#define MY_CAP_ARCH "S" #define MY_CAP_ARCH "S"
#elif defined(ARDUINO_ARCH_ESP8266) #elif defined(ARDUINO_ARCH_ESP8266)
#define MY_CAP_ARCH "E" #define MY_CAP_ARCH "E"
#elif defined(ARDUINO_ARCH_AVR) #elif defined(ARDUINO_ARCH_AVR)
#define MY_CAP_ARCH "A" #define MY_CAP_ARCH "A"
#else #else
#define MY_CAP_ARCH "-" #define MY_CAP_ARCH "-"
#endif #endif
#if defined(MY_SIGNING_ATSHA204) #if defined(MY_SIGNING_ATSHA204)
#define MY_CAP_SIGN "A" #define MY_CAP_SIGN "A"
#elif defined(MY_SIGNING_SOFT) #elif defined(MY_SIGNING_SOFT)
#define MY_CAP_SIGN "S" #define MY_CAP_SIGN "S"
#else #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 #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 */
+68 -23
View File
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes. * network topology allowing messages to be routed to nodes.
* *
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org> * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
* *
* Documentation: http://www.mysensors.org * Documentation: http://www.mysensors.org
@@ -17,28 +17,73 @@
* version 2 as published by the Free Software Foundation. * 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 #ifndef MyEepromAddresses_h
#define MyEepromAddresses_h #define MyEepromAddresses_h
// EEPROM start address for mysensors library data
#define EEPROM_START 0
// EEPROM location of 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 // 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
/** @brief Address node ID */
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
/** @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
/** @}*/
+15 -10
View File
@@ -20,9 +20,13 @@
#include "MyGatewayTransport.h" #include "MyGatewayTransport.h"
extern bool transportSendRoute(MyMessage &message); extern bool transportSendRoute(MyMessage &message);
extern MyMessage _msg;
inline void gatewayTransportProcess() { // global variables
extern MyMessage _msg;
extern MyMessage _msgTmp;
inline void gatewayTransportProcess(void)
{
if (gatewayTransportAvailable()) { if (gatewayTransportAvailable()) {
_msg = gatewayTransportReceive(); _msg = gatewayTransportReceive();
if (_msg.destination == GATEWAY_ADDRESS) { if (_msg.destination == GATEWAY_ADDRESS) {
@@ -31,21 +35,22 @@ inline void gatewayTransportProcess() {
if (mGetRequestAck(_msg)) { if (mGetRequestAck(_msg)) {
// Copy message // Copy message
_msgTmp = _msg; _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); mSetAck(_msgTmp, true);
_msgTmp.sender = _nc.nodeId; _msgTmp.sender = getNodeId();
_msgTmp.destination = _msg.sender; _msgTmp.destination = _msg.sender;
gatewayTransportSend(_msgTmp); gatewayTransportSend(_msgTmp);
} }
if (mGetCommand(_msg) == C_INTERNAL) { if (mGetCommand(_msg) == C_INTERNAL) {
if (_msg.type == I_VERSION) { if (_msg.type == I_VERSION) {
// Request for version. Create the response // Request for version. Create the response
gatewayTransportSend(buildGw(_msg, I_VERSION).set(MYSENSORS_LIBRARY_VERSION)); gatewayTransportSend(buildGw(_msgTmp, I_VERSION).set(MYSENSORS_LIBRARY_VERSION));
#ifdef MY_INCLUSION_MODE_FEATURE #ifdef MY_INCLUSION_MODE_FEATURE
} else if (_msg.type == I_INCLUSION_MODE) { } else if (_msg.type == I_INCLUSION_MODE) {
// Request to change inclusion mode // Request to change inclusion mode
inclusionModeSet(atoi(_msg.data) == 1); inclusionModeSet(atoi(_msg.data) == 1);
#endif #endif
} else { } else {
_processInternalMessages(); _processInternalMessages();
} }
@@ -56,9 +61,9 @@ inline void gatewayTransportProcess() {
} }
} }
} else { } else {
#if defined(MY_RADIO_FEATURE) #if defined(MY_SENSOR_NETWORK)
transportSendRoute(_msg); transportSendRoute(_msg);
#endif #endif
} }
} }
} }
+8 -5
View File
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes. * network topology allowing messages to be routed to nodes.
* *
* Created by Tomas Hozza <thozza@gmail.com> * Created by Tomas Hozza <thozza@gmail.com>
* Copyright (C) 2015 Tomas Hozza * Copyright (C) 2015 Tomas Hozza
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
* *
* Documentation: http://www.mysensors.org * Documentation: http://www.mysensors.org
@@ -21,10 +21,13 @@
#define MyGatewayTransport_h #define MyGatewayTransport_h
#include "MyProtocol.h" #include "MyProtocol.h"
#include "MySensorsCore.h"
#define MSG_GW_STARTUP_COMPLETE "Gateway startup complete."
// Common gateway functions // Common gateway functions
void gatewayTransportProcess(); void gatewayTransportProcess(void);
// Gateway "interface" functions // Gateway "interface" functions
@@ -32,7 +35,7 @@ void gatewayTransportProcess();
/** /**
* initialize the driver * initialize the driver
*/ */
bool gatewayTransportInit(); bool gatewayTransportInit(void);
/** /**
* Send message to controller * Send message to controller
@@ -42,11 +45,11 @@ bool gatewayTransportSend(MyMessage &message);
/* /*
* Check if a new message is available from controller * Check if a new message is available from controller
*/ */
bool gatewayTransportAvailable(); bool gatewayTransportAvailable(void);
/* /*
* Pick up last message received from controller * Pick up last message received from controller
*/ */
MyMessage& gatewayTransportReceive(); MyMessage& gatewayTransportReceive(void);
#endif /* MyGatewayTransportEthernet_h */ #endif /* MyGatewayTransportEthernet_h */
+329 -275
View File
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes. * network topology allowing messages to be routed to nodes.
* *
* Created by Tomas Hozza <thozza@gmail.com> * Created by Tomas Hozza <thozza@gmail.com>
* Copyright (C) 2015 Tomas Hozza * Copyright (C) 2015 Tomas Hozza
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
* *
* Documentation: http://www.mysensors.org * Documentation: http://www.mysensors.org
@@ -19,353 +19,406 @@
#include "MyGatewayTransport.h" #include "MyGatewayTransport.h"
// global variables
extern MyMessage _msgTmp;
#if defined(MY_CONTROLLER_IP_ADDRESS) #if defined(MY_CONTROLLER_IP_ADDRESS)
IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS); IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS);
#endif #endif
#if defined(MY_IP_ADDRESS) #if defined(MY_IP_ADDRESS)
IPAddress _ethernetGatewayIP(MY_IP_ADDRESS); IPAddress _ethernetGatewayIP(MY_IP_ADDRESS);
#endif #endif
byte _ethernetGatewayMAC[] = { MY_MAC_ADDRESS }; uint8_t _ethernetGatewayMAC[] = { MY_MAC_ADDRESS };
uint16_t _ethernetGatewayPort = MY_PORT; uint16_t _ethernetGatewayPort = MY_PORT;
MyMessage _ethernetMsg; MyMessage _ethernetMsg;
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #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];
typedef struct uint8_t idx;
{
char string[MY_GATEWAY_MAX_RECEIVE_LENGTH];
uint8_t idx;
} inputBuffer; } inputBuffer;
#if defined(MY_GATEWAY_ESP8266) #if defined(MY_GATEWAY_ESP8266)
// Some re-defines to make code more readable below // Some re-defines to make code more readable below
#define EthernetServer WiFiServer #define EthernetServer WiFiServer
#define EthernetClient WiFiClient #define EthernetClient WiFiClient
#define EthernetUDP WiFiUDP #define EthernetUDP WiFiUDP
#if defined(MY_IP_ADDRESS) #if defined(MY_IP_ADDRESS)
IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
#endif #endif
static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS];
#endif #endif
#if defined(MY_USE_UDP) #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 #else
EthernetServer _ethernetServer(_ethernetGatewayPort); EthernetServer _ethernetServer(_ethernetGatewayPort);
#endif #endif
#if defined(MY_GATEWAY_CLIENT_MODE)
#if defined(MY_GATEWAY_ESP8266) static EthernetClient client = EthernetClient();
static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS]; static inputBuffer inputString;
static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS]; #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 #else
static EthernetClient client = EthernetClient(); static EthernetClient client = EthernetClient();
static inputBuffer inputString; static inputBuffer inputString;
#endif #endif
#ifndef MY_IP_ADDRESS #ifndef MY_IP_ADDRESS
void gatewayTransportRenewIP(); void gatewayTransportRenewIP();
#endif #endif
// On W5100 boards with SPI_EN exposed we can use the real SPI bus together with radio // On W5100 boards with SPI_EN exposed we can use the real SPI bus together with radio
// (if we enable it during usage) // (if we enable it during usage)
#ifdef MY_W5100_SPI_EN #ifdef MY_W5100_SPI_EN
void _w5100_spi_en(bool enable) void _w5100_spi_en(bool enable)
{ {
if (enable) if (enable) {
{ // Pull up pin
// Pull up pin hwPinMode(MY_W5100_SPI_EN, INPUT);
pinMode(MY_W5100_SPI_EN, INPUT); hwDigitalWrite(MY_W5100_SPI_EN, HIGH);
digitalWrite(MY_W5100_SPI_EN, HIGH); } else {
} // Ground pin
else hwPinMode(MY_W5100_SPI_EN, OUTPUT);
{ hwDigitalWrite(MY_W5100_SPI_EN, LOW);
// Ground pin
pinMode(MY_W5100_SPI_EN, OUTPUT);
digitalWrite(MY_W5100_SPI_EN, LOW);
}
} }
}
#else #else
#define _w5100_spi_en(x) #define _w5100_spi_en(x)
#endif #endif
bool gatewayTransportInit(void)
{
bool gatewayTransportInit() {
_w5100_spi_en(true); _w5100_spi_en(true);
#if defined(MY_GATEWAY_ESP8266) #if defined(MY_GATEWAY_ESP8266)
#if defined(MY_ESP8266_SSID) #if defined(MY_ESP8266_SSID)
// Turn off access point // Turn off access point
WiFi.mode (WIFI_STA); WiFi.mode (WIFI_STA);
#if defined(MY_ESP8266_HOSTNAME) #if defined(MY_ESP8266_HOSTNAME)
WiFi.hostname(MY_ESP8266_HOSTNAME); WiFi.hostname(MY_ESP8266_HOSTNAME);
#endif #endif
(void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD); #ifdef MY_IP_ADDRESS
#ifdef MY_IP_ADDRESS WiFi.config(_ethernetGatewayIP, _gatewayIp, _subnetIp);
WiFi.config(_ethernetGatewayIP, _gatewayIp, _subnetIp); #endif
#endif (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
while (WiFi.status() != WL_CONNECTED) while (WiFi.status() != WL_CONNECTED) {
{ wait(500);
delay(500); MY_SERIALDEVICE.print(F("."));
MY_SERIALDEVICE.print("."); }
yield(); MY_SERIALDEVICE.print(F("IP: "));
} MY_SERIALDEVICE.println(WiFi.localIP());
MY_SERIALDEVICE.print(F("IP: ")); #endif
MY_SERIALDEVICE.println(WiFi.localIP()); #elif defined(MY_GATEWAY_LINUX)
#endif // 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 */
#else #ifdef MY_USE_UDP
#ifdef MY_IP_ADDRESS _ethernetServer.begin(_ethernetGatewayPort);
Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP); #elif defined(MY_GATEWAY_CLIENT_MODE)
#else #if defined(MY_CONTROLLER_URL_ADDRESS)
// Get IP address from DHCP if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
if (!Ethernet.begin(_ethernetGatewayMAC)) { #else
MY_SERIALDEVICE.print("DHCP FAILURE..."); if (client.connect(_ethernetControllerIP, MY_PORT)) {
_w5100_spi_en(false); #endif
return false; debug(PSTR("Eth: connect\n"));
} _w5100_spi_en(false);
#endif gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
MY_SERIALDEVICE.print(F("IP: ")); _w5100_spi_en(true);
MY_SERIALDEVICE.println(Ethernet.localIP()); presentNode();
// give the Ethernet interface a second to initialize } else {
delay(1000); client.stop();
#endif debug(PSTR("Eth: Failed to connect\n"));
}
#ifdef MY_USE_UDP #else
_ethernetServer.begin(_ethernetGatewayPort); #if defined(MY_GATEWAY_LINUX) && defined(MY_IP_ADDRESS)
#else _ethernetServer.begin(_ethernetGatewayIP);
// we have to use pointers due to the constructor of EthernetServer #else
_ethernetServer.begin(); // we have to use pointers due to the constructor of EthernetServer
#endif /* USE_UDP */ _ethernetServer.begin();
#endif
#endif /* USE_UDP */
_w5100_spi_en(false); _w5100_spi_en(false);
return true; return true;
} }
bool gatewayTransportSend(MyMessage &message) bool gatewayTransportSend(MyMessage &message)
{ {
bool ret = true; int nbytes = 0;
char *_ethernetMsg = protocolFormat(message); char *_ethernetMsg = protocolFormat(message);
setIndication(INDICATION_GW_TX); setIndication(INDICATION_GW_TX);
_w5100_spi_en(true); _w5100_spi_en(true);
#if defined(MY_CONTROLLER_IP_ADDRESS) #if defined(MY_GATEWAY_CLIENT_MODE)
#if defined(MY_USE_UDP) #if defined(MY_USE_UDP)
_ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT); #if defined(MY_CONTROLLER_URL_ADDRESS)
_ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg)); _ethernetServer.beginPacket(MY_CONTROLLER_URL_ADDRESS, MY_PORT);
// returns 1 if the packet was sent successfully #else
ret = _ethernetServer.endPacket(); _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT);
#else #endif
EthernetClient client; _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg));
#if defined(MY_CONTROLLER_URL_ADDRESS) // returns 1 if the packet was sent successfully
if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { nbytes = _ethernetServer.endPacket();
#else #else
if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) { if (!client.connected()) {
#endif client.stop();
client.write(_ethernetMsg, strlen(_ethernetMsg)); #if defined(MY_CONTROLLER_URL_ADDRESS)
} if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
else { #else
// connecting to the server failed! if (client.connect(_ethernetControllerIP, MY_PORT)) {
ret = false; #endif
} debug(PSTR("Eth: connect\n"));
#endif _w5100_spi_en(false);
#else gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
// Send message to connected clients _w5100_spi_en(true);
#if defined(MY_GATEWAY_ESP8266) presentNode();
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) } else {
{ // connecting to the server failed!
if (clients[i] && clients[i].connected()) debug(PSTR("Eth: Failed to connect\n"));
{ _w5100_spi_en(false);
clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg)); return false;
} }
} }
#else nbytes = client.write(_ethernetMsg, strlen(_ethernetMsg));
_ethernetServer.write(_ethernetMsg); #endif
#endif #else
#endif // Send message to connected clients
_w5100_spi_en(false);
return ret;
}
#if defined(MY_GATEWAY_ESP8266) #if defined(MY_GATEWAY_ESP8266)
bool _readFromClient(uint8_t i) { for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
while (clients[i].connected() && clients[i].available()) { if (clients[i] && clients[i].connected()) {
char inChar = clients[i].read(); nbytes += clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
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;
}
} 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;
} }
#else #else
bool _readFromClient() { nbytes = _ethernetServer.write(_ethernetMsg);
while (client.connected() && client.available()) { #endif
char inChar = client.read(); #endif /* MY_GATEWAY_CLIENT_MODE */
if (inputString.idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) { _w5100_spi_en(false);
// if newline then command is complete return (nbytes > 0);
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 { #if (defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX)) && !defined(MY_GATEWAY_CLIENT_MODE)
// add it to the inputString: bool _readFromClient(uint8_t i)
inputString.string[inputString.idx++] = inChar; {
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 { } else {
// Incoming message too long. Throw away // add it to the inputString:
debug(PSTR("Eth: Message too long\n")); inputString[i].string[inputString[i].idx++] = inChar;
inputString.idx = 0;
// Finished with this client's message. Next loop() we'll see if there's more to read.
break;
} }
} 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(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 {
// 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;
}
#endif #endif
bool gatewayTransportAvailable() bool gatewayTransportAvailable(void)
{ {
_w5100_spi_en(true); _w5100_spi_en(true);
#if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100) #if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100)
// renew IP address using DHCP // renew IP address using DHCP
gatewayTransportRenewIP(); gatewayTransportRenewIP();
#endif #endif
#ifdef MY_USE_UDP #ifdef MY_USE_UDP
int packet_size = _ethernetServer.parsePacket();
int packet_size = _ethernetServer.parsePacket(); if (packet_size) {
//debug(PSTR("UDP packet available. Size:%d\n"), packet_size);
if (packet_size) { #if defined(MY_GATEWAY_ESP8266)
//debug(PSTR("UDP packet available. Size:%d\n"), packet_size); _ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
setIndication(INDICATION_GW_RX); inputString[0].string[packet_size] = 0;
#if defined(MY_GATEWAY_ESP8266) debug(PSTR("UDP packet received: %s\n"), inputString[0].string);
_ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH); const bool ok = protocolParse(_ethernetMsg, inputString[0].string);
inputString[0].string[packet_size] = 0; #else
debug(PSTR("UDP packet received: %s\n"), inputString[0].string); _ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
return protocolParse(_ethernetMsg, inputString[0].string); inputString.string[packet_size] = 0;
#else debug(PSTR("UDP packet received: %s\n"), inputString.string);
_ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH); _w5100_spi_en(false);
inputString.string[packet_size] = 0; const bool ok = protocolParse(_ethernetMsg, inputString.string);
debug(PSTR("UDP packet received: %s\n"), inputString.string); #endif
_w5100_spi_en(false); if (ok) {
return protocolParse(_ethernetMsg, inputString.string); setIndication(INDICATION_GW_RX);
#endif
} }
#else return ok;
#if defined(MY_GATEWAY_ESP8266) }
// ESP8266: Go over list of clients and stop any that are no longer connected. #elif defined(MY_GATEWAY_CLIENT_MODE)
// If the server has a new client connection it will be assigned to a free slot. if (!client.connected()) {
bool allSlotsOccupied = true; client.stop();
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) { #if defined(MY_CONTROLLER_URL_ADDRESS)
if (!clients[i].connected()) { if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
if (clientsConnected[i]) { #else
debug(PSTR("Client %d disconnected\n"), i); if (client.connect(_ethernetControllerIP, MY_PORT)) {
clients[i].stop(); #endif
} debug(PSTR("Eth: connect\n"));
//check if there are any new clients _w5100_spi_en(false);
if (_ethernetServer.hasClient()) { gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
clients[i] = _ethernetServer.available(); _w5100_spi_en(true);
inputString[i].idx = 0; presentNode();
debug(PSTR("Client %d connected\n"), i); } else {
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete.")); debug(PSTR("Eth: Failed to connect\n"));
if (presentation) _w5100_spi_en(false);
presentation(); return false;
} }
} }
bool connected = clients[i].connected(); if (_readFromClient()) {
clientsConnected[i] = connected; setIndication(INDICATION_GW_RX);
allSlotsOccupied &= connected; _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();
} }
if (allSlotsOccupied && _ethernetServer.hasClient()) { //check if there are any new clients
//no free/disconnected spot so reject if (_ethernetServer.hasClient()) {
debug(PSTR("No free slot available\n")); clients[i] = _ethernetServer.available();
EthernetClient c = _ethernetServer.available(); inputString[i].idx = 0;
c.stop(); 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();
} }
// Loop over clients connect and read available data }
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) { bool connected = clients[i].connected();
if (_readFromClient(i)) { clientsConnected[i] = connected;
setIndication(INDICATION_GW_RX); allSlotsOccupied &= connected;
_w5100_spi_en(false); }
return true; 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;
} }
#else }
// W5100/ENC module does not have hasClient-method. We can only serve one client at the time. }
EthernetClient newclient = _ethernetServer.available(); #endif /* MY_GATEWAY_ESP8266 */
// if a new client connects make sure to dispose any previous existing sockets #endif
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();
}
}
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
_w5100_spi_en(false); _w5100_spi_en(false);
return false; return false;
} }
MyMessage& gatewayTransportReceive() MyMessage& gatewayTransportReceive(void)
{ {
// Return the last parsed message // Return the last parsed message
return _ethernetMsg; return _ethernetMsg;
} }
#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_LINUX)
#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) void gatewayTransportRenewIP(void)
void gatewayTransportRenewIP()
{ {
/* renew/rebind IP address /* renew/rebind IP address
0 - nothing happened 0 - nothing happened
@@ -378,8 +431,9 @@ void gatewayTransportRenewIP()
unsigned long now = hwMillis(); unsigned long now = hwMillis();
// http://playground.arduino.cc/Code/TimingRollover // http://playground.arduino.cc/Code/TimingRollover
if ((long)(now - next_time) < 0) if ((long)(now - next_time) < 0) {
return; return;
}
if (Ethernet.maintain() & ~(0x06)) { if (Ethernet.maintain() & ~(0x06)) {
debug(PSTR("IP was not renewed correctly\n")); debug(PSTR("IP was not renewed correctly\n"));
/* Error occured -> IP was not renewed */ /* Error occured -> IP was not renewed */
@@ -388,4 +442,4 @@ void gatewayTransportRenewIP()
_w5100_spi_en(false); _w5100_spi_en(false);
next_time = now + MY_IP_RENEWAL_INTERVAL; next_time = now + MY_IP_RENEWAL_INTERVAL;
} }
#endif /* IP_ADDRESS_DHCP */ #endif
+88 -152
View File
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes. * network topology allowing messages to be routed to nodes.
* *
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org> * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
* *
* Documentation: http://www.mysensors.org * 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 // 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 #if defined MY_CONTROLLER_IP_ADDRESS
IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS); IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS);
#endif #endif
#if defined(MY_GATEWAY_ESP8266) #if defined(MY_GATEWAY_ESP8266)
#define EthernetClient WiFiClient #define EthernetClient WiFiClient
#if defined(MY_IP_ADDRESS) #if defined(MY_IP_ADDRESS)
IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
#endif #endif
#elif defined(MY_GATEWAY_LINUX)
// Nothing to do here
#else #else
byte _MQTT_clientMAC[] = { MY_MAC_ADDRESS }; uint8_t _MQTT_clientMAC[] = { MY_MAC_ADDRESS };
#endif #endif
#if defined(MY_IP_ADDRESS) #if defined(MY_IP_ADDRESS)
IPAddress _MQTT_clientIp(MY_IP_ADDRESS); IPAddress _MQTT_clientIp(MY_IP_ADDRESS);
#endif #endif
static EthernetClient _MQTT_ethClient; static EthernetClient _MQTT_ethClient;
@@ -45,107 +48,37 @@ static bool _MQTT_connecting = true;
static bool _MQTT_available = false; static bool _MQTT_available = false;
static MyMessage _MQTT_msg; static MyMessage _MQTT_msg;
uint8_t protocolH2i(char c) { bool gatewayTransportSend(MyMessage &message)
uint8_t i = 0; {
if (c <= '9') if (!_MQTT_client.connected()) {
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())
return false; 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));
}
void incomingMQTT(char* topic, byte* 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++;
} }
setIndication(INDICATION_GW_TX);
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, uint8_t* payload, unsigned int length)
{
debug(PSTR("Message arrived on topic: %s\n"), topic);
_MQTT_available = protocolMQTTParse(_MQTT_msg, topic, payload, length);
}
bool reconnectMQTT() { bool reconnectMQTT(void)
{
debug(PSTR("Attempting MQTT connection...\n")); debug(PSTR("Attempting MQTT connection...\n"));
// Attempt to connect // Attempt to connect
if (_MQTT_client.connect(MY_MQTT_CLIENT_ID if (_MQTT_client.connect(MY_MQTT_CLIENT_ID
#if defined(MY_MQTT_USER) && defined(MY_MQTT_PASSWORD) #if defined(MY_MQTT_USER) && defined(MY_MQTT_PASSWORD)
, MY_MQTT_USER, MY_MQTT_PASSWORD , MY_MQTT_USER, MY_MQTT_PASSWORD
#endif #endif
)) { )) {
debug(PSTR("MQTT connected\n")); debug(PSTR("MQTT connected\n"));
// Send presentation of locally attached sensors (and node if applicable)
presentNode();
// Once connected, publish an announcement... // Once connected, publish an announcement...
//_MQTT_client.publish("outTopic","hello world"); //_MQTT_client.publish("outTopic","hello world");
// ... and resubscribe // ... and resubscribe
@@ -155,54 +88,60 @@ bool reconnectMQTT() {
return false; return false;
} }
bool gatewayTransportConnect() { bool gatewayTransportConnect(void)
#if defined(MY_GATEWAY_ESP8266) {
while (WiFi.status() != WL_CONNECTED) { #if defined(MY_GATEWAY_ESP8266)
delay(500); // delay calls yield while (WiFi.status() != WL_CONNECTED) {
MY_SERIALDEVICE.print("."); wait(500);
} MY_SERIALDEVICE.print(F("."));
MY_SERIALDEVICE.print("IP: "); }
MY_SERIALDEVICE.println(WiFi.localIP()); MY_SERIALDEVICE.print(F("IP: "));
#else MY_SERIALDEVICE.println(WiFi.localIP());
#ifdef MY_IP_ADDRESS #elif defined(MY_GATEWAY_LINUX)
Ethernet.begin(_MQTT_clientMAC, _MQTT_clientIp); #if defined(MY_IP_ADDRESS)
#else //TODO
// Get IP address from DHCP #endif
if (!Ethernet.begin(_MQTT_clientMAC)) { #else
MY_SERIALDEVICE.print("DHCP FAILURE..."); #ifdef MY_IP_ADDRESS
_MQTT_connecting = false; Ethernet.begin(_MQTT_clientMAC, _MQTT_clientIp);
return false; #else
} // Get IP address from DHCP
#endif if (!Ethernet.begin(_MQTT_clientMAC)) {
MY_SERIALDEVICE.print("IP: "); MY_SERIALDEVICE.print(F("DHCP FAILURE..."));
MY_SERIALDEVICE.println(Ethernet.localIP()); _MQTT_connecting = false;
// give the Ethernet interface a second to initialize return false;
delay(1000); }
#endif #endif
MY_SERIALDEVICE.print(F("IP: "));
MY_SERIALDEVICE.println(Ethernet.localIP());
// give the Ethernet interface a second to initialize
delay(1000);
#endif
return true; return true;
} }
bool gatewayTransportInit() { bool gatewayTransportInit(void)
{
_MQTT_connecting = true; _MQTT_connecting = true;
#if defined(MY_CONTROLLER_IP_ADDRESS) #if defined(MY_CONTROLLER_IP_ADDRESS)
_MQTT_client.setServer(_brokerIp, MY_PORT); _MQTT_client.setServer(_brokerIp, MY_PORT);
#else #else
_MQTT_client.setServer(MY_CONTROLLER_URL_ADDRESS, MY_PORT); _MQTT_client.setServer(MY_CONTROLLER_URL_ADDRESS, MY_PORT);
#endif #endif
_MQTT_client.setCallback(incomingMQTT); _MQTT_client.setCallback(incomingMQTT);
#if defined(MY_GATEWAY_ESP8266) #if defined(MY_GATEWAY_ESP8266)
// Turn off access point // Turn off access point
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
#if defined(MY_ESP8266_HOSTNAME) #if defined(MY_ESP8266_HOSTNAME)
WiFi.hostname(MY_ESP8266_HOSTNAME); WiFi.hostname(MY_ESP8266_HOSTNAME);
#endif #endif
(void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD); (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
#ifdef MY_IP_ADDRESS #ifdef MY_IP_ADDRESS
WiFi.config(_MQTT_clientIp, _gatewayIp, _subnetIp); WiFi.config(_MQTT_clientIp, _gatewayIp, _subnetIp);
#endif #endif
#endif #endif
gatewayTransportConnect(); gatewayTransportConnect();
@@ -210,30 +149,27 @@ bool gatewayTransportInit() {
return true; return true;
} }
bool gatewayTransportAvailable(void)
{
bool gatewayTransportAvailable() { if (_MQTT_connecting) {
if (_MQTT_connecting)
return false; return false;
}
//keep lease on dhcp address //keep lease on dhcp address
//Ethernet.maintain(); //Ethernet.maintain();
if (!_MQTT_client.connected()) { if (!_MQTT_client.connected()) {
//reinitialise client //reinitialise client
if (gatewayTransportConnect()) if (gatewayTransportConnect()) {
reconnectMQTT(); reconnectMQTT();
}
return false; return false;
} }
_MQTT_client.loop(); _MQTT_client.loop();
return _MQTT_available; return _MQTT_available;
} }
MyMessage & gatewayTransportReceive() { MyMessage & gatewayTransportReceive(void)
{
// Return the last parsed message // Return the last parsed message
_MQTT_available = false; _MQTT_available = false;
return _MQTT_msg; return _MQTT_msg;
} }
+21 -13
View File
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes. * network topology allowing messages to be routed to nodes.
* *
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org> * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
* *
* Documentation: http://www.mysensors.org * Documentation: http://www.mysensors.org
@@ -17,42 +17,49 @@
* version 2 as published by the Free Software Foundation. * version 2 as published by the Free Software Foundation.
*/ */
#include "MyConfig.h" #include "MyConfig.h"
#include "MyProtocol.h" #include "MyProtocol.h"
#include "MyGatewayTransport.h" #include "MyGatewayTransport.h"
#include "MyMessage.h" #include "MyMessage.h"
#include "MyProtocol.h" #include "MyProtocol.h"
// global variables
extern MyMessage _msgTmp;
char _serialInputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface char _serialInputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface
int _serialInputPos; uint8_t _serialInputPos;
MyMessage _serialMsg; MyMessage _serialMsg;
bool gatewayTransportSend(MyMessage &message)
bool gatewayTransportSend(MyMessage &message) { {
setIndication(INDICATION_GW_TX); setIndication(INDICATION_GW_TX);
MY_SERIALDEVICE.print(protocolFormat(message)); MY_SERIALDEVICE.print(protocolFormat(message));
// Serial print is always successful // Serial print is always successful
return true; return true;
} }
bool gatewayTransportInit() { bool gatewayTransportInit(void)
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete.")); {
(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; return true;
} }
bool gatewayTransportAvailable(void)
bool gatewayTransportAvailable() { {
while (MY_SERIALDEVICE.available()) { while (MY_SERIALDEVICE.available()) {
// get the new byte: // 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 // if the incoming character is a newline, set a flag
// so the main loop can do something about it: // so the main loop can do something about it:
if (_serialInputPos < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) { if (_serialInputPos < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
if (inChar == '\n') { if (inChar == '\n') {
_serialInputString[_serialInputPos] = 0; _serialInputString[_serialInputPos] = 0;
bool ok = protocolParse(_serialMsg, _serialInputString); const bool ok = protocolParse(_serialMsg, _serialInputString);
if (ok) {
setIndication(INDICATION_GW_RX);
}
_serialInputPos = 0; _serialInputPos = 0;
return ok; return ok;
} else { } else {
@@ -68,7 +75,8 @@ bool gatewayTransportAvailable() {
return false; return false;
} }
MyMessage & gatewayTransportReceive() { MyMessage & gatewayTransportReceive(void)
{
// Return the last parsed message // Return the last parsed message
return _serialMsg; return _serialMsg;
} }
+91 -5
View File
@@ -1,4 +1,4 @@
/** /*
* The MySensors Arduino library handles the wireless radio link and protocol * The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice. * between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each * 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. * version 2 as published by the Free Software Foundation.
*/ */
/**
* @file MyHw.h
*
* MySensors hardware abstraction layer
*/
#ifndef MyHw_h #ifndef MyHw_h
#define MyHw_h #define MyHw_h
@@ -30,23 +36,103 @@
// Implement these as functions or macros // Implement these as functions or macros
/* /*
#define hwDigitalWrite(__pin, __value)
#define hwInit() MY_SERIALDEVICE.begin(BAUD_RATE) #define hwInit() MY_SERIALDEVICE.begin(BAUD_RATE)
#define hwWatchdogReset() wdt_reset() #define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1) #define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis() #define hwMillis() millis()
#define hwDigitalWrite(__pin, __value)
#define hwDigitalRead(__pin)
#define hwPinMode(__pin, __value)
void hwReadConfigBlock(void* buf, void* adr, size_t length); void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfig(int adr, uint8_t value); void hwWriteConfig(int adr, uint8_t value);
uint8_t hwReadConfig(int adr); 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); 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 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);
#ifdef MY_DEBUG /**
void hwDebugPrint(const char *fmt, ... ); * 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 #endif
#ifdef MY_DEBUG
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 #endif // #ifdef MyHw_h
+163 -91
View File
@@ -21,15 +21,35 @@
#include "MyHwATMega328.h" #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 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 // 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 // disable ADC for power saving
ADCSRA &= ~(1 << ADEN); ADCSRA &= ~(1 << ADEN);
// save WDT settings // save WDT settings
uint8_t WDTsave = WDTCSR; uint8_t WDTsave = WDTCSR;
if (period != SLEEP_FOREVER) if (period != SLEEP_FOREVER) {
{
wdt_enable(period); wdt_enable(period);
// enable WDT interrupt before system reset // enable WDT interrupt before system reset
WDTCSR |= (1 << WDCE) | (1 << WDIE); WDTCSR |= (1 << WDCE) | (1 << WDIE);
@@ -59,10 +78,11 @@ void hwPowerDown(period_t period) {
#if defined __AVR_ATmega328P__ #if defined __AVR_ATmega328P__
sleep_bod_disable(); sleep_bod_disable();
#endif #endif
// Enable interrupts & sleep until WDT or ext. interrupt
sei(); sei();
// sleep until WDT or ext. interrupt // Directly sleep CPU, to prevent race conditions! (see chapter 7.7 of ATMega328P datasheet)
sleep_cpu(); sleep_cpu();
sleep_disable(); sleep_disable();
// restore previous WDT settings // restore previous WDT settings
cli(); cli();
wdt_reset(); wdt_reset();
@@ -75,88 +95,136 @@ void hwPowerDown(period_t period) {
ADCSRA |= (1 << ADEN); ADCSRA |= (1 << ADEN);
} }
void hwInternalSleep(unsigned long ms) { void hwInternalSleep(unsigned long ms)
{
// Let serial prints finish (debug, log etc) // Let serial prints finish (debug, log etc)
#ifndef MY_DISABLED_SERIAL #ifndef MY_DISABLED_SERIAL
MY_SERIALDEVICE.flush(); MY_SERIALDEVICE.flush();
#endif #endif
// reset interrupt trigger var while (!interruptWakeUp() && ms >= 8000) {
pinIntTrigger = 0; hwPowerDown(SLEEP_8S);
while (!pinIntTrigger && ms >= 8000) { hwPowerDown(SLEEP_8S); ms -= 8000; } ms -= 8000;
if (!pinIntTrigger && ms >= 4000) { hwPowerDown(SLEEP_4S); ms -= 4000; } }
if (!pinIntTrigger && ms >= 2000) { hwPowerDown(SLEEP_2S); ms -= 2000; } if (!interruptWakeUp() && ms >= 4000) {
if (!pinIntTrigger && ms >= 1000) { hwPowerDown(SLEEP_1S); ms -= 1000; } hwPowerDown(SLEEP_4S);
if (!pinIntTrigger && ms >= 500) { hwPowerDown(SLEEP_500MS); ms -= 500; } ms -= 4000;
if (!pinIntTrigger && ms >= 250) { hwPowerDown(SLEEP_250MS); ms -= 250; } }
if (!pinIntTrigger && ms >= 125) { hwPowerDown(SLEEP_120MS); ms -= 120; } if (!interruptWakeUp() && ms >= 2000) {
if (!pinIntTrigger && ms >= 64) { hwPowerDown(SLEEP_60MS); ms -= 60; } hwPowerDown(SLEEP_2S);
if (!pinIntTrigger && ms >= 32) { hwPowerDown(SLEEP_30MS); ms -= 30; } ms -= 2000;
if (!pinIntTrigger && ms >= 16) { hwPowerDown(SLEEP_15MS); ms -= 15; } }
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); hwInternalSleep(ms);
return -1; return MY_WAKE_UP_BY_TIMER;
} }
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
return hwSleep(interrupt,mode,0xFF,0x00,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) { int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
// reset interrupt trigger var unsigned long ms)
pinIntTrigger = 0; {
// attach interrupts // Disable interrupts until going to sleep, otherwise interrupts occurring between attachInterrupt()
attachInterrupt(interrupt1, wakeUp, mode1); // and sleep might cause the ATMega to not wakeup from sleep as interrupt has already be handled!
if (interrupt2!=0xFF) attachInterrupt(interrupt2, wakeUp2, mode2); 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) { if (ms>0) {
// sleep for defined time // sleep for defined time
hwInternalSleep(ms); hwInternalSleep(ms);
} else { } else {
// sleep until ext interrupt triggered // sleep until ext interrupt triggered
hwPowerDown(SLEEP_FOREVER); hwPowerDown(SLEEP_FOREVER);
} }
detachInterrupt(interrupt1);
if (interrupt2!=0xFF) detachInterrupt(interrupt2);
// default: no interrupt triggered, timer wake up
int8_t retVal = -1;
if (pinIntTrigger == 1) { // Assure any interrupts attached, will get detached when they did not occur.
retVal = (int8_t)interrupt1; if (interrupt1 != INVALID_INTERRUPT_NUM) {
} else if (pinIntTrigger == 2) { detachInterrupt(interrupt1);
retVal = (int8_t)interrupt2;
} }
return retVal; if (interrupt2 != INVALID_INTERRUPT_NUM) {
detachInterrupt(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<int8_t>(_wokeUpByInterrupt);
}
// Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
_wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
return ret;
} }
uint16_t hwCPUVoltage() { #if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
// Measure Vcc against 1.1V Vref uint16_t hwCPUVoltage()
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) {
ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); // Measure Vcc against 1.1V Vref
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = (_BV(MUX5) | _BV(MUX0)); ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = (_BV(MUX3) | _BV(MUX2)); ADMUX = (_BV(MUX5) | _BV(MUX0));
#else #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); ADMUX = (_BV(MUX3) | _BV(MUX2));
#endif #else
ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#endif
// Vref settle // Vref settle
delay(70); delay(70);
// Do conversion // Do conversion
ADCSRA |= _BV(ADSC); ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA,ADSC)); while (bit_is_set(ADCSRA,ADSC)) {};
// return Vcc in mV // return Vcc in mV
return (1125300UL) / ADC; return (1125300UL) / ADC;
} }
uint16_t hwCPUFrequency() { uint16_t hwCPUFrequency()
noInterrupts(); {
cli();
// setup timer1 // setup timer1
TIFR1 = 0xFF; TIFR1 = 0xFF;
TCNT1 = 0; TCNT1 = 0;
TCCR1A = 0; TCCR1A = 0;
TCCR1C = 0; TCCR1C = 0;
// save WDT settings // save WDT settings
@@ -166,47 +234,51 @@ uint16_t hwCPUFrequency() {
WDTCSR |= (1 << WDIE); WDTCSR |= (1 << WDIE);
wdt_reset(); wdt_reset();
// start timer1 with 1024 prescaling // start timer1 with 1024 prescaling
TCCR1B = _BV(CS12) | _BV(CS10); TCCR1B = _BV(CS12) | _BV(CS10);
// wait until wdt interrupt // wait until wdt interrupt
while (bit_is_clear(WDTCSR,WDIF)); while (bit_is_clear(WDTCSR,WDIF)) {};
// stop timer // stop timer
TCCR1B = 0; TCCR1B = 0;
// restore WDT settings // restore WDT settings
wdt_reset(); wdt_reset();
WDTCSR |= (1 << WDCE) | (1 << WDE); WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = WDTsave; WDTCSR = WDTsave;
interrupts(); sei();
// return frequency in 1/10MHz (accuracy +- 10%) // return frequency in 1/10MHz (accuracy +- 10%)
return TCNT1 * 2048UL / 100000UL; return TCNT1 * 2048UL / 100000UL;
} }
uint16_t hwFreeMem() { uint16_t hwFreeMem()
extern int __heap_start, *__brkval; {
int v; extern int __heap_start, *__brkval;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
} }
#endif
#ifdef MY_DEBUG #ifdef MY_DEBUG
void hwDebugPrint(const char *fmt, ... ) { void hwDebugPrint(const char *fmt, ... )
char fmtBuffer[300]; {
#ifdef MY_GATEWAY_FEATURE char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) #ifdef MY_GATEWAY_FEATURE
snprintf_P(fmtBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE); // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
MY_SERIALDEVICE.print(fmtBuffer); snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
#endif MY_SERIALDEVICE.print(fmtBuffer);
#else
// prepend timestamp (AVR nodes)
MY_SERIALDEVICE.print(hwMillis());
MY_SERIALDEVICE.print(" ");
#endif
va_list args; va_list args;
va_start (args, fmt ); va_start (args, fmt );
va_end (args); #ifdef MY_GATEWAY_FEATURE
#ifdef MY_GATEWAY_FEATURE // Truncate message if this is gateway node
// Truncate message if this is gateway node vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
vsnprintf_P(fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, fmt, args); fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1] = '\n'; fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH] = '\0'; #else
#else vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
vsnprintf_P(fmtBuffer, 299, fmt, args); #endif
#endif
va_end (args); va_end (args);
MY_SERIALDEVICE.print(fmtBuffer); MY_SERIALDEVICE.print(fmtBuffer);
MY_SERIALDEVICE.flush(); MY_SERIALDEVICE.flush();
+36 -27
View File
@@ -27,56 +27,61 @@
#include <avr/power.h> #include <avr/power.h>
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include <avr/wdt.h> #include <avr/wdt.h>
#include <util/atomic.h>
#ifdef __cplusplus #ifdef __cplusplus
#include <Arduino.h> #include <Arduino.h>
#endif #endif
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial #define MY_SERIALDEVICE Serial
#endif
#if defined __AVR_ATmega328P__ #if defined __AVR_ATmega328P__
#ifndef sleep_bod_disable #ifndef sleep_bod_disable
#define sleep_bod_disable() \ #define sleep_bod_disable() \
do { \ do { \
unsigned char tempreg; \ unsigned char tempreg; \
__asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \ __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
"ori %[tempreg], %[bods_bodse]" "\n\t" \ "ori %[tempreg], %[bods_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" "\n\t" \ "out %[mcucr], %[tempreg]" "\n\t" \
"andi %[tempreg], %[not_bodse]" "\n\t" \ "andi %[tempreg], %[not_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" \ "out %[mcucr], %[tempreg]" \
: [tempreg] "=&d" (tempreg) \ : [tempreg] "=&d" (tempreg) \
: [mcucr] "I" _SFR_IO_ADDR(MCUCR), \ : [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
[bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \ [bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
[not_bodse] "i" (~_BV(BODSE))); \ [not_bodse] "i" (~_BV(BODSE))); \
} while (0) } while (0)
#endif #endif
#endif #endif
// Define these as macros to save valuable space // Define these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) #define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
#define hwDigitalRead(__pin) digitalReadFast(__pin)
#define hwPinMode(__pin, __value) pinModeFast(__pin, __value)
#if defined(MY_DISABLED_SERIAL)
#define hwInit()
#else
#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE) #define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE)
#endif
#define hwWatchdogReset() wdt_reset() #define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1) #define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis() #define hwMillis() millis()
#define hwReadConfig(__pos) (eeprom_read_byte((uint8_t*)(__pos))) #define hwRandomNumberInit() randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN))
#define hwReadConfig(__pos) eeprom_read_byte((uint8_t*)(__pos))
#ifndef eeprom_update_byte #define hwWriteConfig(__pos, __val) eeprom_update_byte((uint8_t*)(__pos), (__val))
#define hwWriteConfig(loc, val) if((uint8_t)(val) != eeprom_read_byte((uint8_t*)(loc))) { eeprom_write_byte((uint8_t*)(loc), (val)); } #define hwReadConfigBlock(__buf, __pos, __length) eeprom_read_block((void*)(__buf), (void*)(__pos), (__length))
#else #define hwWriteConfigBlock(__buf, __pos, __length) eeprom_update_block((void*)(__buf), (void*)(__pos), (__length))
#define hwWriteConfig(__pos, __value) (eeprom_update_byte((uint8_t*)(__pos), (__value)))
#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)))
enum period_t enum period_t {
{
SLEEP_15MS, SLEEP_15MS,
SLEEP_30MS, SLEEP_30MS,
SLEEP_60MS, SLEEP_60MS,
@@ -92,4 +97,8 @@ enum period_t
void hwInternalSleep(unsigned long ms); void hwInternalSleep(unsigned long ms);
#ifndef DOXYGEN
#define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
#endif /* DOXYGEN */
#endif #endif
+73 -80
View File
@@ -20,135 +20,128 @@
#include "MyHwESP8266.h" #include "MyHwESP8266.h"
#include <EEPROM.h> #include <EEPROM.h>
/* void hwInit(void)
int8_t pinIntTrigger = 0;
void wakeUp() //place to send the interrupts
{ {
pinIntTrigger = 1; #if !defined(MY_DISABLED_SERIAL)
} MY_SERIALDEVICE.begin(MY_BAUD_RATE, SERIAL_8N1, MY_ESP8266_SERIAL_MODE, 1);
void wakeUp2() //place to send the second interrupts MY_SERIALDEVICE.setDebugOutput(true);
{ #endif
pinIntTrigger = 2; EEPROM.begin(EEPROM_size);
} }
// Watchdog Timer interrupt service routine. This routine is required void hwReadConfigBlock(void* buf, void* addr, size_t length)
// to allow automatic WDIF and WDIE bit clearance in hardware.
ISR (WDT_vect)
{ {
// WDIE & WDIF is cleared in hardware upon entering this ISR uint8_t* dst = static_cast<uint8_t*>(buf);
wdt_disable(); int pos = reinterpret_cast<int>(addr);
} while (length-- > 0) {
*/ *dst++ = EEPROM.read(pos++);
}
static void hwInitConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*/ )
{
static bool initDone = false;
if (!initDone)
{
EEPROM.begin(length);
initDone = true;
}
} }
void hwReadConfigBlock(void* buf, void* adr, size_t length) void hwWriteConfigBlock(void* buf, void* addr, size_t length)
{ {
hwInitConfigBlock(); uint8_t* src = static_cast<uint8_t*>(buf);
uint8_t* dst = static_cast<uint8_t*>(buf); int pos = reinterpret_cast<int>(addr);
int offs = reinterpret_cast<int>(adr); while (length-- > 0) {
while (length-- > 0) EEPROM.write(pos++, *src++);
{ }
*dst++ = EEPROM.read(offs++); // see implementation, commit only executed if diff
} EEPROM.commit();
} }
void hwWriteConfigBlock(void* buf, void* adr, size_t length) uint8_t hwReadConfig(const int addr)
{ {
hwInitConfigBlock(); uint8_t value;
uint8_t* src = static_cast<uint8_t*>(buf); hwReadConfigBlock(&value, reinterpret_cast<void*>(addr), 1);
int offs = reinterpret_cast<int>(adr); return value;
while (length-- > 0)
{
EEPROM.write(offs++, *src++);
}
EEPROM.commit();
} }
uint8_t hwReadConfig(int adr) void hwWriteConfig(const int addr, uint8_t value)
{ {
uint8_t value; hwWriteConfigBlock(&value, reinterpret_cast<void*>(addr), 1);
hwReadConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
return value;
}
void hwWriteConfig(int adr, uint8_t value)
{
uint8_t curr = hwReadConfig(adr);
if (curr != value)
{
hwWriteConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
}
} }
int8_t hwSleep(unsigned long ms) { int8_t hwSleep(unsigned long ms)
{
// TODO: Not supported! // TODO: Not supported!
(void)ms; (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! // TODO: Not supported!
(void)interrupt; (void)interrupt;
(void)mode; (void)mode;
(void)ms; (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! // TODO: Not supported!
(void)interrupt1; (void)interrupt1;
(void)mode1; (void)mode1;
(void)interrupt2; (void)interrupt2;
(void)mode2; (void)mode2;
(void)ms; (void)ms;
return -2; return MY_SLEEP_NOT_POSSIBLE;
} }
#if defined(MY_SPECIAL_DEBUG)
// settings for getVcc()
ADC_MODE(ADC_VCC); ADC_MODE(ADC_VCC);
#else
// [default] settings for analogRead(A0)
ADC_MODE(ADC_TOUT);
#endif
uint16_t hwCPUVoltage() { #if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
// in mV
uint16_t hwCPUVoltage()
{
#if defined(MY_SPECIAL_DEBUG)
// in mV, requires ADC_VCC set
return ESP.getVcc(); return ESP.getVcc();
#else
// not possible
return 0;
#endif
} }
uint16_t hwCPUFrequency() { uint16_t hwCPUFrequency()
{
// in 1/10Mhz // in 1/10Mhz
return ESP.getCpuFreqMHz()*10; return ESP.getCpuFreqMHz()*10;
} }
uint16_t hwFreeMem() { uint16_t hwFreeMem()
{
return ESP.getFreeHeap(); return ESP.getFreeHeap();
} }
#endif
#ifdef MY_DEBUG #ifdef MY_DEBUG
void hwDebugPrint(const char *fmt, ... ) { void hwDebugPrint(const char *fmt, ... )
char fmtBuffer[300]; {
#ifdef MY_GATEWAY_FEATURE char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) #ifdef MY_GATEWAY_FEATURE
snprintf_P(fmtBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE); // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
MY_SERIALDEVICE.print(fmtBuffer); snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
#endif MY_SERIALDEVICE.print(fmtBuffer);
#endif
va_list args; va_list args;
va_start (args, fmt ); va_start (args, fmt );
va_end (args); #ifdef MY_GATEWAY_FEATURE
#ifdef MY_GATEWAY_FEATURE // Truncate message if this is gateway node
// Truncate message if this is gateway node vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
vsnprintf_P(fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, fmt, args); fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1] = '\n'; fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH] = '\0'; #else
#else vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
vsnprintf_P(fmtBuffer, 299, fmt, args); #endif
#endif
va_end (args); va_end (args);
MY_SERIALDEVICE.print(fmtBuffer); MY_SERIALDEVICE.print(fmtBuffer);
MY_SERIALDEVICE.flush(); MY_SERIALDEVICE.flush();
+24 -6
View File
@@ -25,23 +25,41 @@
#include <Arduino.h> #include <Arduino.h>
#endif #endif
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial #define MY_SERIALDEVICE Serial
#endif
#define min(a,b) ((a)<(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b))
#define max(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 these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) #define hwDigitalRead(__pin) digitalRead(__pin)
#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true) #define hwPinMode(__pin, __value) pinMode(__pin, __value)
#define hwWatchdogReset() wdt_reset() #define hwWatchdogReset() wdt_reset()
#define hwReboot() ESP.restart(); #define hwReboot() ESP.restart()
#define hwMillis() millis() #define hwMillis() millis()
#define hwRandomNumberInit() randomSeed(RANDOM_REG32)
void hwInit(void);
void hwReadConfigBlock(void* buf, void* adr, size_t length); void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfig(int adr, uint8_t value); void hwWriteConfig(const int addr, uint8_t value);
uint8_t hwReadConfig(int adr); 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 #endif // #ifdef ARDUINO_ARCH_ESP8266
+132
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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 <stdarg.h>
#include <time.h>
#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
+91
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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 <cstdlib>
#include <pthread.h>
#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
+146
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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 <stdarg.h>
#include <time.h>
#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);
}
+55
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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 <stdint.h>
#include <stdlib.h>
#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
+141 -96
View File
@@ -42,142 +42,187 @@ ISR (WDT_vect)
void i2c_eeprom_write_byte(unsigned int eeaddress, byte data ) { void i2c_eeprom_write_byte(unsigned int eeaddress, byte data )
int rdata = data; {
Wire.beginTransmission(I2C_EEP_ADDRESS); int rdata = data;
Wire.write((int)(eeaddress >> 8)); // MSB Wire.beginTransmission(I2C_EEP_ADDRESS);
Wire.write((int)(eeaddress & 0xFF)); // LSB Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write(rdata); Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.endTransmission(); Wire.write(rdata);
Wire.endTransmission();
} }
byte i2c_eeprom_read_byte(unsigned int eeaddress ) { byte i2c_eeprom_read_byte(unsigned int eeaddress )
byte rdata = 0xFF; {
Wire.beginTransmission(I2C_EEP_ADDRESS); byte rdata = 0xFF;
Wire.write((int)(eeaddress >> 8)); // MSB Wire.beginTransmission(I2C_EEP_ADDRESS);
Wire.write((int)(eeaddress & 0xFF)); // LSB Wire.write((int)(eeaddress >> 8)); // MSB
Wire.endTransmission(); Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.requestFrom(I2C_EEP_ADDRESS,1); Wire.endTransmission();
if (Wire.available()) rdata = Wire.read(); Wire.requestFrom(I2C_EEP_ADDRESS,1);
return rdata; if (Wire.available()) {
rdata = Wire.read();
}
return rdata;
} }
void hwReadConfigBlock(void* buf, void* adr, size_t length) void hwReadConfigBlock(void* buf, void* adr, size_t length)
{ {
uint8_t* dst = static_cast<uint8_t*>(buf); uint8_t* dst = static_cast<uint8_t*>(buf);
int offs = reinterpret_cast<int>(adr); int offs = reinterpret_cast<int>(adr);
while (length-- > 0) while (length-- > 0) {
{ *dst++ = i2c_eeprom_read_byte(offs++);
*dst++ = i2c_eeprom_read_byte(offs++); }
}
} }
void hwWriteConfigBlock(void* buf, void* adr, size_t length) void hwWriteConfigBlock(void* buf, void* adr, size_t length)
{ {
uint8_t* src = static_cast<uint8_t*>(buf); uint8_t* src = static_cast<uint8_t*>(buf);
int offs = reinterpret_cast<int>(adr); int offs = reinterpret_cast<int>(adr);
while (length-- > 0) while (length-- > 0) {
{ i2c_eeprom_write_byte(offs++, *src++);
i2c_eeprom_write_byte(offs++, *src++); }
}
} }
uint8_t hwReadConfig(int adr) uint8_t hwReadConfig(int adr)
{ {
uint8_t value; uint8_t value;
hwReadConfigBlock(&value, reinterpret_cast<void*>(adr), 1); hwReadConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
return value; return value;
} }
void hwWriteConfig(int adr, uint8_t value) void hwWriteConfig(int adr, uint8_t value)
{ {
uint8_t curr = hwReadConfig(adr); uint8_t curr = hwReadConfig(adr);
if (curr != value) if (curr != value) {
{ hwWriteConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
hwWriteConfigBlock(&value, reinterpret_cast<void*>(adr), 1); }
}
} }
void hwInit() { void hwInit()
MY_SERIALDEVICE.begin(MY_BAUD_RATE); {
#ifndef MY_GATEWAY_W5100 MY_SERIALDEVICE.begin(MY_BAUD_RATE);
while (!MY_SERIALDEVICE) {} #if defined(MY_GATEWAY_SERIAL)
#endif while (!MY_SERIALDEVICE) {}
Wire.begin(); #endif
Wire.begin();
} }
void hwWatchdogReset() { void hwWatchdogReset()
// TODO: Not supported! {
// TODO: Not supported!
} }
void hwReboot() { void hwReboot()
// TODO: Not supported! {
NVIC_SystemReset();
while (true);
} }
int8_t hwSleep(unsigned long ms) { int8_t hwSleep(unsigned long ms)
// TODO: Not supported! {
(void)ms; // TODO: Not supported!
return -2; (void)ms;
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; // TODO: Not supported!
(void)mode; (void)interrupt;
(void)ms; (void)mode;
return -2; (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) { int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
// TODO: Not supported! unsigned long ms)
(void)interrupt1; {
(void)mode1; // TODO: Not supported!
(void)interrupt2; (void)interrupt1;
(void)mode2; (void)mode1;
(void)ms; (void)interrupt2;
return -2; (void)mode2;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
} }
uint16_t hwCPUVoltage() { #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()
{
// TODO: Not supported! // TODO: Not supported!
return 0; return 0;
} }
#endif
uint16_t hwCPUFrequency() {
// TODO: Not supported!
return 0;
}
uint16_t hwFreeMem() {
// TODO: Not supported!
return 0;
}
#ifdef MY_DEBUG #ifdef MY_DEBUG
void hwDebugPrint(const char *fmt, ... ) { void hwDebugPrint(const char *fmt, ... )
if (MY_SERIALDEVICE) { {
char fmtBuffer[300]; if (MY_SERIALDEVICE) {
#ifdef MY_GATEWAY_FEATURE char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
#ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) // 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); MY_SERIALDEVICE.print(fmtBuffer);
#endif #endif
va_list args; va_list args;
va_start (args, fmt ); va_start (args, fmt );
va_end (args); #ifdef MY_GATEWAY_FEATURE
#ifdef MY_GATEWAY_FEATURE
// Truncate message if this is gateway node // Truncate message if this is gateway node
vsnprintf(fmtBuffer, 60, fmt, args); vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args);
fmtBuffer[59] = '\n'; fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
fmtBuffer[60] = '\0'; fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
#else #else
vsnprintf(fmtBuffer, 299, fmt, args); vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args);
#endif #endif
va_end (args); va_end (args);
MY_SERIALDEVICE.print(fmtBuffer); MY_SERIALDEVICE.print(fmtBuffer);
// MY_SERIALDEVICE.flush(); // MY_SERIALDEVICE.flush();
//MY_SERIALDEVICE.write(freeRam()); //MY_SERIALDEVICE.write(freeRam());
} }
} }
#endif #endif
+30 -10
View File
@@ -34,28 +34,48 @@
#define max(a,b) ((a)>(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b))
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__) #define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
uint8_t configBlock[1024];
// Define these as macros to save valuable space // 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 hwInit();
void hwWatchdogReset(); void hwWatchdogReset();
void hwReboot(); void hwReboot();
#define hwMillis() millis()
void hwReadConfigBlock(void* buf, void* adr, size_t length); void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfig(int adr, uint8_t value); void hwWriteConfig(int adr, uint8_t value);
uint8_t hwReadConfig(int adr); uint8_t hwReadConfig(int adr);
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE SerialUSB #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 #endif // #ifdef ARDUINO_ARCH_SAMD
+25 -19
View File
@@ -19,38 +19,44 @@
#include "MyInclusionMode.h" #include "MyInclusionMode.h"
// global variables
extern MyMessage _msgTmp;
unsigned long _inclusionStartTime; unsigned long _inclusionStartTime;
bool _inclusionMode; bool _inclusionMode;
inline void inclusionInit() { inline void inclusionInit()
{
_inclusionMode = false; _inclusionMode = false;
#if defined(MY_INCLUSION_BUTTON_FEATURE) #if defined(MY_INCLUSION_BUTTON_FEATURE)
// Setup digital in that triggers inclusion mode // Setup digital in that triggers inclusion mode
pinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT); hwPinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT);
digitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH); hwDigitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH);
#endif #endif
} }
void inclusionModeSet(bool newMode) { void inclusionModeSet(bool newMode)
if (newMode != _inclusionMode) { {
_inclusionMode = newMode; if (newMode != _inclusionMode) {
// Send back mode change to controller _inclusionMode = newMode;
gatewayTransportSend(buildGw(_msg, I_INCLUSION_MODE).set((uint8_t)(_inclusionMode?1:0))); // Send back mode change to controller
if (_inclusionMode) { gatewayTransportSend(buildGw(_msgTmp, I_INCLUSION_MODE).set((uint8_t)(_inclusionMode?1:0)));
_inclusionStartTime = hwMillis(); if (_inclusionMode) {
} _inclusionStartTime = hwMillis();
} }
}
} }
inline void inclusionProcess() { inline void inclusionProcess()
#ifdef MY_INCLUSION_BUTTON_FEATURE {
if (!_inclusionMode && digitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == MY_INCLUSION_BUTTON_PRESSED) { #ifdef MY_INCLUSION_BUTTON_FEATURE
if (!_inclusionMode && hwDigitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == MY_INCLUSION_BUTTON_PRESSED) {
// Start inclusion mode // Start inclusion mode
inclusionModeSet(true); inclusionModeSet(true);
} }
#endif #endif
if (_inclusionMode && hwMillis()-_inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) { if (_inclusionMode && hwMillis()-_inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode // inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
+19 -15
View File
@@ -18,25 +18,29 @@
*/ */
#include "MyIndication.h" #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" #include "MyLeds.h"
#endif #endif
void setIndication( const indication_t ind ) void setIndication( const indication_t ind )
{ {
#ifdef MY_LEDS_BLINKING_FEATURE #if defined(MY_DEFAULT_TX_LED_PIN)
if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind)) if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind)) {
{ ledsBlinkTx(1);
ledsBlinkTx(1); } else
} 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);
}
#endif #endif
if (indication) #if defined(MY_DEFAULT_RX_LED_PIN)
indication(ind); 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);
}
} }
+36 -33
View File
@@ -24,41 +24,44 @@
* Indication type * Indication type
*/ */
typedef enum { typedef enum {
INDICATION_TX = 0, //!< Sent a message. INDICATION_TX = 0, //!< Sent a message.
INDICATION_RX, //!< Received 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_ERR_START = 100, INDICATION_GW_TX, //!< Gateway transmit message.
INDICATION_ERR_TX, //!< Failed to 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_TRANSPORT_FAILURE, //!< Transport failure.
INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure. INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure.
INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node. INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node.
INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID. INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID.
INDICATION_ERR_SIGN, //!< Error signing. INDICATION_ERR_CHECK_UPLINK, //!< Failed to check uplink
INDICATION_ERR_VERSION, //!< Protocol version mismatch. INDICATION_ERR_SIGN, //!< Error signing.
INDICATION_ERR_NET_FULL, //!< Network full. All node ID's are taken. INDICATION_ERR_LENGTH, //!< Invalid message length.
INDICATION_ERR_INIT_GWTRANSPORT, //!< Gateway transport hardware init failure. INDICATION_ERR_VERSION, //!< Protocol version mismatch.
INDICATION_ERR_LOCKED, //!< Node is locked. INDICATION_ERR_NET_FULL, //!< Network full. All node ID's are taken.
INDICATION_ERR_FW_FLASH_INIT, //!< Firmware update flash initialisation failure. INDICATION_ERR_INIT_GWTRANSPORT, //!< Gateway transport hardware init failure.
INDICATION_ERR_FW_TIMEOUT, //!< Firmware update timeout. INDICATION_ERR_LOCKED, //!< Node is locked.
INDICATION_ERR_FW_CHECKSUM, //!< Firmware update checksum mismatch. INDICATION_ERR_FW_FLASH_INIT, //!< Firmware update flash initialisation failure.
INDICATION_ERR_END INDICATION_ERR_FW_TIMEOUT, //!< Firmware update timeout.
INDICATION_ERR_FW_CHECKSUM, //!< Firmware update checksum mismatch.
INDICATION_ERR_END
} indication_t; } indication_t;
/** /**
+64 -29
View File
@@ -26,8 +26,7 @@
static uint8_t countRx; static uint8_t countRx;
static uint8_t countTx; static uint8_t countTx;
static uint8_t countErr; 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() inline void ledsInit()
{ {
@@ -37,46 +36,82 @@ inline void ledsInit()
countErr = 0; countErr = 0;
// Setup led pins // Setup led pins
pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT); #if defined(MY_DEFAULT_RX_LED_PIN)
pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT); hwPinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT); #endif
#if defined(MY_DEFAULT_TX_LED_PIN)
ledsProcess(); 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... // Just return if it is not the time...
if ((hwMillis() - prevTime) < LED_PROCESS_INTERVAL_MS) if ((hwMillis() - prevTime) < LED_PROCESS_INTERVAL_MS) {
return; return;
}
prevTime = hwMillis();
prevTime += LED_PROCESS_INTERVAL_MS; uint8_t state;
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
// For an On/Off ratio of 4, the pattern repeated will be [on, on, on, off] #if defined(MY_DEFAULT_TX_LED_PIN)
// until the counter becomes 0. if (countTx) {
state = (countRx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF; --countTx;
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);
#endif
state = (countTx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF; #if defined(MY_DEFAULT_ERR_LED_PIN)
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, state); if (countErr) {
if (countTx) --countTx; --countErr;
}
state = (countErr & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF; state = (countErr & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, state); hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, state);
if (countErr) --countErr; #endif
} }
void ledsBlinkRx(uint8_t cnt) { void ledsBlinkRx(uint8_t cnt)
if (!countRx) { countRx = cnt*LED_ON_OFF_RATIO; } {
if (!countRx) {
countRx = cnt*LED_ON_OFF_RATIO;
}
ledsProcess();
} }
void ledsBlinkTx(uint8_t cnt) { void ledsBlinkTx(uint8_t cnt)
if(!countTx) { countTx = cnt*LED_ON_OFF_RATIO; } {
if(!countTx) {
countTx = cnt*LED_ON_OFF_RATIO;
}
ledsProcess();
} }
void ledsBlinkErr(uint8_t cnt) { void ledsBlinkErr(uint8_t cnt)
if(!countErr) { countErr = cnt*LED_ON_OFF_RATIO; } {
if(!countErr) {
countErr = cnt*LED_ON_OFF_RATIO;
}
ledsProcess();
} }
bool ledsBlinking()
{
return countRx || countTx || countErr;
}
+22 -17
View File
@@ -29,26 +29,31 @@
#define LED_OFF 0x1 #define LED_OFF 0x1
#endif #endif
#ifdef MY_LEDS_BLINKING_FEATURE #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 ledBlinkTx(x,...) ledsBlinkTx(x)
#define ledBlinkRx(x,...) ledsBlinkRx(x) #define ledBlinkRx(x,...) ledsBlinkRx(x)
#define ledBlinkErr(x,...) ledsBlinkErr(x) #define ledBlinkErr(x,...) ledsBlinkErr(x)
/** /**
* Blink with LEDs * Blink with LEDs
* @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms * @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms
*/ */
void ledsInit(); void ledsInit();
void ledsBlinkRx(uint8_t cnt); void ledsBlinkRx(uint8_t cnt);
void ledsBlinkTx(uint8_t cnt); void ledsBlinkTx(uint8_t cnt);
void ledsBlinkErr(uint8_t cnt); void ledsBlinkErr(uint8_t cnt);
void ledsProcess(); // do the actual blinking 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 #else
// Remove led functions if feature is disabled // Remove led functions if feature is disabled
#define ledBlinkTx(x,...) #define ledBlinkTx(x,...)
#define ledBlinkRx(x,...) #define ledBlinkRx(x,...)
#define ledBlinkErr(x,...) #define ledBlinkErr(x,...)
#endif #endif
#endif #endif
+36 -9
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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 // Initialize library and handle sketch functions like we want to
int main(void) { extern "C" void __libc_init_array(void);
int main(void)
{
init(); init();
#if defined(USBCON) #if defined(USBCON)
#if defined(ARDUINO_ARCH_SAMD) #if defined(ARDUINO_ARCH_SAMD)
USBDevice.init(); __libc_init_array();
#endif USBDevice.init();
USBDevice.attach(); #endif
#endif USBDevice.attach();
#endif
_begin(); // Startup MySensors library _begin(); // Startup MySensors library
for(;;) { for(;;) {
_process(); // Process incoming data _process(); // Process incoming data
if (loop) loop(); // Call sketch loop if (loop) {
if (serialEventRun) serialEventRun(); loop(); // Call sketch loop
}
if (serialEventRun) {
serialEventRun();
}
} }
return 0; return 0;
} }
+53 -23
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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: //This may be used to change user task stack size:
//#define CONT_STACKSIZE 4096 //#define CONT_STACKSIZE 4096
@@ -20,27 +38,31 @@ extern "C" {
struct rst_info resetInfo; struct rst_info resetInfo;
extern "C" { 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 = const char* core_release =
#ifdef ARDUINO_ESP8266_RELEASE #ifdef ARDUINO_ESP8266_RELEASE
ARDUINO_ESP8266_RELEASE; ARDUINO_ESP8266_RELEASE;
#else #else
NULL; NULL;
#endif #endif
} // extern "C" } // extern "C"
int atexit(void(*func)()) { int atexit(void(*func)())
{
(void)func; (void)func;
return 0; return 0;
} }
extern "C" void ets_update_cpu_frequency(int freqmhz); extern "C" void ets_update_cpu_frequency(int freqmhz);
void initVariant() __attribute__((weak)); void initVariant() __attribute__((weak));
void initVariant() { void initVariant()
{
} }
void preloop_update_frequency() __attribute__((weak)); void preloop_update_frequency() __attribute__((weak));
void preloop_update_frequency() { void preloop_update_frequency()
{
#if defined(F_CPU) && (F_CPU == 160000000L) #if defined(F_CPU) && (F_CPU == 160000000L)
REG_SET_BIT(0x3ff00014, BIT(0)); REG_SET_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(160); 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; static uint32_t g_micros_at_task_start;
extern "C" void esp_yield() { extern "C" void esp_yield()
{
if (cont_can_yield(&g_cont)) { if (cont_can_yield(&g_cont)) {
cont_yield(&g_cont); cont_yield(&g_cont);
} }
} }
extern "C" void esp_schedule() { extern "C" void esp_schedule()
{
ets_post(LOOP_TASK_PRIORITY, 0, 0); ets_post(LOOP_TASK_PRIORITY, 0, 0);
} }
extern "C" void __yield() { extern "C" void __yield()
{
if (cont_can_yield(&g_cont)) { if (cont_can_yield(&g_cont)) {
esp_schedule(); esp_schedule();
esp_yield(); esp_yield();
} } else {
else {
panic(); panic();
} }
} }
extern "C" void yield(void) __attribute__((weak, alias("__yield"))); 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) && 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(); yield();
} }
} }
static void loop_wrapper() { static void loop_wrapper()
{
static bool setup_done = false; static bool setup_done = false;
preloop_update_frequency(); preloop_update_frequency();
if (!setup_done) { if (!setup_done) {
@@ -98,7 +123,8 @@ static void loop_wrapper() {
esp_schedule(); esp_schedule();
} }
static void loop_task(os_event_t *events) { static void loop_task(os_event_t *events)
{
(void)events; (void)events;
g_micros_at_task_start = system_get_time(); g_micros_at_task_start = system_get_time();
cont_run(&g_cont, &loop_wrapper); 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; void(**p)(void) = &__init_array_end;
while (p != &__init_array_start) while (p != &__init_array_start) {
(*--p)(); (*--p)();
}
} }
extern "C" void __gdb_init() {} 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() {}
extern "C" void gdb_do_break(void) __attribute__((weak, alias("__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); system_set_os_print(1);
gdb_init(); gdb_init();
do_global_ctors(); 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(); struct rst_info *rtc_info_ptr = system_get_rst_info();
memcpy((void *)&resetInfo, (void *)rtc_info_ptr, sizeof(resetInfo)); memcpy((void *)&resetInfo, (void *)rtc_info_ptr, sizeof(resetInfo));
@@ -140,8 +170,8 @@ extern "C" void user_init(void) {
cont_init(&g_cont); cont_init(&g_cont);
ets_task(loop_task, ets_task(loop_task,
LOOP_TASK_PRIORITY, g_loop_queue, LOOP_TASK_PRIORITY, g_loop_queue,
LOOP_QUEUE_SIZE); LOOP_QUEUE_SIZE);
system_init_done_cb(&init_done); system_init_done_cb(&init_done);
} }
+450
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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 <stdio.h>
#include <csignal>
#include <cstdlib>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <getopt.h>
#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_cast<void*>EEPROM_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_cast<void*>EEPROM_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_cast<void*>EEPROM_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_cast<void*>EEPROM_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_cast<void*>EEPROM_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_cast<void*>EEPROM_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;
}
+89 -44
View File
@@ -21,32 +21,53 @@
#include "MyMessage.h" #include "MyMessage.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
MyMessage::MyMessage()
MyMessage::MyMessage() { {
destination = 0; // Gateway is default destination clear();
} }
MyMessage::MyMessage(uint8_t _sensor, uint8_t _type) { MyMessage::MyMessage(uint8_t _sensor, uint8_t _type)
destination = 0; // Gateway is default destination {
clear();
sensor = _sensor; sensor = _sensor;
type = _type; type = _type;
} }
bool MyMessage::isAck() const { 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
{
return miGetAck(); return miGetAck();
} }
uint8_t MyMessage::getCommand() const { uint8_t MyMessage::getCommand() const
{
return miGetCommand(); return miGetCommand();
} }
/* Getters for payload converted to desired form */ /* Getters for payload converted to desired form */
void* MyMessage::getCustom() const { void* MyMessage::getCustom() const
{
return (void *)data; return (void *)data;
} }
const char* MyMessage::getString() const { const char* MyMessage::getString() const
{
uint8_t payloadType = miGetPayloadType(); uint8_t payloadType = miGetPayloadType();
if (payloadType == P_STRING) { if (payloadType == P_STRING) {
return data; return data;
@@ -56,17 +77,19 @@ const char* MyMessage::getString() const {
} }
// handles single character hex (0 - 15) // 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; uint8_t k = i & 0x0F;
if (k <= 9) if (k <= 9) {
return '0' + k; return '0' + k;
else } else {
return 'A' + k - 10; return 'A' + k - 10;
}
} }
char* MyMessage::getCustomString(char *buffer) const { char* MyMessage::getCustomString(char *buffer) const
for (uint8_t i = 0; i < miGetLength(); i++) {
{ for (uint8_t i = 0; i < miGetLength(); i++) {
buffer[i * 2] = i2h(data[i] >> 4); buffer[i * 2] = i2h(data[i] >> 4);
buffer[(i * 2) + 1] = i2h(data[i]); buffer[(i * 2) + 1] = i2h(data[i]);
} }
@@ -74,7 +97,8 @@ char* MyMessage::getCustomString(char *buffer) const {
return buffer; return buffer;
} }
char* MyMessage::getStream(char *buffer) const { char* MyMessage::getStream(char *buffer) const
{
uint8_t cmd = miGetCommand(); uint8_t cmd = miGetCommand();
if ((cmd == C_STREAM) && (buffer != NULL)) { if ((cmd == C_STREAM) && (buffer != NULL)) {
return getCustomString(buffer); 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(); uint8_t payloadType = miGetPayloadType();
if (buffer != NULL) { if (buffer != NULL) {
if (payloadType == P_STRING) { if (payloadType == P_STRING) {
@@ -100,7 +125,7 @@ char* MyMessage::getString(char *buffer) const {
} else if (payloadType == P_ULONG32) { } else if (payloadType == P_ULONG32) {
ultoa(ulValue, buffer, 10); ultoa(ulValue, buffer, 10);
} else if (payloadType == P_FLOAT32) { } 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) { } else if (payloadType == P_CUSTOM) {
return getCustomString(buffer); return getCustomString(buffer);
} }
@@ -110,11 +135,13 @@ char* MyMessage::getString(char *buffer) const {
} }
} }
bool MyMessage::getBool() const { bool MyMessage::getBool() const
{
return getByte(); return getByte();
} }
uint8_t MyMessage::getByte() const { uint8_t MyMessage::getByte() const
{
if (miGetPayloadType() == P_BYTE) { if (miGetPayloadType() == P_BYTE) {
return data[0]; return data[0];
} else if (miGetPayloadType() == P_STRING) { } 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) { if (miGetPayloadType() == P_FLOAT32) {
return fValue; return fValue;
} else if (miGetPayloadType() == P_STRING) { } 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) { if (miGetPayloadType() == P_LONG32) {
return lValue; return lValue;
} else if (miGetPayloadType() == P_STRING) { } 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) { if (miGetPayloadType() == P_ULONG32) {
return ulValue; return ulValue;
} else if (miGetPayloadType() == P_STRING) { } else if (miGetPayloadType() == P_STRING) {
@@ -155,8 +185,9 @@ uint32_t MyMessage::getULong() const {
} }
} }
int16_t MyMessage::getInt() const { int16_t MyMessage::getInt() const
if (miGetPayloadType() == P_INT16) { {
if (miGetPayloadType() == P_INT16) {
return iValue; return iValue;
} else if (miGetPayloadType() == P_STRING) { } else if (miGetPayloadType() == P_STRING) {
return atoi(data); return atoi(data);
@@ -165,8 +196,9 @@ int16_t MyMessage::getInt() const {
} }
} }
uint16_t MyMessage::getUInt() const { uint16_t MyMessage::getUInt() const
if (miGetPayloadType() == P_UINT16) { {
if (miGetPayloadType() == P_UINT16) {
return uiValue; return uiValue;
} else if (miGetPayloadType() == P_STRING) { } else if (miGetPayloadType() == P_STRING) {
return atoi(data); 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; type = _type;
return *this; return *this;
} }
MyMessage& MyMessage::setSensor(uint8_t _sensor) { MyMessage& MyMessage::setSensor(uint8_t _sensor)
{
sensor = _sensor; sensor = _sensor;
return *this; return *this;
} }
MyMessage& MyMessage::setDestination(uint8_t _destination) { MyMessage& MyMessage::setDestination(uint8_t _destination)
{
destination = _destination; destination = _destination;
return *this; return *this;
} }
// Set payload // 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); miSetPayloadType(P_CUSTOM);
miSetLength(length); memcpy(data, value, payloadLength);
memcpy(data, value, min(length, MAX_PAYLOAD));
return *this; return *this;
} }
MyMessage& MyMessage::set(const char* value) { MyMessage& MyMessage::set(const char* value)
uint8_t length = value == NULL ? 0 : min(strlen(value), MAX_PAYLOAD); {
uint8_t length = value == NULL ? 0 : min(strlen(value), (size_t)MAX_PAYLOAD);
miSetLength(length); miSetLength(length);
miSetPayloadType(P_STRING); miSetPayloadType(P_STRING);
if (length) { if (length) {
strncpy(data, value, length); strncpy(data, value, length);
} }
// null terminate string // null terminate string
@@ -211,21 +249,24 @@ MyMessage& MyMessage::set(const char* value) {
return *this; return *this;
} }
MyMessage& MyMessage::set(bool value) { MyMessage& MyMessage::set(bool value)
{
miSetLength(1); miSetLength(1);
miSetPayloadType(P_BYTE); miSetPayloadType(P_BYTE);
data[0] = value; data[0] = value;
return *this; return *this;
} }
MyMessage& MyMessage::set(uint8_t value) { MyMessage& MyMessage::set(uint8_t value)
{
miSetLength(1); miSetLength(1);
miSetPayloadType(P_BYTE); miSetPayloadType(P_BYTE);
data[0] = value; data[0] = value;
return *this; return *this;
} }
MyMessage& MyMessage::set(float value, uint8_t decimals) { MyMessage& MyMessage::set(float value, uint8_t decimals)
{
miSetLength(5); // 32 bit float + persi miSetLength(5); // 32 bit float + persi
miSetPayloadType(P_FLOAT32); miSetPayloadType(P_FLOAT32);
fValue=value; fValue=value;
@@ -233,28 +274,32 @@ MyMessage& MyMessage::set(float value, uint8_t decimals) {
return *this; return *this;
} }
MyMessage& MyMessage::set(uint32_t value) { MyMessage& MyMessage::set(uint32_t value)
{
miSetPayloadType(P_ULONG32); miSetPayloadType(P_ULONG32);
miSetLength(4); miSetLength(4);
ulValue = value; ulValue = value;
return *this; return *this;
} }
MyMessage& MyMessage::set(int32_t value) { MyMessage& MyMessage::set(int32_t value)
{
miSetPayloadType(P_LONG32); miSetPayloadType(P_LONG32);
miSetLength(4); miSetLength(4);
lValue = value; lValue = value;
return *this; return *this;
} }
MyMessage& MyMessage::set(uint16_t value) { MyMessage& MyMessage::set(uint16_t value)
{
miSetPayloadType(P_UINT16); miSetPayloadType(P_UINT16);
miSetLength(2); miSetLength(2);
uiValue = value; uiValue = value;
return *this; return *this;
} }
MyMessage& MyMessage::set(int16_t value) { MyMessage& MyMessage::set(int16_t value)
{
miSetPayloadType(P_INT16); miSetPayloadType(P_INT16);
miSetLength(2); miSetLength(2);
iValue = value; iValue = value;
+61 -55
View File
@@ -32,13 +32,12 @@
#ifdef __cplusplus #ifdef __cplusplus
#include <Arduino.h> #include <Arduino.h>
#include <string.h>
#include <stdint.h> #include <stdint.h>
#endif #endif
#define PROTOCOL_VERSION 2 //!< The version of the protocol #define PROTOCOL_VERSION (2u) //!< The version of the protocol
#define MAX_MESSAGE_LENGTH 32 //!< The maximum size of a message (including header) #define MAX_MESSAGE_LENGTH (32u) //!< The maximum size of a message (including header)
#define HEADER_SIZE 7 //!< The size of the 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 #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 /// @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) /// @brief Type of sensor (used when presenting sensors)
typedef enum { typedef enum {
S_DOOR = 0, //!< Door sensor, V_TRIPPED, V_ARMED 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_SMOKE = 2, //!< Smoke sensor, V_TRIPPED, V_ARMED
S_BINARY = 3, //!< Binary light or relay, V_STATUS, V_WATT S_BINARY = 3, //!< Binary light or relay, V_STATUS, V_WATT
S_LIGHT = 3, //!< \deprecated Same as S_BINARY, **** DEPRECATED, DO NOT USE **** 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_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_TEMP = 6, //!< Temperature sensor, V_TEMP
S_HUM = 7, //!< Humidity sensor, V_HUM S_HUM = 7, //!< Humidity sensor, V_HUM
@@ -71,38 +70,38 @@ typedef enum {
S_DISTANCE = 15, //!< Distance sensor, V_DISTANCE 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_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_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_LOCK = 19, //!< Lock device, V_LOCK_STATUS
S_IR = 20, //!< IR device, V_IR_SEND, V_IR_RECEIVE S_IR = 20, //!< IR device, V_IR_SEND, V_IR_RECEIVE
S_WATER = 21, //!< Water meter, V_FLOW, V_VOLUME S_WATER = 21, //!< Water meter, V_FLOW, V_VOLUME
S_AIR_QUALITY = 22, //!< Air quality sensor, V_LEVEL 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_DUST = 24, //!< Dust sensor, V_LEVEL
S_SCENE_CONTROLLER = 25, //!< Scene controller device, V_SCENE_ON, V_SCENE_OFF. 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_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_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_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_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_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_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_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_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_INFO = 36, //!< LCD text device / Simple information device on controller, V_TEXT
S_GAS = 37, //!< Gas meter, V_FLOW, V_VOLUME S_GAS = 37, //!< Gas meter, V_FLOW, V_VOLUME
S_GPS = 38, //!< GPS Sensor, V_POSITION 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; } mysensor_sensor;
/// @brief Type of sensor data (for set/req/ack messages) /// @brief Type of sensor data (for set/req/ack messages)
typedef enum { typedef enum {
V_TEMP = 0, //!< S_TEMP. Temperature S_TEMP, S_HEATER, S_HVAC V_TEMP = 0, //!< S_TEMP. Temperature S_TEMP, S_HEATER, S_HVAC
V_HUM = 1, //!< S_HUM. Humidity 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_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_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_DIMMER = 3, //!< \deprecated Same as V_PERCENTAGE, **** DEPRECATED, DO NOT USE ****
V_PRESSURE = 4, //!< S_BARO. Atmospheric Pressure V_PRESSURE = 4, //!< S_BARO. Atmospheric Pressure
V_FORECAST = 5, //!< S_BARO. Whether forecast. string of "stable", "sunny", "cloudy", "unstable", "thunderstorm" or "unknown" V_FORECAST = 5, //!< S_BARO. Whether forecast. string of "stable", "sunny", "cloudy", "unstable", "thunderstorm" or "unknown"
V_RAIN = 6, //!< S_RAIN. Amount of rain 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_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_ON = 19, //!< S_SCENE_CONTROLLER. Turn on a scene
V_SCENE_OFF = 20, //!< S_SCENE_CONTROLLER. Turn of 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_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_LIGHT_LEVEL = 23, //!< S_LIGHT_LEVEL. Uncalibrated light level. 0-100%. Use V_LEVEL for light level in lux
V_VAR1 = 24, //!< VAR1 V_VAR1 = 24, //!< VAR1
V_VAR2 = 25, //!< VAR2 V_VAR2 = 25, //!< VAR2
V_VAR3 = 26, //!< VAR3 V_VAR3 = 26, //!< VAR3
V_VAR4 = 27, //!< VAR4 V_VAR4 = 27, //!< VAR4
V_VAR5 = 28, //!< VAR5 V_VAR5 = 28, //!< VAR5
@@ -138,11 +137,11 @@ typedef enum {
V_VOLUME = 35, //!< S_WATER. Water volume V_VOLUME = 35, //!< S_WATER. Water volume
V_LOCK_STATUS = 36, //!< S_LOCK. Set or get lock status. 1=Locked, 0=Unlocked 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_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_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_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_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_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_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) 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_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_IR_RECORD = 50, //!< Record IR codes S_IR for playback
V_PH = 51, //!< S_WATER_QUALITY, water PH V_PH = 51, //!< S_WATER_QUALITY, water PH
V_ORP = 52, //!< S_WATER_QUALITY, water ORP : redox potential in mV 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_EC = 53, //!< S_WATER_QUALITY, water electric conductivity μS/cm (microSiemens/cm)
V_VAR = 54, //!< S_POWER, Reactive power: volt-ampere reactive (var) V_VAR = 54, //!< S_POWER, Reactive power: volt-ampere reactive (var)
V_VA = 55, //!< S_POWER, Apparent power: volt-ampere (VA) 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_POWER_FACTOR = 56, //!< S_POWER, Ratio of real power to apparent power: floating point value in the range [-1,..,1]
} mysensor_data; } mysensor_data;
/// @brief Type of internal messages (for internal messages) /// @brief Type of internal messages (for internal messages)
typedef enum { typedef enum {
I_BATTERY_LEVEL = 0, //!< Battery level I_BATTERY_LEVEL = 0, //!< Battery level
I_TIME = 1, //!< Time I_TIME = 1, //!< Time (request/response)
I_VERSION = 2, //!< Version I_VERSION = 2, //!< Version
I_ID_REQUEST = 3, //!< ID request I_ID_REQUEST = 3, //!< ID request
I_ID_RESPONSE = 4, //!< ID response I_ID_RESPONSE = 4, //!< ID response
I_INCLUSION_MODE = 5, //!< Inclusion mode I_INCLUSION_MODE = 5, //!< Inclusion mode
I_CONFIG = 6, //!< Config I_CONFIG = 6, //!< Config (request/response)
I_FIND_PARENT = 7, //!< Find parent I_FIND_PARENT_REQUEST = 7, //!< Find parent
I_FIND_PARENT_RESPONSE = 8, //!< Find parent response I_FIND_PARENT_RESPONSE = 8, //!< Find parent response
I_LOG_MESSAGE = 9, //!< Log message I_LOG_MESSAGE = 9, //!< Log message
I_CHILDREN = 10, //!< Children I_CHILDREN = 10, //!< Children
@@ -180,9 +179,9 @@ typedef enum {
I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version) I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version)
I_NONCE_REQUEST = 16, //!< Request for a nonce I_NONCE_REQUEST = 16, //!< Request for a nonce
I_NONCE_RESPONSE = 17, //!< Payload is nonce data 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_PRESENTATION = 19, //!< Presentation message
I_DISCOVER = 20, //!< Discover request I_DISCOVER_REQUEST = 20, //!< Discover request
I_DISCOVER_RESPONSE = 21, //!< Discover response I_DISCOVER_RESPONSE = 21, //!< Discover response
I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response
I_LOCKED = 23, //!< Node is locked (reason in string-payload) 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' #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 // Getters/setters for special bit fields in header
#define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 2) //!< Set version field #define mSetVersion(_message,_version) BF_SET(_message.version_length, _version, 0, 2) //!< Set version field
#define mGetVersion(_msg) ((uint8_t)BF_GET(_msg.version_length, 0, 2)) //!< Get 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 mSetSigned(_message,_signed) BF_SET(_message.version_length, _signed, 2, 1) //!< Set signed field
#define mGetSigned(_msg) ((bool)BF_GET(_msg.version_length, 2, 1)) //!< Get versignedsion 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 mSetLength(_message,_length) BF_SET(_message.version_length, _length, 3, 5) //!< Set length field
#define mGetLength(_msg) ((uint8_t)BF_GET(_msg.version_length, 3, 5)) //!< Get 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 mSetCommand(_message,_command) BF_SET(_message.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 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 mSetRequestAck(_message,_rack) BF_SET(_message.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 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 mSetAck(_message,_ackMsg) BF_SET(_message.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 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 mSetPayloadType(_message, _pt) BF_SET(_message.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 mGetPayloadType(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 5, 3)) //!< Get payload type field
// internal access for special fields // 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 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 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 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 #define miGetRequestAck() ((bool)BF_GET(command_ack_payload, 3, 1)) //!< Internal getter for ack-request field
@@ -282,6 +284,11 @@ public:
char i2h(uint8_t i) const; 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 * 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 * into string representation by supplying a buffer with the minimum size of
@@ -324,8 +331,7 @@ public:
#else #else
typedef union { typedef union {
struct struct {
{
#endif #endif
uint8_t last; // 8 bit - Id of last node this message passed 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 destination; // 8 bit - Id of destination node
uint8_t version_length; // 2 bit - Protocol version uint8_t version_length; // 2 bit - Protocol version
// 1 bit - Signed flag // 1 bit - Signed flag
// 5 bit - Length of payload // 5 bit - Length of payload
uint8_t command_ack_payload; // 3 bit - Command type uint8_t command_ack_payload; // 3 bit - Command type
// 1 bit - Request an ack - Indicator that receiver should send an ack back. // 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. // 1 bit - Is ack messsage - Indicator that this is the actual ack message.
// 3 bit - Payload data type // 3 bit - Payload data type
uint8_t type; // 8 bit - Type varies depending on command uint8_t type; // 8 bit - Type varies depending on command
uint8_t sensor; // 8 bit - Id of sensor that this message concerns. uint8_t sensor; // 8 bit - Id of sensor that this message concerns.
@@ -357,7 +363,7 @@ struct
}; };
struct { // Presentation messages struct { // Presentation messages
uint8_t version; // Library version 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]; char data[MAX_PAYLOAD + 1];
} __attribute__((packed)); } __attribute__((packed));
@@ -365,7 +371,7 @@ struct
} __attribute__((packed)); } __attribute__((packed));
#else #else
}; };
uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1]; uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1];
} __attribute__((packed)) MyMessage; } __attribute__((packed)) MyMessage;
#endif #endif
#endif #endif
+103 -77
View File
@@ -16,134 +16,160 @@
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation. * version 2 as published by the Free Software Foundation.
*/ */
#include "MyOTAFirmwareUpdate.h" #include "MyOTAFirmwareUpdate.h"
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); // global variables
NodeFirmwareConfig _fc; extern MyMessage _msg;
bool _fwUpdateOngoing; extern MyMessage _msgTmp;
unsigned long _fwLastRequestTime;
uint16_t _fwBlock;
uint8_t _fwRetry;
inline void readFirmwareSettings() { // local variables
hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
nodeFirmwareConfig_t _nodeFirmwareConfig;
bool _firmwareUpdateOngoing;
uint32_t _firmwareLastRequest;
uint16_t _firmwareBlock;
uint8_t _firmwareRetry;
void readFirmwareSettings(void)
{
hwReadConfigBlock((void*)&_nodeFirmwareConfig, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,
sizeof(nodeFirmwareConfig_t));
} }
inline void firmwareOTAUpdateRequest() { void firmwareOTAUpdateRequest(void)
unsigned long enter = hwMillis(); {
if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) { const uint32_t enterMS = hwMillis();
if (!_fwRetry) { if (_firmwareUpdateOngoing && (enterMS - _firmwareLastRequest > MY_OTA_RETRY_DELAY)) {
setIndication(INDICATION_ERR_FW_TIMEOUT); if (!_firmwareRetry) {
debug(PSTR("fw upd fail\n")); 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. // Give up. We have requested MY_OTA_RETRY times without any packet in return.
_fwUpdateOngoing = false; _firmwareUpdateOngoing = false;
return; return;
} }
_fwRetry--; _firmwareRetry--;
_fwLastRequestTime = enter; _firmwareLastRequest = enterMS;
// Time to (re-)request firmware block from controller // Time to (re-)request firmware block from controller
RequestFWBlock firmwareRequest; requestFirmwareBlock_t firmwareRequest;
firmwareRequest.type = _fc.type; firmwareRequest.type = _nodeFirmwareConfig.type;
firmwareRequest.version = _fc.version; firmwareRequest.version = _nodeFirmwareConfig.version;
firmwareRequest.block = (_fwBlock - 1); firmwareRequest.block = (_firmwareBlock - 1);
debug(PSTR("req FW: T=%02X, V=%02X, B=%04X\n"),_fc.type,_fc.version,_fwBlock - 1); OTA_DEBUG(PSTR("OTA:FRQ:FW REQ,T=%04X,V=%04X,B=%04X\n"), _nodeFirmwareConfig.type,
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false).set(&firmwareRequest,sizeof(RequestFWBlock))); _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) { if (_msg.type == ST_FIRMWARE_CONFIG_RESPONSE) {
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data; nodeFirmwareConfig_t *firmwareConfigResponse = (nodeFirmwareConfig_t *)_msg.data;
// compare with current node configuration, if they differ, start fw fetch process // compare with current node configuration, if they differ, start FW fetch process
if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { if (memcmp(&_nodeFirmwareConfig, firmwareConfigResponse, sizeof(nodeFirmwareConfig_t))) {
setIndication(INDICATION_FW_UPDATE_START); setIndication(INDICATION_FW_UPDATE_START);
debug(PSTR("fw update\n")); OTA_DEBUG(PSTR("OTA:FWP:UPDATE\n")); // FW update initiated
// copy new FW config // copy new FW config
memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); (void)memcpy(&_nodeFirmwareConfig, firmwareConfigResponse, sizeof(nodeFirmwareConfig_t));
// Init flash // Init flash
if (!_flash.initialize()) { if (!_flash.initialize()) {
setIndication(INDICATION_ERR_FW_FLASH_INIT); setIndication(INDICATION_ERR_FW_FLASH_INIT);
debug(PSTR("flash init fail\n")); OTA_DEBUG(PSTR("!OTA:FWP:FLASH INIT FAIL\n")); // failed to initialise flash
_fwUpdateOngoing = false; _firmwareUpdateOngoing = false;
} else { } else {
// erase lower 32K -> max flash size for ATMEGA328 // erase lower 32K -> max flash size for ATMEGA328
_flash.blockErase32K(0); _flash.blockErase32K(0);
// wait until flash erased // wait until flash erased
while ( _flash.busy() ); while ( _flash.busy() ) {}
_fwBlock = _fc.blocks; _firmwareBlock = _nodeFirmwareConfig.blocks;
_fwUpdateOngoing = true; _firmwareUpdateOngoing = true;
// reset flags // reset flags
_fwRetry = MY_OTA_RETRY+1; _firmwareRetry = MY_OTA_RETRY + 1;
_fwLastRequestTime = 0; _firmwareLastRequest = 0;
} }
return true; 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) { } else if (_msg.type == ST_FIRMWARE_RESPONSE) {
if (_fwUpdateOngoing) { if (_firmwareUpdateOngoing) {
// Save block to flash // Save block to flash
setIndication(INDICATION_FW_UPDATE_RX); setIndication(INDICATION_FW_UPDATE_RX);
debug(PSTR("fw block %d\n"), _fwBlock); OTA_DEBUG(PSTR("OTA:FWP:RECV B=%04X\n"), _firmwareBlock); // received FW block
// extract FW block // extract FW block
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data; replyFirmwareBlock_t *firmwareResponse = (replyFirmwareBlock_t *)_msg.data;
// write to flash // 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 // wait until flash written
while ( _flash.busy() ); while (_flash.busy()) {}
_fwBlock--; _firmwareBlock--;
if (!_fwBlock) { if (!_firmwareBlock) {
// We're finished! Do a checksum and reboot. // 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()) { if (transportIsValidFirmware()) {
debug(PSTR("fw checksum ok\n")); OTA_DEBUG(PSTR("OTA:FWP:CRC OK\n")); // FW checksum ok
// 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);
// Write the new firmware config to eeprom // 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(); hwReboot();
} else { } else {
setIndication(INDICATION_ERR_FW_CHECKSUM); setIndication(INDICATION_ERR_FW_CHECKSUM);
debug(PSTR("fw checksum fail\n")); OTA_DEBUG(PSTR("!OTA:FWP:CRC FAIL\n"));
} }
} }
// reset flags // reset flags
_fwRetry = MY_OTA_RETRY+1; _firmwareRetry = MY_OTA_RETRY + 1;
_fwLastRequestTime = 0; _firmwareLastRequest = 0;
} else { } else {
debug(PSTR("No fw update ongoing\n")); OTA_DEBUG(PSTR("!OTA:FWP:NO UPDATE\n"));
} }
return true; return true;
} }
return false; return false;
} }
inline void presentBootloaderInformation(){ void presentBootloaderInformation(void)
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msgTmp.data; {
mSetLength(_msgTmp, sizeof(RequestFirmwareConfig)); requestFirmwareConfig_t *requestFirmwareConfig = (requestFirmwareConfig_t *)_msgTmp.data;
mSetLength(_msgTmp, sizeof(requestFirmwareConfig_t));
mSetCommand(_msgTmp, C_STREAM); mSetCommand(_msgTmp, C_STREAM);
mSetPayloadType(_msgTmp,P_CUSTOM); mSetPayloadType(_msgTmp, P_CUSTOM);
// copy node settings to reqFWConfig // copy node settings to reqFWConfig
memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig)); (void)memcpy(requestFirmwareConfig, &_nodeFirmwareConfig, sizeof(nodeFirmwareConfig_t));
// add bootloader information // add bootloader information
reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; requestFirmwareConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
_fwUpdateOngoing = false; _firmwareUpdateOngoing = false;
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, 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 // do a crc16 on the whole received firmware
inline bool transportIsValidFirmware() { bool transportIsValidFirmware(void)
{
// init crc // init crc
uint16_t crc = ~0; 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); crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET);
for (int8_t j = 0; j < 8; ++j) { for (int8_t j = 0; j < 8; ++j) {
if (crc & 1) if (crc & 1) {
crc = (crc >> 1) ^ 0xA001; crc = (crc >> 1) ^ 0xA001;
else } else {
crc = (crc >> 1); 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;
} }
+89 -45
View File
@@ -17,58 +17,100 @@
* version 2 as published by the Free Software Foundation. * 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:
* - <b>OTA</b> messages emitted by MyOTAFirmwareUpdate
* - SUB SYSTEMS:
* - OTA:<b>FRQ</b> from @ref firmwareOTAUpdateRequest()
* - OTA:<b>FWP</b> 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 #ifndef MyOTAFirmwareUpdate_h
#define MyOTAFirmwareUpdate_h #define MyOTAFirmwareUpdate_h
#include "MySensorsCore.h" #include "MySensorsCore.h"
// Size of each firmware block #define FIRMWARE_BLOCK_SIZE (16u) //!< Size of each firmware block
#define FIRMWARE_BLOCK_SIZE 16 #define FIRMWARE_MAX_REQUESTS (5u) //!< Number of times a firmware block should be requested before giving up
// 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 FIRMWARE_MAX_REQUESTS 5 #define MY_OTA_RETRY_DELAY (500u) //!< Number of milliseconds before re-requesting a FW block
// Number of times to request a fw block before giving up #define FIRMWARE_START_OFFSET (10u) //!< Start offset for firmware in flash (DualOptiboot wants to keeps a signature first)
#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 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 { typedef struct {
uint16_t type; //!< Type of config uint16_t type; //!< Type of config
uint16_t version; //!< Version of config uint16_t version; //!< Version of config
uint16_t blocks; //!< Number of blocks uint16_t blocks; //!< Number of blocks
uint16_t crc; //!< CRC of block data uint16_t crc; //!< CRC of block data
} __attribute__((packed)) NodeFirmwareConfig; } __attribute__((packed)) nodeFirmwareConfig_t;
/// @brief FW config request structure /**
* @brief FW config request structure
*/
typedef struct { typedef struct {
uint16_t type; //!< Type of config uint16_t type; //!< Type of config
uint16_t version; //!< Version of config uint16_t version; //!< Version of config
uint16_t blocks; //!< Number of blocks uint16_t blocks; //!< Number of blocks
uint16_t crc; //!< CRC of block data uint16_t crc; //!< CRC of block data
uint16_t BLVersion; //!< Bootloader version uint16_t BLVersion; //!< Bootloader version
} __attribute__((packed)) RequestFirmwareConfig; } __attribute__((packed)) requestFirmwareConfig_t;
/// @brief FW block request structure /**
* @brief FW block request structure
*/
typedef struct { typedef struct {
uint16_t type; //!< Type of config uint16_t type; //!< Type of config
uint16_t version; //!< Version of config uint16_t version; //!< Version of config
uint16_t block; //!< Block index uint16_t block; //!< Block index
} __attribute__((packed)) RequestFWBlock; } __attribute__((packed)) requestFirmwareBlock_t;
/// @brief FW block reply structure /**
* @brief FW block reply structure
*/
typedef struct { typedef struct {
uint16_t type; //!< Type of config uint16_t type; //!< Type of config
uint16_t version; //!< Version of config uint16_t version; //!< Version of config
uint16_t block; //!< Block index uint16_t block; //!< Block index
uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data
} __attribute__((packed)) ReplyFWBlock; } __attribute__((packed)) replyFirmwareBlock_t;
/** /**
@@ -76,26 +118,28 @@ typedef struct {
* *
* Current firmware settings (type, version, crc, blocks) are read into _fc * Current firmware settings (type, version, crc, blocks) are read into _fc
*/ */
void readFirmwareSettings(); void readFirmwareSettings(void);
/** /**
* @brief Handle OTA FW update requests * @brief Handle OTA FW update requests
*/ */
void firmwareOTAUpdateRequest(); void firmwareOTAUpdateRequest(void);
/** /**
* @brief Handle OTA FW update responses * @brief Handle OTA FW update responses
* *
* This function handles incoming OTA FW packets and stores them to external flash (Sensebender) * This function handles incoming OTA FW packets and stores them to external flash (Sensebender)
*/ */
bool firmwareOTAUpdateProcess(); bool firmwareOTAUpdateProcess(void);
/** /**
* @brief Validate uploaded FW CRC * @brief Validate uploaded FW CRC
* *
* This function verifies if uploaded FW CRC is valid * 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
/** @}*/
Regular → Executable
+151 -56
View File
@@ -20,95 +20,190 @@
#include "MyConfig.h" #include "MyConfig.h"
#include "MyTransport.h" #include "MyTransport.h"
#include "MyProtocol.h" #include "MyProtocol.h"
#include <string.h>
uint8_t protocolH2i(char c); uint8_t protocolH2i(char c);
char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH]; char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH];
char _convBuffer[MAX_PAYLOAD*2+1]; char _convBuffer[MAX_PAYLOAD*2+1];
bool protocolParse(MyMessage &message, char *inputString) { bool protocolParse(MyMessage &message, char *inputString)
{
char *str, *p, *value=NULL; char *str, *p, *value=NULL;
uint8_t bvalue[MAX_PAYLOAD]; uint8_t bvalue[MAX_PAYLOAD];
uint8_t blen = 0; uint8_t blen = 0;
int i = 0; int i = 0;
uint8_t command = 0; uint8_t command = 0;
uint8_t ack = 0;
// Extract command data coming on serial line // Extract command data coming on serial line
for (str = strtok_r(inputString, ";", &p); // split using semicolon for (str = strtok_r(inputString, ";", &p); // split using semicolon
str && i < 6; // loop while str is not null an max 5 times str && i < 6; // loop while str is not null an max 5 times
str = strtok_r(NULL, ";", &p) // get subsequent tokens str = strtok_r(NULL, ";", &p) // get subsequent tokens
) { ) {
switch (i) { switch (i) {
case 0: // Radioid (destination) case 0: // Radioid (destination)
message.destination = atoi(str); message.destination = atoi(str);
break; break;
case 1: // Childid case 1: // Childid
message.sensor = atoi(str); message.sensor = atoi(str);
break; break;
case 2: // Message type case 2: // Message type
command = atoi(str); command = atoi(str);
mSetCommand(message, command); mSetCommand(message, command);
break; break;
case 3: // Should we request ack from destination? case 3: // Should we request ack from destination?
ack = atoi(str); mSetRequestAck(message, atoi(str)?1:0);
break; break;
case 4: // Data type case 4: // Data type
message.type = atoi(str); message.type = atoi(str);
break; break;
case 5: // Variable value case 5: // Variable value
if (command == C_STREAM) { if (command == C_STREAM) {
blen = 0; blen = 0;
while (*str) {
uint8_t val; uint8_t val;
while (*str) { val = protocolH2i(*str++) << 4;
val = protocolH2i(*str++) << 4; val += protocolH2i(*str++);
val += protocolH2i(*str++); bvalue[blen] = val;
bvalue[blen] = val; blen++;
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;
} }
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++; i++;
} }
//debug(PSTR("Received %d"), i); //debug(PSTR("Received %d"), i);
// Check for invalid input // Check for invalid input
if (i < 5) if (i < 5) {
return false; return false;
}
message.sender = GATEWAY_ADDRESS; message.sender = GATEWAY_ADDRESS;
message.last = GATEWAY_ADDRESS; message.last = GATEWAY_ADDRESS;
mSetRequestAck(message, ack?1:0); mSetAck(message, false);
mSetAck(message, false); if (command == C_STREAM) {
if (command == C_STREAM)
message.set(bvalue, blen); message.set(bvalue, blen);
else } else {
message.set(value); message.set(value);
}
return true; return true;
} }
char * protocolFormat(MyMessage &message) { 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)); {
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; return _fmtBuffer;
} }
uint8_t protocolH2i(char c) { char * protocolFormatMQTTTopic(const char* prefix, MyMessage &message)
uint8_t i = 0; {
if (c <= '9') snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/%d/%d/%d/%d/%d"), prefix,
i += c - '0'; message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type);
else if (c >= 'a') return _fmtBuffer;
i += c - 'a' + 10;
else
i += c - 'A' + 10;
return i;
} }
char * protocolFormatMQTTSubscribe(const char* prefix)
{
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/+/+/+/+/+"), prefix);
return _fmtBuffer;
}
#ifdef MY_GATEWAY_MQTT_CLIENT
bool protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsigned int length)
{
char *str, *p;
uint8_t i = 0;
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') {
i += c - 'a' + 10;
} else {
i += c - 'A' + 10;
}
return i;
}
+557 -430
View File
File diff suppressed because it is too large Load Diff
+247 -86
View File
@@ -17,6 +17,52 @@
* version 2 as published by the Free Software Foundation. * 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:
* - <b>MCO</b> messages emitted by MySensorsCore
* - SUB SYSTEMS:
* - MCO:<b>BGN</b> from @ref _begin()
* - MCO:<b>REG</b> from @ref _registerNode()
* - MCO:<b>SND</b> from @ref send()
* - MCO:<b>PIM</b> from @ref _processInternalMessages()
* - MCO:<b>NLK</b> 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 #ifndef MySensorsCore_h
#define MySensorsCore_h #define MySensorsCore_h
@@ -28,54 +74,64 @@
#include <stddef.h> #include <stddef.h>
#include <stdarg.h> #include <stdarg.h>
#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 #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 #else
#define debug(x,...) #define debug(x,...) //!< debug NULL, to be removed (follow-up PR)
#define CORE_DEBUG(x,...) //!< debug NULL
#endif #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
*
* This structure stores node-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)
};
/** /**
* @brief Controller configuration * @brief Controller configuration
* *
* This structure stores controllerrelated configurations * This structure stores controller-related configurations
*/ */
struct ControllerConfig { typedef struct {
uint8_t isMetric; //!< Flag indicating if metric or imperial measurements are used uint8_t isMetric; //!< Flag indicating if metric or imperial measurements are used
}; } controllerConfig_t;
/**
* @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. * Return this nodes id.
*/ */
uint8_t getNodeId(); uint8_t getNodeId(void);
/** /**
* Return the parent node id. * Return the parent node id.
*/ */
uint8_t getParentNodeId(); uint8_t getParentNodeId(void);
/** /**
* Sends node information to the gateway. * 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. * 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 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 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. * @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. * 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 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 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. * @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 * 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. * @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. * @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. * Send this nodes battery level to gateway.
* @param level Level between 0-100(%) * @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. * @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. * 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. * 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. * 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 childSensorId The unique child id for the different sensors connected to this Arduino. 0-254.
* @param variableType The variableType to fetch * @param variableType The variableType to fetch
* @param destination The nodeId of other node in radio network. Default is gateway * @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. * 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 * 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 * 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 pos The position to store value in (0-255)
* @param value to store in position * @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). * 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) * @param pos The position to fetch value from (0-255)
* @return Value to store in position * @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. * Wait for a specified amount of time to pass. Keeps process()ing.
* This does not power-down the radio nor the Arduino. * This does not power-down the radio nor the Arduino.
* Because this calls process() in a loop, it is a good way to wait * 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. * 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. * 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. * This does not power-down the radio nor the Arduino.
* Because this calls process() in a loop, it is a good way to wait * 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. * 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 cmd Command of incoming message.
* @param msgtype Message type. * @param msgtype Message type.
* @return True if specified message received * @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. * Sleep (PowerDownMode) the MCU and radio. Wake up on timer.
* @param ms Number of milliseconds to sleep. * @param sleepingMS Number of milliseconds to sleep.
* @return -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update) * @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 sleep(const uint32_t sleepingMS, const bool smartSleep = false);
int8_t smartSleep(unsigned long ms);
/** /**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change. * 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 * is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
* @param interrupt Interrupt that should trigger the wakeup * @param interrupt Interrupt that should trigger the wakeup
* @param mode RISING, FALLING, CHANGE * @param mode RISING, FALLING, CHANGE
* @param ms Number of milliseconds to sleep or 0 to sleep forever * @param sleepingMS 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 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 sleep(const uint8_t interrupt, const uint8_t mode, const uint32_t sleepingMS = 0,
int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0); const bool smartSleep = false);
/** /**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change for two separate interrupts. * 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 mode1 Mode for first interrupt (RISING, FALLING, CHANGE)
* @param interrupt2 Second interrupt that should trigger the wakeup * @param interrupt2 Second interrupt that should trigger the wakeup
* @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE) * @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE)
* @param ms Number of milliseconds to sleep or 0 to sleep forever * @param sleepingMS 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 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 sleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0); 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 MyLockgrp
* @ingroup internals
* @brief Lock a node and transmit provided message with 30m intervals * @brief Lock a node and transmit provided message with 30m intervals
* *
* This function is called if suspicious activity has exceeded the threshold (see * 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 * @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 UNLOCK_PIN low during power on/reset. * while unlocking with a custom bootloader require holding @ref MY_NODE_UNLOCK_PIN low during power on/reset.
* *
* @param str The string to transmit. * @param str The string to transmit.
*/ */
void nodeLock(const char* str); void _nodeLock(const char* str);
#endif
/****** 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); void _process(void);
/**
bool _processInternalMessages(); * @brief Processes internal messages
* @return True if received message requires further processing
void _infiniteLoop(); */
bool _processInternalMessages(void);
void _registerNode(); /**
* @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); bool _sendRoute(MyMessage &message);
extern NodeConfig _nc; /**
extern MyMessage _msg; // Buffer for incoming messages. * @brief Callback for incoming messages
extern MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others). * @param message
#ifdef MY_DEBUG */
extern char _convBuf[MAX_PAYLOAD*2+1];
#endif
void receive(const MyMessage &message) __attribute__((weak)); void receive(const MyMessage &message) __attribute__((weak));
/**
* @brief Callback for incoming time messages
*/
void receiveTime(unsigned long) __attribute__((weak)); void receiveTime(unsigned long) __attribute__((weak));
void presentation() __attribute__((weak)); /**
void before() __attribute__((weak)); * @brief Node presenation
void setup() __attribute__((weak)); */
void loop() __attribute__((weak)); 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 // 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) { static inline MyMessage& build(MyMessage &msg, const uint8_t destination, const uint8_t sensor,
msg.sender = sender; const uint8_t command, const uint8_t type, const bool ack = false)
{
msg.sender = getNodeId();
msg.destination = destination; msg.destination = destination;
msg.sensor = sensor; msg.sensor = sensor;
msg.type = type; msg.type = type;
mSetCommand(msg,command); mSetCommand(msg,command);
mSetRequestAck(msg,enableAck); mSetRequestAck(msg,ack);
mSetAck(msg,false); mSetAck(msg,false);
return msg; 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.sender = GATEWAY_ADDRESS;
msg.destination = GATEWAY_ADDRESS; msg.destination = GATEWAY_ADDRESS;
msg.sensor = 255; msg.sensor = NODE_SENSOR_ID;
msg.type = type; msg.type = type;
mSetCommand(msg, C_INTERNAL); mSetCommand(msg, C_INTERNAL);
mSetRequestAck(msg, false); mSetRequestAck(msg, false);
@@ -288,3 +447,5 @@ static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
#endif #endif
/** @}*/
+312 -252
View File
@@ -33,6 +33,12 @@
#if defined(MY_SIGNING_REQUEST_SIGNATURES) && (!defined(MY_SIGNING_ATSHA204) && !defined(MY_SIGNING_SOFT)) #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! #error You have to pick either MY_SIGNING_ATSHA204 or MY_SIGNING_SOFT in order to require signatures!
#endif #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 #ifdef MY_SIGNING_FEATURE
uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication
uint8_t _doWhitelist[32]; // Bitfield indicating which sensors require serial salted signatures 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; static uint8_t nof_failed_verifications = 0;
#endif #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 }; 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<<node%8)) #define DO_SIGN(node) (~_doSign[node>>3]&(1<<node%8))
#define SET_SIGN(node) (_doSign[node>>3]&=~(1<<node%8)) #define SET_SIGN(node) (_doSign[node>>3]&=~(1<<node%8))
#define CLEAR_SIGN(node) (_doSign[node>>3]|=(1<<node%8)) #define CLEAR_SIGN(node) (_doSign[node>>3]|=(1<<node%8))
#define DO_WHITELIST(node) (~_doWhitelist[node>>3]&(1<<node%8))
#define SET_WHITELIST(node) (_doWhitelist[node>>3]&=~(1<<node%8))
#define CLEAR_WHITELIST(node) (_doWhitelist[node>>3]|=(1<<node%8))
/*
* Stores signing identifier and a new nonce in provided message for signing operations.
*
* All space in message payload buffer is used for signing identifier and nonce.
* Returns false if subsystem is busy processing an ongoing signing operation.
* If successful, this marks the start of a signing operation at the receiving side so
* implementation is expected to do any necessary initializations within this call.
* This function is typically called as action when receiving a I_NONCE_REQUEST message.
*/
#if defined(MY_SIGNING_SOFT) #if defined(MY_SIGNING_SOFT)
extern void signerAtsha204SoftInit(void); extern void signerAtsha204SoftInit(void);
extern bool signerAtsha204SoftCheckTimer(void); extern bool signerAtsha204SoftCheckTimer(void);
@@ -68,71 +68,52 @@ extern bool signerAtsha204SoftGetNonce(MyMessage &msg);
extern void signerAtsha204SoftPutNonce(MyMessage &msg); extern void signerAtsha204SoftPutNonce(MyMessage &msg);
extern bool signerAtsha204SoftVerifyMsg(MyMessage &msg); extern bool signerAtsha204SoftVerifyMsg(MyMessage &msg);
extern bool signerAtsha204SoftSignMsg(MyMessage &msg); extern bool signerAtsha204SoftSignMsg(MyMessage &msg);
#endif #define signerBackendInit signerAtsha204SoftInit
#if defined(MY_SIGNING_ATSHA204) #define signerBackendCheckTimer signerAtsha204SoftCheckTimer
#define signerBackendGetNonce signerAtsha204SoftGetNonce
#define signerBackendPutNonce signerAtsha204SoftPutNonce
#define signerBackendVerifyMsg signerAtsha204SoftVerifyMsg
#define signerBackendSignMsg signerAtsha204SoftSignMsg
#elif defined(MY_SIGNING_ATSHA204)
extern void signerAtsha204Init(void); extern void signerAtsha204Init(void);
extern bool signerAtsha204CheckTimer(void); extern bool signerAtsha204CheckTimer(void);
extern bool signerAtsha204GetNonce(MyMessage &msg); extern bool signerAtsha204GetNonce(MyMessage &msg);
extern void signerAtsha204PutNonce(MyMessage &msg); extern void signerAtsha204PutNonce(MyMessage &msg);
extern bool signerAtsha204VerifyMsg(MyMessage &msg); extern bool signerAtsha204VerifyMsg(MyMessage &msg);
extern bool signerAtsha204SignMsg(MyMessage &msg); extern bool signerAtsha204SignMsg(MyMessage &msg);
#define signerBackendInit signerAtsha204Init
#define signerBackendCheckTimer signerAtsha204CheckTimer
#define signerBackendGetNonce signerAtsha204GetNonce
#define signerBackendPutNonce signerAtsha204PutNonce
#define signerBackendVerifyMsg signerAtsha204VerifyMsg
#define signerBackendSignMsg signerAtsha204SignMsg
#endif #endif
static bool skipSign(MyMessage &msg);
// Helper function to centralize signing/verification exceptions #else // not MY_SIGNING_FEATURE
static bool skipSign(MyMessage &msg) { #define signerBackendCheckTimer() true
if mGetAck(msg) {
SIGN_DEBUG(PSTR("Skipping security for ACK on command %d type %d\n"), mGetCommand(msg), msg.type);
return 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 || msg.type == I_FIND_PARENT_RESPONSE ||
msg.type == I_HEARTBEAT || 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);
return 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);
return true;
} else {
return false;
}
}
#endif // MY_SIGNING_FEATURE #endif // MY_SIGNING_FEATURE
static void prepareSigningPresentation(MyMessage &msg, uint8_t destination);
static bool signerInternalProcessPresentation(MyMessage &msg);
static bool signerInternalProcessNonceRequest(MyMessage &msg);
static bool signerInternalProcessNonceResponse(MyMessage &msg);
// Helper to prepare a signing presentation message void signerInit(void)
static void prepareSigningPresentation(MyMessage &msg, uint8_t destination) { {
// Only supports version 1 for now
build(msg, _nc.nodeId, destination, NODE_SENSOR_ID, C_INTERNAL, I_SIGNING_PRESENTATION, false).set("");
mSetLength(msg, 2);
mSetPayloadType(msg, P_CUSTOM); // displayed as hex
msg.data[0] = SIGNING_PRESENTATION_VERSION_1;
msg.data[1] = 0;
}
void signerInit(void) {
#if defined(MY_SIGNING_FEATURE) #if defined(MY_SIGNING_FEATURE)
// Read out the signing requirements from EEPROM // Read out the signing requirements from EEPROM
hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS,
sizeof(_doSign)); sizeof(_doSign));
// Read out the whitelist requirements from EEPROM // Read out the whitelist requirements from EEPROM
hwReadConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS, hwReadConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS,
sizeof(_doWhitelist)); sizeof(_doWhitelist));
#if defined(MY_SIGNING_SOFT) signerBackendInit();
signerAtsha204SoftInit();
#endif
#if defined(MY_SIGNING_ATSHA204)
signerAtsha204Init();
#endif
#endif #endif
} }
void signerPresentation(MyMessage &msg, uint8_t destination) { void signerPresentation(MyMessage &msg, uint8_t destination)
{
prepareSigningPresentation(msg, destination); prepareSigningPresentation(msg, destination);
#if defined(MY_SIGNING_REQUEST_SIGNATURES) #if defined(MY_SIGNING_REQUEST_SIGNATURES)
@@ -145,239 +126,126 @@ void signerPresentation(MyMessage &msg, uint8_t destination) {
#endif #endif
if (!_sendRoute(msg)) { if (!_sendRoute(msg)) {
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!")); SIGN_DEBUG(PSTR("Failed to transmit signing presentation!\n"));
} }
#if defined(MY_SIGNING_FEATURE)
// If we do support signing, wait for the gateway to tell us how it prefer us to transmit our messages
if (destination == GATEWAY_ADDRESS) { if (destination == GATEWAY_ADDRESS) {
SIGN_DEBUG(PSTR("Waiting for GW to send signing preferences...\n")); SIGN_DEBUG(PSTR("Waiting for GW to send signing preferences...\n"));
wait(2000, C_INTERNAL, I_SIGNING_PRESENTATION); wait(2000, C_INTERNAL, I_SIGNING_PRESENTATION);
} }
#endif
} }
bool signerProcessInternal(MyMessage &msg) { bool signerProcessInternal(MyMessage &msg)
uint8_t sender = msg.sender; {
(void)sender; bool ret;
if (mGetCommand(msg) == C_INTERNAL) { switch (msg.type) {
#if !defined(MY_SIGNING_FEATURE) && defined(MY_GATEWAY_FEATURE) case I_SIGNING_PRESENTATION:
// If we act as gateway and do not have the signing feature and receive a signing request we still ret = signerInternalProcessPresentation(msg);
// need to do make sure the requester does not believe the gateway still require signatures break;
if (msg.type == I_SIGNING_PRESENTATION) { case I_NONCE_REQUEST:
prepareSigningPresentation(msg, sender); ret = signerInternalProcessNonceRequest(msg);
SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures because we do not support it\n"), break;
sender); case I_NONCE_RESPONSE:
if (!_sendRoute(msg)) { ret = signerInternalProcessNonceResponse(msg);
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!")); break;
} default:
return true; // No need to further process I_SIGNING_PRESENTATION in this case ret = false; // Let the transport process this message further as it is not related to signing
} break;
#elif defined(MY_SIGNING_FEATURE)
if (msg.type == I_NONCE_REQUEST) {
#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
#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
} }
return false; return ret;
} }
bool signerCheckTimer(void) { bool signerCheckTimer(void)
#if defined(MY_SIGNING_SOFT) {
return signerAtsha204SoftCheckTimer(); return signerBackendCheckTimer();
#elif defined(MY_SIGNING_ATSHA204)
return signerAtsha204CheckTimer();
#else
return true; // Without a configured backend, we always give "positive" results
#endif
} }
bool signerSignMsg(MyMessage &msg) { bool signerSignMsg(MyMessage &msg)
{
bool ret;
#if defined(MY_SIGNING_FEATURE) #if defined(MY_SIGNING_FEATURE)
// If destination is known to require signed messages and we are the sender, // If destination is known to require signed messages and we are the sender,
// sign this message unless it is a handshake message // sign this message unless it is identified as an exception
if (DO_SIGN(msg.destination) && msg.sender == _nc.nodeId) { if (DO_SIGN(msg.destination) && msg.sender == getNodeId()) {
if (skipSign(msg)) { if (skipSign(msg)) {
return true; ret = true;
} else { } else {
// Send nonce-request // Send nonce-request
_signingNonceStatus=SIGN_WAITING_FOR_NONCE; _signingNonceStatus=SIGN_WAITING_FOR_NONCE;
if (!_sendRoute(build(_msgSign, _nc.nodeId, msg.destination, msg.sensor, if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL,
C_INTERNAL, I_NONCE_REQUEST, false).set(""))) { I_NONCE_REQUEST).set(""))) {
SIGN_DEBUG(PSTR("Failed to transmit nonce request!\n")); SIGN_DEBUG(PSTR("Failed to transmit nonce request!\n"));
return false; ret = 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"));
} else { } else {
SIGN_DEBUG(PSTR("Message to send could not be signed!\n")); SIGN_DEBUG(PSTR("Nonce requested from %d. Waiting...\n"), msg.destination);
return false; // 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 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 #else
(void)msg; (void)msg;
ret = true;
#endif // MY_SIGNING_FEATURE #endif // MY_SIGNING_FEATURE
return true; return ret;
} }
bool signerVerifyMsg(MyMessage &msg) { bool signerVerifyMsg(MyMessage &msg)
{
bool verificationResult = true; bool verificationResult = true;
// Before processing message, reject unsigned messages if signing is required and check signature // Before processing message, reject unsigned messages if signing is required and check signature
// (if it is signed and addressed to us) // (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 // 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 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... // 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 // Internal messages of certain types are not verified
if (skipSign(msg)) { if (skipSign(msg)) {
verificationResult = true; verificationResult = true;
} } else if (!mGetSigned(msg)) {
else if (!mGetSigned(msg)) {
// Got unsigned message that should have been signed // Got unsigned message that should have been signed
SIGN_DEBUG(PSTR("Message is not signed, but it should have been!\n")); SIGN_DEBUG(PSTR("Message is not signed, but it should have been!\n"));
verificationResult = false; verificationResult = false;
} else { } else {
#if defined(MY_SIGNING_SOFT) if (!signerBackendVerifyMsg(msg)) {
verificationResult = signerAtsha204SoftVerifyMsg(msg);
#endif
#if defined(MY_SIGNING_ATSHA204)
verificationResult = signerAtsha204VerifyMsg(msg);
#endif
if (!verificationResult) {
SIGN_DEBUG(PSTR("Signature verification failed!\n")); SIGN_DEBUG(PSTR("Signature verification failed!\n"));
verificationResult = false;
} }
#if defined(MY_NODE_LOCK_FEATURE) #if defined(MY_NODE_LOCK_FEATURE)
if (verificationResult) { if (verificationResult) {
@@ -386,9 +254,10 @@ bool signerVerifyMsg(MyMessage &msg) {
nof_failed_verifications = 0; nof_failed_verifications = 0;
} else { } else {
nof_failed_verifications++; 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) { if (nof_failed_verifications >= MY_NODE_LOCK_COUNTER_MAX) {
nodeLock("TMFV"); //Too many failed verifications _nodeLock("TMFV"); //Too many failed verifications
} }
} }
#endif #endif
@@ -404,23 +273,27 @@ bool signerVerifyMsg(MyMessage &msg) {
static uint8_t sha256_hash[32]; static uint8_t sha256_hash[32];
Sha256Class _soft_sha256; Sha256Class _soft_sha256;
void signerSha256Init(void) { void signerSha256Init(void)
{
memset(sha256_hash, 0, 32); memset(sha256_hash, 0, 32);
_soft_sha256.init(); _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++) { for (size_t i = 0; i < sz; i++) {
_soft_sha256.write(data[i]); _soft_sha256.write(data[i]);
} }
} }
uint8_t* signerSha256Final(void) { uint8_t* signerSha256Final(void)
{
memcpy(sha256_hash, _soft_sha256.result(), 32); memcpy(sha256_hash, _soft_sha256.result(), 32);
return sha256_hash; 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; int retVal;
size_t i; size_t i;
int done = 0; int done = 0;
@@ -448,3 +321,190 @@ int signerMemcmp(const void* a, const void* b, size_t sz) {
} }
return retVal; 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
}
+10 -16
View File
@@ -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 * 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. * 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 * 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 * course not mandatory and some might be happy with only having encryption to cover their need for security. I, however, have only focused on
* <i>integrity</i> and <i>authenticity</i> while at the same time keeping the current message routing mechanisms intact and therefore leave * <i>integrity</i> and <i>authenticity</i> 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. * 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 * 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 * 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.<br> * 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.<br>
* If you want to have two nodes communicate securely directly with each other, the nodes that require signatures must send a presentation * 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().<br> * message to all nodes it expect signed messages from (only the gateway is informed automatically). See @ref signerPresentation().<br>
* A node can have three "states" with respect to signing: * 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.<br> * If a node does require signing, any unsigned message sent to the node will be rejected.<br>
* This also applies to the gateway. However, the difference is that the gateway will only require signed messages from nodes it knows in turn * 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.<br> * require signed messages (unless @ref MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL is set).<br>
* 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 * 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.<br> * of type @ref I_SIGNING_PRESENTATION and provide flags as payload that inform the receiver of the signing preferences of the sender.<br>
* 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 * 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 * 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.<br> * expect signed messages.<br>
* 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, * 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: * The following sequence diagram illustrate how messages are passed in a MySensors network with respect to signing:
* @image html MySigning/signingsequence.png * @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 * 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 * 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.<br> * process. The list is stored as the value of the flag that enables whitelisting, @ref MY_SIGNING_NODE_WHITELISTING.<br>
* *
* Whitelisting is achieved by 'salting' the signature with some node-unique information known to the receiver. In the case of ATSHA204A this is the * 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 * 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.<br> * figure out a "trusted" serial by snooping on the traffic.<br>
@@ -429,7 +431,7 @@
* ... * ...
* @endcode * @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):<br> * 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):<br>
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_SOFT * #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * #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 */ /** @brief Helper macro to determine the number of elements in a array */
#define NUM_OF(x) (sizeof(x)/sizeof(x[0])) #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<<node%8))
/** @brief Helper macro to set that node require serial salted signatures */
#define SET_WHITELIST(node) (_doWhitelist[node>>3]&=~(1<<node%8))
/** @brief Helper macro to set that node does not require serial salted signatures */
#define CLEAR_WHITELIST(node) (_doWhitelist[node>>3]|=(1<<node%8))
/** /**
* @brief Initializes signing infrastructure and associated backend. * @brief Initializes signing infrastructure and associated backend.
* *
@@ -521,7 +515,7 @@ void signerInit(void);
* @param destination Node ID of the destination. * @param destination Node ID of the destination.
*/ */
void signerPresentation(MyMessage &msg, uint8_t destination); void signerPresentation(MyMessage &msg, uint8_t destination);
/** /**
* @brief Manages internal signing message handshaking. * @brief Manages internal signing message handshaking.
* *
@@ -572,7 +566,7 @@ bool signerPutNonce(MyMessage &msg);
* deinitializations and enter a power saving state within this call. * deinitializations and enter a power saving state within this call.
* \n@b Usage: This function is typically called as action when receiving a @ref I_NONCE_RESPONSE * \n@b Usage: This function is typically called as action when receiving a @ref I_NONCE_RESPONSE
* message and after @ref signerPutNonce() has successfully been executed. * message and after @ref signerPutNonce() has successfully been executed.
* *
* @param msg The message to sign. * @param msg The message to sign.
* @returns @c true if successful, else @c false. * @returns @c true if successful, else @c false.
*/ */
+74 -46
View File
@@ -41,7 +41,7 @@ uint8_t _signing_tx_buffer[SHA204_CMD_SIZE_MAX];
extern uint8_t _doWhitelist[32]; extern uint8_t _doWhitelist[32];
#ifdef MY_SIGNING_NODE_WHITELISTING #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 #endif
static void signerCalculateSignature(MyMessage &msg, bool signing); static void signerCalculateSignature(MyMessage &msg, bool signing);
@@ -52,21 +52,25 @@ static uint8_t* signerSha256(const uint8_t* data, size_t sz);
static char i2h(uint8_t i) static char i2h(uint8_t i)
{ {
uint8_t k = i & 0x0F; uint8_t k = i & 0x0F;
if (k <= 9) if (k <= 9) {
return '0' + k; return '0' + k;
else } else {
return 'A' + k - 10; return 'A' + k - 10;
}
} }
static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) { static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz)
{
static char printBuffer[300]; static char printBuffer[300];
if (NULL == buf) {
return;
}
#ifdef MY_GATEWAY_FEATURE #ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) // 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); snprintf_P(printBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
MY_SERIALDEVICE.print(printBuffer); MY_SERIALDEVICE.print(printBuffer);
#endif #endif
for (int i=0; i<sz; i++) for (int i=0; i<sz; i++) {
{
printBuffer[i * 2] = i2h(buf[i] >> 4); printBuffer[i * 2] = i2h(buf[i] >> 4);
printBuffer[(i * 2) + 1] = i2h(buf[i]); 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'; printBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1-strlen_P((const char*)str)] = '\0';
#endif #endif
MY_SERIALDEVICE.print(str); MY_SERIALDEVICE.print(str);
if (sz > 0) if (sz > 0) {
{
MY_SERIALDEVICE.print(printBuffer); MY_SERIALDEVICE.print(printBuffer);
} }
MY_SERIALDEVICE.println(""); 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) #define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
#endif #endif
void signerAtsha204Init(void) { void signerAtsha204Init(void)
{
atsha204_init(MY_SIGNING_ATSHA204_PIN); atsha204_init(MY_SIGNING_ATSHA204_PIN);
} }
bool signerAtsha204CheckTimer(void) { bool signerAtsha204CheckTimer(void)
{
if (_signing_verification_ongoing) { 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); DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce // Purge nonce
memset(_signing_signing_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); memset(_signing_signing_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
memset(_signing_verifying_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); memset(_signing_verifying_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
_signing_verification_ongoing = false; _signing_verification_ongoing = false;
return false; return false;
} }
} }
return true; return true;
} }
bool signerAtsha204GetNonce(MyMessage &msg) { bool signerAtsha204GetNonce(MyMessage &msg)
{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
// Generate random number for use as nonce // 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 // 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 // This 32-byte random value is then hashed (SHA256) to produce the resulting nonce
(void)atsha204_wakeup(_signing_temp_message); (void)atsha204_wakeup(_signing_temp_message);
if (atsha204_execute(SHA204_RANDOM, RANDOM_SEED_UPDATE, 0, 0, NULL, 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); DEBUG_SIGNING_PRINTBUF(F("Failed to generate nonce"), NULL, 0);
return false; 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) // 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 // 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' // 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; return true;
} }
void signerAtsha204PutNonce(MyMessage &msg) { void signerAtsha204PutNonce(MyMessage &msg)
{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); 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); 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 we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) { if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0);
return false; return false;
} }
// Calculate signature of message // Calculate signature of message
@@ -157,10 +168,12 @@ bool signerAtsha204SignMsg(MyMessage &msg) {
if (DO_WHITELIST(msg.destination)) { if (DO_WHITELIST(msg.destination)) {
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device // 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; _signing_signing_nonce[32] = msg.sender;
atsha204_getSerialNumber(&_signing_signing_nonce[33]); 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); 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; _signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
// Transfer as much signature data as the remaining space in the message permits // 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)); memcpy(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg)); MAX_PAYLOAD-mGetLength(msg));
DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)],
MAX_PAYLOAD-mGetLength(msg));
return true; return true;
} }
bool signerAtsha204VerifyMsg(MyMessage &msg) { bool signerAtsha204VerifyMsg(MyMessage &msg)
{
if (!_signing_verification_ongoing) { if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
return false; return false;
} else { } else {
// Make sure we have not expired // Make sure we have not expired
if (!signerCheckTimer()) { if (!signerCheckTimer()) {
return false; return false;
} }
_signing_verification_ongoing = false; _signing_verification_ongoing = false;
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) { if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0); 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 signerCalculateSignature(msg, false); // Get signature of message
#ifdef MY_SIGNING_NODE_WHITELISTING #ifdef MY_SIGNING_NODE_WHITELISTING
@@ -203,10 +220,12 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
for (j=0; j < NUM_OF(_signing_whitelist); j++) { for (j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) { if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0); 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; _signing_verifying_nonce[32] = msg.sender;
memcpy(&_signing_verifying_nonce[33], _signing_whitelist[j].serial, SHA204_SERIAL_SZ); 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; break;
} }
} }
@@ -225,12 +244,14 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
_signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; _signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
// Compare the caluclated signature with the provided signature // 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))) { if (signerMemcmp(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
DEBUG_SIGNING_PRINTBUF(F("Signature bad: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); 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 #ifdef MY_SIGNING_NODE_WHITELISTING
DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0);
#endif #endif
return false; return false;
} else { } else {
DEBUG_SIGNING_PRINTBUF(F("Signature OK"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signature OK"), NULL, 0);
return true; 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]) // 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); (void)atsha204_wakeup(_signing_temp_message);
memset(_signing_temp_message, 0, 32); 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 // 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("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE],
DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), signing ? _signing_signing_nonce : _signing_verifying_nonce, 32); MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
(void)atsha204_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, _signing_temp_message, DEBUG_SIGNING_PRINTBUF(F("Current nonce: "),
WRITE_COUNT_LONG, _signing_tx_buffer, WRITE_RSP_SIZE, _signing_rx_buffer); 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) // 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, (void)atsha204_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH,
signing ? _signing_signing_nonce : _signing_verifying_nonce, signing ? _signing_signing_nonce : _signing_verifying_nonce,
NONCE_COUNT_LONG, _signing_tx_buffer, NONCE_RSP_SIZE_SHORT, _signing_rx_buffer); NONCE_COUNT_LONG, _signing_tx_buffer, NONCE_RSP_SIZE_SHORT, _signing_rx_buffer);
// Purge nonce when used // 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 // Generate digest of data and nonce
(void)atsha204_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL, (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 // Calculate HMAC of message+nonce digest and secret key
(void)atsha204_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL, (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); 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) // 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]) // 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 // Initiate SHA256 calculator
(void)atsha204_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, (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 // Calculate a hash
memset(_signing_temp_message, 0x00, SHA_MSG_SIZE); 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-2] = (sz >> 5);
_signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3); _signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3);
(void)atsha204_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message, (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); DEBUG_SIGNING_PRINTBUF(F("SHA256: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
return &_signing_rx_buffer[SHA204_BUFFER_POS_DATA]; return &_signing_rx_buffer[SHA204_BUFFER_POS_DATA];
+94 -45
View File
@@ -47,75 +47,89 @@ extern uint8_t _doWhitelist[32];
static uint8_t _signing_node_serial_info[9]; static uint8_t _signing_node_serial_info[9];
#ifdef MY_SIGNING_NODE_WHITELISTING #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 #endif
static void signerCalculateSignature(MyMessage &msg, bool signing); static void signerCalculateSignature(MyMessage &msg, bool signing);
#ifdef MY_DEBUG_VERBOSE_SIGNING #ifdef MY_DEBUG_VERBOSE_SIGNING
static char i2h(uint8_t i) static char i2h(uint8_t i)
{ {
uint8_t k = i & 0x0F; uint8_t k = i & 0x0F;
if (k <= 9) if (k <= 9) {
return '0' + k; return '0' + k;
else } else {
return 'A' + k - 10; 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]; 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) // 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); snprintf_P(printBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
MY_SERIALDEVICE.print(printBuffer); MY_SERIALDEVICE.print(printBuffer);
#endif #endif
for (int i=0; i<sz; i++) for (int i=0; i<sz; i++) {
{
printBuffer[i * 2] = i2h(buf[i] >> 4); printBuffer[i * 2] = i2h(buf[i] >> 4);
printBuffer[(i * 2) + 1] = i2h(buf[i]); printBuffer[(i * 2) + 1] = i2h(buf[i]);
} }
printBuffer[sz * 2] = '\0'; printBuffer[sz * 2] = '\0';
#ifdef MY_GATEWAY_FEATURE #if defined(MY_GATEWAY_FEATURE) && !defined(__linux__)
// Truncate message if this is gateway node // Truncate message if this is gateway node
printBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1-strlen_P((const char*)str)] = '\0'; printBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1-strlen_P((const char*)str)] = '\0';
#endif #endif
MY_SERIALDEVICE.print(str); MY_SERIALDEVICE.print(str);
if (sz > 0) if (sz > 0) {
{
MY_SERIALDEVICE.print(printBuffer); MY_SERIALDEVICE.print(printBuffer);
} }
#if !defined(__linux__)
MY_SERIALDEVICE.println(""); MY_SERIALDEVICE.println("");
#endif
} }
#else #else
#define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
#endif #endif
void signerAtsha204SoftInit(void) { void signerAtsha204SoftInit(void)
{
// initialize pseudo-RNG // initialize pseudo-RNG
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)); hwRandomNumberInit();
// Set secrets // Set secrets
hwReadConfigBlock((void*)_signing_hmac_key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32); 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); hwReadConfigBlock((void*)_signing_node_serial_info, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
} }
bool signerAtsha204SoftCheckTimer(void) { bool signerAtsha204SoftCheckTimer(void)
{
if (_signing_verification_ongoing) { 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); DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce // Purge nonce
memset(_signing_signing_nonce, 0xAA, 32); memset(_signing_signing_nonce, 0xAA, 32);
memset(_signing_verifying_nonce, 0xAA, 32); memset(_signing_verifying_nonce, 0xAA, 32);
_signing_verification_ongoing = false; _signing_verification_ongoing = false;
return false; return false;
} }
} }
return true; return true;
} }
bool signerAtsha204SoftGetNonce(MyMessage &msg) { bool signerAtsha204SoftGetNonce(MyMessage &msg)
{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0); 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 // hashed (SHA256) to produce the resulting nonce
_signing_sha256.init(); _signing_sha256.init();
for (int i = 0; i < 32; i++) { 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) // 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 // 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' // 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; return true;
} }
void signerAtsha204SoftPutNonce(MyMessage &msg) { void signerAtsha204SoftPutNonce(MyMessage &msg)
{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0);
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); 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); 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 we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) { if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0);
return false; return false;
} }
// Calculate signature of message // Calculate signature of message
@@ -160,9 +178,13 @@ bool signerAtsha204SoftSignMsg(MyMessage &msg) {
if (DO_WHITELIST(msg.destination)) { if (DO_WHITELIST(msg.destination)) {
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
_signing_sha256.init(); _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); _signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]); for (int i=0; i<SHA204_SERIAL_SZ; i++) {
_signing_sha256.write(_signing_node_serial_info[i]);
}
memcpy(_signing_hmac, _signing_sha256.result(), 32); memcpy(_signing_hmac, _signing_sha256.result(), 32);
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32); DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32);
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
@@ -173,30 +195,33 @@ bool signerAtsha204SoftSignMsg(MyMessage &msg) {
// Transfer as much signature data as the remaining space in the message permits // Transfer as much signature data as the remaining space in the message permits
memcpy(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg)); memcpy(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg));
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));
return true; return true;
} }
bool signerAtsha204SoftVerifyMsg(MyMessage &msg) { bool signerAtsha204SoftVerifyMsg(MyMessage &msg)
{
if (!_signing_verification_ongoing) { if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
return false; return false;
} else { } else {
// Make sure we have not expired // Make sure we have not expired
if (!signerCheckTimer()) { if (!signerCheckTimer()) {
return false; return false;
} }
_signing_verification_ongoing = false; _signing_verification_ongoing = false;
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) { if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0);
return false; return false;
} }
// Get signature of message // Get signature of message
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); signerCalculateSignature(msg, false);
#ifdef MY_SIGNING_NODE_WHITELISTING #ifdef MY_SIGNING_NODE_WHITELISTING
@@ -206,9 +231,13 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
if (_signing_whitelist[j].nodeId == msg.sender) { if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0);
_signing_sha256.init(); _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); _signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_whitelist[j].serial[i]); for (int i=0; i<SHA204_SERIAL_SZ; i++) {
_signing_sha256.write(_signing_whitelist[j].serial[i]);
}
memcpy(_signing_hmac, _signing_sha256.result(), 32); memcpy(_signing_hmac, _signing_sha256.result(), 32);
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32); DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32);
break; break;
@@ -229,7 +258,7 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
#ifdef MY_SIGNING_NODE_WHITELISTING #ifdef MY_SIGNING_NODE_WHITELISTING
DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0);
#endif #endif
return false; return false;
} else { } else {
DEBUG_SIGNING_PRINTBUF(F("Signature OK"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signature OK"), NULL, 0);
return true; return true;
@@ -238,11 +267,15 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
} }
// Helper to calculate signature of msg (returned in hmac) // Helper to calculate signature of msg (returned in hmac)
static void signerCalculateSignature(MyMessage &msg, bool signing) { static void signerCalculateSignature(MyMessage &msg, bool signing)
{
memset(_signing_temp_message, 0, 32); 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],
DEBUG_SIGNING_PRINTBUF(F("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), signing ? _signing_signing_nonce : _signing_verifying_nonce, 32); 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);
// ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data: // ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data:
// 32 bytes zeroes // 32 bytes zeroes
@@ -268,7 +301,9 @@ static void signerCalculateSignature(MyMessage &msg, bool signing) {
// Calculate message digest first // Calculate message digest first
_signing_sha256.init(); _signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]); for (int i=0; i<32; i++) {
_signing_sha256.write(_signing_temp_message[i]);
}
_signing_sha256.write(0x15); // OPCODE _signing_sha256.write(0x15); // OPCODE
_signing_sha256.write(0x02); // param1 _signing_sha256.write(0x02); // param1
_signing_sha256.write(0x08); // param2(1) _signing_sha256.write(0x08); // param2(1)
@@ -276,26 +311,40 @@ static void signerCalculateSignature(MyMessage &msg, bool signing) {
_signing_sha256.write(0xEE); // SN[8] _signing_sha256.write(0xEE); // SN[8]
_signing_sha256.write(0x01); // SN[0] _signing_sha256.write(0x01); // SN[0]
_signing_sha256.write(0x23); // SN[1] _signing_sha256.write(0x23); // SN[1]
for (int i=0; i<25; i++) _signing_sha256.write(0x00); for (int i=0; i<25; i++) {
for (int i=0; i<32; i++) _signing_sha256.write(signing ? _signing_signing_nonce[i] : _signing_verifying_nonce[i]); _signing_sha256.write(0x00);
}
for (int i=0; i<32; i++) {
_signing_sha256.write(signing ? _signing_signing_nonce[i] : _signing_verifying_nonce[i]);
}
// Purge nonce when used // Purge nonce when used
memset(signing ? _signing_signing_nonce : _signing_verifying_nonce, 0xAA, 32); memset(signing ? _signing_signing_nonce : _signing_verifying_nonce, 0xAA, 32);
memcpy(_signing_temp_message, _signing_sha256.result(), 32); memcpy(_signing_temp_message, _signing_sha256.result(), 32);
// Feed "message" to HMAC calculator // Feed "message" to HMAC calculator
_signing_sha256.initHmac(_signing_hmac_key,32); // Set the key to use _signing_sha256.initHmac(_signing_hmac_key,32); // Set the key to use
for (int i=0; i<32; i++) _signing_sha256.write(0x00); // 32 bytes zeroes for (int i=0; i<32; i++) {
for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]); // 32 bytes digest _signing_sha256.write(0x00); // 32 bytes zeroes
}
for (int i=0; i<32; i++) {
_signing_sha256.write(_signing_temp_message[i]); // 32 bytes digest
}
_signing_sha256.write(0x11); // OPCODE _signing_sha256.write(0x11); // OPCODE
_signing_sha256.write(0x04); // Mode _signing_sha256.write(0x04); // Mode
_signing_sha256.write(0x00); // SlotID(1) _signing_sha256.write(0x00); // SlotID(1)
_signing_sha256.write(0x00); // SlotID(2) _signing_sha256.write(0x00); // SlotID(2)
for (int i=0; i<11; i++) _signing_sha256.write(0x00); // 11 bytes zeroes for (int i=0; i<11; i++) {
_signing_sha256.write(0x00); // 11 bytes zeroes
}
_signing_sha256.write(0xEE); // SN[8] _signing_sha256.write(0xEE); // SN[8]
for (int i=0; i<4; i++) _signing_sha256.write(0x00); // 4 bytes zeroes for (int i=0; i<4; i++) {
_signing_sha256.write(0x00); // 4 bytes zeroes
}
_signing_sha256.write(0x01); // SN[0] _signing_sha256.write(0x01); // SN[0]
_signing_sha256.write(0x23); // SN[1] _signing_sha256.write(0x23); // SN[1]
for (int i=0; i<2; i++) _signing_sha256.write(0x00); // 2 bytes zeroes for (int i=0; i<2; i++) {
_signing_sha256.write(0x00); // 2 bytes zeroes
}
memcpy(_signing_hmac, _signing_sha256.resultHmac(), 32); memcpy(_signing_hmac, _signing_sha256.resultHmac(), 32);
+719 -431
View File
File diff suppressed because it is too large Load Diff
+345 -82
View File
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes. * network topology allowing messages to be routed to nodes.
* *
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org> * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
* *
* Documentation: http://www.mysensors.org * Documentation: http://www.mysensors.org
@@ -17,154 +17,355 @@
* version 2 as published by the Free Software Foundation. * version 2 as published by the Free Software Foundation.
*/ */
/** /**
* @file MyTransport.h * @file MyTransport.h
* *
* @brief API declaration for MyTransport * @defgroup MyTransportgrp MyTransport
*/ * @ingroup internals
* @{
*
* Transport-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE
* - [!] Exclamation mark is prepended in case of error
* - SYSTEM:
* - <b>TSM</b>: messages emitted by the transport state machine
* - <b>TSF</b>: messages emitted by transport support functions
* - SUB SYSTEMS:
* - Transport state machine (<b>TSM</b>)
* - TSM:INIT from <b>stInit</b> Initialize transport and radio
* - TSM:FPAR from <b>stParent</b> Find parent
* - TSM:ID from <b>stID</b> Check/request node ID, if dynamic node ID set
* - TSM:UPL from <b>stUplink</b> Verify uplink connection by pinging GW
* - TSM:READY from <b>stReady</b> Transport is ready and fully operational
* - TSM:FAIL from <b>stFailure</b> Failure in transport link or transport HW
* - Transport support function (<b>TSF</b>)
* - 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 | | <b>Transition to stInit state</b>
* | | 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 | | <b>Transition to stParent state</b>
* | | 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 | | <b>Transition to stID state</b>
* | | 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 | | <b>Transition to stUplink state</b>
* | | 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| <b>Transition to stReady</b> 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 | <b>Transition to stFailure state</b>, 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 <a href="https://www.mysensors.org/download/serial_api_20">here</a> 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:
* - <b>s</b>=sensor ID
* - <b>c</b>=command
* - <b>t</b>=msg type
* - <b>pt</b>=payload type
* - <b>l</b>=length
* - <b>sg</b>=signing flag
* - <b>ft</b>=failed uplink transmission counter
* - <b>st</b>=send status, OK=success, NACK=no radio ACK received
*
* @brief API declaration for MyTransport
*
*/
#ifndef MyTransport_h #ifndef MyTransport_h
#define MyTransport_h #define MyTransport_h
#include "MySensorsCore.h" #include "MySensorsCore.h"
#if defined(MY_REPEATER_FEATURE) // debug
#define TRANSMISSION_FAILURES 10 //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes #if defined(MY_DEBUG)
#define TRANSPORT_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
extern char _convBuf[MAX_PAYLOAD * 2 + 1];
#else #else
#define TRANSMISSION_FAILURES 5 //!< search for a new parent node after this many transmission failures, lower threshold for non-repeating nodes #define TRANSPORT_DEBUG(x,...) //!< debug NULL
#endif
#if defined(MY_REPEATER_FEATURE)
#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 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 #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 _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 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 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 * @brief SM state
* *
* This structure stores SM state definitions * This structure stores SM state definitions
*/ */
struct State { typedef struct {
void(*Transition)(); //!< state transition function void(*Transition)(void); //!< state transition function
void(*Update)(); //!< state update function void(*Update)(void); //!< state update function
}; } transportState_t;
/** /**
* @brief Status variables and SM state * @brief Status variables and SM state
* *
* This structure stores transport status and SM variables * This structure stores transport status and SM variables
*/ */
typedef struct { 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 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 findingParentNode : 1; //!< flag finding parent node is active
bool preferredParentFound : 1; //!< flag preferred parent found bool preferredParentFound : 1; //!< flag preferred parent found
bool uplinkOk : 1; //!< flag uplink ok bool uplinkOk : 1; //!< flag uplink ok
bool pingActive : 1; //!< flag ping active bool pingActive : 1; //!< flag ping active
bool transportActive : 1; //!< flag transport active bool transportActive : 1; //!< flag transport active
uint8_t reserved : 3; //!< reserved uint8_t stateRetries : 3; //!< retries / state re-enter (max 7)
uint8_t retries : 4; //!< retries / state re-enter // 8 bits
uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions (max 15)
uint8_t pingResponse; //!< stores hops received in I_PONG uint8_t failureCounter : 3; //!< counter for TSM failures (max 7)
} __attribute__((packed)) transportSM; 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 // PRIVATE functions
/** /**
* @brief Initialize SM variables and transport HW * @brief Initialize SM variables and transport HW
*/ */
void stInitTransition(); void stInitTransition(void);
/**
* @brief Initialize transport
*/
void stInitUpdate(void);
/** /**
* @brief Find parent * @brief Find parent
*/ */
void stParentTransition(); void stParentTransition(void);
/** /**
* @brief Verify find parent responses * @brief Verify find parent responses
*/ */
void stParentUpdate(); void stParentUpdate(void);
/** /**
* @brief Send ID request * @brief Send ID request
*/ */
void stIDTransition(); void stIDTransition(void);
/** /**
* @brief Verify ID response and GW link * @brief Verify ID response and GW link
*/ */
void stIDUpdate(); void stIDUpdate(void);
/** /**
* @brief Send uplink ping request * @brief Send uplink ping request
*/ */
void stUplinkTransition(); void stUplinkTransition(void);
/**
* @brief Verify uplink response
*/
void stUplinkUpdate(void);
/** /**
* @brief Set transport OK * @brief Set transport OK
*/ */
void stOKTransition(); void stReadyTransition(void);
/** /**
* @brief Monitor transport link * @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 * @brief Re-initialize transport after timeout
*/ */
void stFailureUpdate(); void stFailureUpdate(void);
/** /**
* @brief Switch SM state * @brief Switch SM state
* @param newState New state to switch SM to * @param newState New state to switch SM to
*/ */
void transportSwitchSM(State& newState); void transportSwitchSM(transportState_t& newState);
/** /**
* @brief Update SM state * @brief Update SM state
*/ */
void transportUpdateSM(); void transportUpdateSM(void);
/** /**
* @brief Request time in current SM state * @brief Request time in current SM state
* @return ms in current 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 * @brief Process all pending messages in RX FIFO
*/ */
void transportProcessFIFO(); void transportProcessFIFO(void);
/** /**
* @brief Receive message from RX FIFO and process * @brief Receive message from RX FIFO and process
*/ */
void transportProcessMessage(); void transportProcessMessage(void);
/** /**
* @brief Assign node ID * @brief Assign node ID
* @param newNodeId New 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 * @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 cmd Specific command
* @param msgtype Specific message type * @param msgType Specific message type
* @return true if specified command received within waiting time * @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 * @brief Ping node
* @param targetId Node to be pinged * @param targetId Node to be pinged
* @return hops from pinged node or 255 if no answer received within 2000ms * @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 * @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 * @param message
* @return true if message sent successfully * @return true if message sent successfully
@@ -180,55 +381,118 @@ bool transportSendRoute(MyMessage &message);
* @brief Send message to recipient * @brief Send message to recipient
* @param to Recipient of message * @param to Recipient of message
* @param 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 * @brief Check uplink to GW, includes flooding control
* @param force to override flood control timer * @param force to override flood control timer
* @return true if uplink ok * @return true if uplink ok
*/ */
bool transportCheckUplink(bool force); bool transportCheckUplink(const bool force = false);
// PUBLIC functions // 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 * @brief Initialize transport and SM
*/ */
void transportInitialize(); void transportInitialize(void);
/** /**
* @brief Process FIFO msg and update SM * @brief Process FIFO msg and update SM
*/ */
void transportProcess(); void transportProcess(void);
/** /**
* @brief Verify transport status * @brief Flag transport ready
* @return true if transport is initialize and ready * @return true if transport is initialized and ready
*/ */
bool isTransportOK(); bool isTransportReady(void);
/**
* @brief Flag searching parent ongoing
* @return true if transport is searching for parent
*/
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 * @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 // interface functions for radio driver
/** /**
* @brief Initialize transport HW * @brief Initialize transport HW
* @return true if initalization successful * @return true if initialization successful
*/ */
bool transportInit(); bool transportInit(void);
/** /**
* @brief Set node address * @brief Set node address
*/ */
void transportSetAddress(uint8_t address); void transportSetAddress(const uint8_t address);
/** /**
* @brief Retrieve node address * @brief Retrieve node address
*/ */
uint8_t transportGetAddress(); uint8_t transportGetAddress(void);
/** /**
* @brief Send message * @brief Send message
* @param to recipient * @param to recipient
@@ -236,27 +500,26 @@ uint8_t transportGetAddress();
* @param len length of message (header + payload) * @param len length of message (header + payload)
* @return true if message sent successfully * @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 * @brief Verify if RX FIFO has pending messages
* @return true if message available in RX FIFO * @return true if message available in RX FIFO
*/ */
bool transportAvailable(); bool transportAvailable(void);
/** /**
* @brief Sanity check for transport: is transport still responsive? * @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 * @brief Receive message from FIFO
* @return length of recevied message (header + payload) * @return length of recevied message (header + payload)
*/ */
uint8_t transportReceive(void* data); uint8_t transportReceive(void* data);
/** /**
* @brief Power down transport HW * @brief Power down transport HW
*/ */
void transportPowerDown(); void transportPowerDown(void);
#endif // MyTransport_h #endif // MyTransport_h
/** @}*/ /** @}*/
+104 -44
View File
@@ -19,77 +19,137 @@
#include "MyConfig.h" #include "MyConfig.h"
#include "MyTransport.h" #include "MyTransport.h"
#include "drivers/RF24/RF24.h" #include "drivers/RF24/RF24.h"
#include "drivers/CircularBuffer/CircularBuffer.h"
#if defined(MY_RF24_ENABLE_ENCRYPTION) #if defined(MY_RF24_ENABLE_ENCRYPTION)
#include "drivers/AES/AES.h" #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<transportQueuedMessage> 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 #endif
#if defined(MY_RF24_ENABLE_ENCRYPTION) #if defined(MY_RF24_ENABLE_ENCRYPTION)
AES _aes; AES _aes;
uint8_t _dataenc[32] = {0}; uint8_t _dataenc[32] = {0};
uint8_t _psk[16]; uint8_t _psk[16];
#endif #endif
bool transportInit() { bool transportInit(void)
{
#if defined(MY_RF24_ENABLE_ENCRYPTION) #if defined(MY_RF24_ENABLE_ENCRYPTION)
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
//set up AES-key //set up AES-key
_aes.set_key(_psk, 16); _aes.set_key(_psk, 16);
// Make sure it is purged from memory when set // Make sure it is purged from memory when set
memset(_psk, 0, 16); memset(_psk, 0, 16);
#endif #endif
#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
RF24_registerReceiveCallback( transportRxCallback );
#endif
return RF24_initialize(); return RF24_initialize();
} }
void transportSetAddress(uint8_t address) { void transportSetAddress(const uint8_t address)
{
RF24_setNodeAddress(address); RF24_setNodeAddress(address);
RF24_startListening(); RF24_startListening();
} }
uint8_t transportGetAddress() { uint8_t transportGetAddress(void)
{
return RF24_getNodeID(); return RF24_getNodeID();
} }
bool transportSend(uint8_t recipient, const void* data, uint8_t len) { 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 #if defined(MY_RF24_ENABLE_ENCRYPTION)
memcpy(_dataenc,data,len); // copy input data because it is read-only
// has to be adjusted, WIP! (void)memcpy(_dataenc,data,len);
_aes.set_IV(0); // has to be adjusted, WIP!
len = len > 16 ? 32 : 16; _aes.set_IV(0);
//encrypt data const uint8_t finalLength = len > 16 ? 32 : 16;
_aes.cbc_encrypt(_dataenc, _dataenc, len/16); //encrypt data
bool status = RF24_sendMessage( recipient, _dataenc, len ); _aes.cbc_encrypt(_dataenc, _dataenc, finalLength /16);
#else return RF24_sendMessage(to, _dataenc, finalLength);
bool status = RF24_sendMessage( recipient, data, len ); #else
#endif return RF24_sendMessage(to, data, len);
#endif
return status;
} }
bool transportAvailable() { bool transportAvailable(void)
bool avail = RF24_isDataAvailable(); {
return avail; #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(); return RF24_sanityCheck();
} }
uint8_t transportReceive(void* data) { uint8_t transportReceive(void* data)
uint8_t len = RF24_readMessage(data); {
#if defined(MY_RF24_ENABLE_ENCRYPTION) uint8_t len = 0;
// has to be adjusted, WIP! #if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
_aes.set_IV(0); transportQueuedMessage* msg = transportRxQueue.getBack();
// decrypt data if (msg) {
_aes.cbc_decrypt((byte*)(data), (byte*)(data), len>16?2:1); len = msg->m_len;
#endif (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; return len;
} }
void transportPowerDown() { void transportPowerDown(void)
{
RF24_powerDown(); RF24_powerDown();
} }
+29 -19
View File
@@ -26,51 +26,61 @@ RFM69 _radio(MY_RF69_SPI_CS, MY_RF69_IRQ_PIN, MY_RFM69HW, MY_RF69_IRQ_NUM);
uint8_t _address; uint8_t _address;
bool transportInit() { bool transportInit(void)
{
// Start up the radio library (_address will be set later by the MySensors library) // Start up the radio library (_address will be set later by the MySensors library)
if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) { if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) {
#ifdef MY_RFM69_ENABLE_ENCRYPTION #ifdef MY_RFM69_ENABLE_ENCRYPTION
uint8_t _psk[16]; uint8_t _psk[16];
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
_radio.encrypt((const char*)_psk); _radio.encrypt((const char*)_psk);
memset(_psk, 0, 16); // Make sure it is purged from memory when set memset(_psk, 0, 16); // Make sure it is purged from memory when set
#endif #endif
return true; return true;
} }
return false; return false;
} }
void transportSetAddress(uint8_t address) { void transportSetAddress(const uint8_t address)
{
_address = address; _address = address;
_radio.setAddress(address); _radio.setAddress(address);
} }
uint8_t transportGetAddress() { uint8_t transportGetAddress(void)
{
return _address; 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); return _radio.sendWithRetry(to,data,len);
} }
bool transportAvailable() { bool transportAvailable(void)
{
return _radio.receiveDone(); return _radio.receiveDone();
} }
bool transportSanityCheck() { bool transportSanityCheck(void)
{
// not implemented yet // not implemented yet
return true; return true;
} }
uint8_t transportReceive(void* data) { uint8_t transportReceive(void* data)
{
memcpy(data,(const void *)_radio.DATA, _radio.DATALEN); 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 // Send ack back if this message wasn't a broadcast
if (_radio.TARGETID != RF69_BROADCAST_ADDR) if(_radio.ACKRequested()) {
_radio.ACKRequested(); _radio.sendACK();
_radio.sendACK(); }
return _radio.DATALEN; return dataLen;
} }
void transportPowerDown() { void transportPowerDown(void)
{
_radio.sleep(); _radio.sleep();
} }
+92
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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();
}
// **********************************************
+188 -177
View File
@@ -57,23 +57,21 @@
#include "MyTransport.h" #include "MyTransport.h"
#include <stdint.h> #include <stdint.h>
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h> #include <Arduino.h>
#else
#include <WProgram.h>
#endif
#include "MyTransport.h" #include "MyTransport.h"
#ifdef __linux__
#include "SerialPort.h"
#endif
#if defined(MY_RS485_DE_PIN) #if defined(MY_RS485_DE_PIN)
#define assertDE() digitalWrite(MY_RS485_DE_PIN, HIGH); delayMicroseconds(5) #define assertDE() hwDigitalWrite(MY_RS485_DE_PIN, HIGH); delayMicroseconds(5)
#define deassertDE() digitalWrite(MY_RS485_DE_PIN, LOW) #define deassertDE() hwDigitalWrite(MY_RS485_DE_PIN, LOW)
#else #else
#define assertDE() #define assertDE()
#define deassertDE() #define deassertDE()
#endif #endif
// We only use SYS_PACK in this application // We only use SYS_PACK in this application
@@ -92,8 +90,14 @@ unsigned char _recSender;
unsigned char _recCS; unsigned char _recCS;
unsigned char _recCalcCS; 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; unsigned char _nodeId;
char _data[MY_RS485_MAX_MESSAGE_LENGTH]; char _data[MY_RS485_MAX_MESSAGE_LENGTH];
@@ -110,13 +114,14 @@ bool _packet_received;
//Reset the state machine and release the data pointer //Reset the state machine and release the data pointer
void _serialReset(){ void _serialReset()
_recPhase = 0; {
_recPos = 0; _recPhase = 0;
_recLen = 0; _recPos = 0;
_recCommand = 0; _recLen = 0;
_recCS = 0; _recCommand = 0;
_recCalcCS = 0; _recCS = 0;
_recCalcCS = 0;
} }
// This is the main reception state machine. Progress through the states // This is the main reception state machine. Progress through the states
@@ -128,120 +133,118 @@ void _serialReset(){
// function. // function.
bool _serialProcess() bool _serialProcess()
{ {
char inch; char inch;
unsigned char i; unsigned char i;
if (!_dev.available()) return false; if (!_dev.available()) {
return false;
}
while(_dev.available()) { while(_dev.available()) {
inch = _dev.read(); inch = _dev.read();
switch(_recPhase) { switch(_recPhase) {
// Case 0 looks for the header. Bytes arrive in the serial interface and get // 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 // 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 // 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. // our ID, save the header information and progress to the next state.
case 0: case 0:
memcpy(&_header[0],&_header[1],5); memcpy(&_header[0],&_header[1],5);
_header[5] = inch; _header[5] = inch;
if ((_header[0] == SOH) && (_header[5] == STX) && (_header[1] != _header[2])) { if ((_header[0] == SOH) && (_header[5] == STX) && (_header[1] != _header[2])) {
_recCalcCS = 0; _recCalcCS = 0;
_recStation = _header[1]; _recStation = _header[1];
_recSender = _header[2]; _recSender = _header[2];
_recCommand = _header[3]; _recCommand = _header[3];
_recLen = _header[4]; _recLen = _header[4];
for (i=1; i<=4; i++) { for (i=1; i<=4; i++) {
_recCalcCS += _header[i]; _recCalcCS += _header[i];
} }
_recPhase = 1; _recPhase = 1;
_recPos = 0; _recPos = 0;
//Check if we should process this message //Check if we should process this message
//We reject the message if we are the sender //We reject the message if we are the sender
//We reject if we are not the receiver and message is not a broadcast //We reject if we are not the receiver and message is not a broadcast
if ((_recSender == _nodeId) || if ((_recSender == _nodeId) ||
(_recStation != _nodeId && (_recStation != _nodeId &&
_recStation != BROADCAST_ADDRESS)) { _recStation != BROADCAST_ADDRESS)) {
_dev.print(" wrongid: "); _serialReset();
_dev.print(_recStation); break;
_dev.print(" - "); }
_dev.println(_nodeId);
_serialReset();
break;
}
if (_recLen == 0) { if (_recLen == 0) {
_recPhase = 2; _recPhase = 2;
} }
} }
break; break;
// Case 1 receives the data portion of the packet. Read in "_recLen" number // Case 1 receives the data portion of the packet. Read in "_recLen" number
// of bytes and store them in the _data array. // of bytes and store them in the _data array.
case 1: case 1:
_data[_recPos++] = inch; _data[_recPos++] = inch;
_recCalcCS += inch; _recCalcCS += inch;
if (_recPos == _recLen) { if (_recPos == _recLen) {
_recPhase = 2; _recPhase = 2;
} }
break; break;
// After the data comes a single ETX character. Do we have it? If not, // 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. // reset the state machine to default and start looking for a new header.
case 2: case 2:
// Packet properly terminated? // Packet properly terminated?
if (inch == ETX) { if (inch == ETX) {
_recPhase = 3; _recPhase = 3;
} else { } else {
_serialReset(); _serialReset();
} }
break; break;
// Next comes the checksum. We have already calculated it from the incoming // Next comes the checksum. We have already calculated it from the incoming
// data, so just store the incoming checksum byte for later. // data, so just store the incoming checksum byte for later.
case 3: case 3:
_recCS = inch; _recCS = inch;
_recPhase = 4; _recPhase = 4;
break; break;
// The final state - check the last character is EOT and that the checksum matches. // 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. // If that test passes, then look for a valid command callback to execute.
// Execute it if found. // Execute it if found.
case 4: case 4:
if (inch == EOT) { if (inch == EOT) {
if (_recCS == _recCalcCS) { if (_recCS == _recCalcCS) {
// First, check for system level commands. It is possible // First, check for system level commands. It is possible
// to register your own callback as well for system level // to register your own callback as well for system level
// commands which will be called after the system default // commands which will be called after the system default
// hook. // hook.
switch (_recCommand) { switch (_recCommand) {
case ICSC_SYS_PACK: case ICSC_SYS_PACK:
_packet_from = _recSender; _packet_from = _recSender;
_packet_len = _recLen; _packet_len = _recLen;
_packet_received = true; _packet_received = true;
break; break;
} }
} }
} }
//Clear the data //Clear the data
_serialReset(); _serialReset();
//Return true, we have processed one command //Return true, we have processed one command
return true; return true;
break; break;
} }
} }
return true; 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<char const *>(data); const char *datap = static_cast<char const *>(data);
unsigned char i; unsigned char i;
unsigned char cs = 0; unsigned char cs = 0;
unsigned char del; unsigned char del;
// This is how many times to try and transmit before failing. // This is how many times to try and transmit before failing.
unsigned char timeout = 10; 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) #if defined(MY_RS485_DE_PIN)
digitalWrite(MY_RS485_DE_PIN, HIGH); hwDigitalWrite(MY_RS485_DE_PIN, HIGH);
delayMicroseconds(5); delayMicroseconds(5);
#endif #endif
// Start of header by writing multiple SOH // Start of header by writing multiple SOH
for(byte w=0;w<1;w++) _dev.write(SOH); for(byte w=0; w<1; w++) {
_dev.write(to); // Destination address _dev.write(SOH);
cs += to; }
_dev.write(_nodeId); // Source address _dev.write(to); // Destination address
cs += _nodeId; cs += to;
_dev.write(ICSC_SYS_PACK); // Command code _dev.write(_nodeId); // Source address
cs += ICSC_SYS_PACK; cs += _nodeId;
_dev.write(len); // Length of text _dev.write(ICSC_SYS_PACK); // Command code
cs += len; cs += ICSC_SYS_PACK;
_dev.write(STX); // Start of text _dev.write(len); // Length of text
for(i=0; i<len; i++) { cs += len;
_dev.write(datap[i]); // Text bytes _dev.write(STX); // Start of text
cs += datap[i]; for(i=0; i<len; i++) {
} _dev.write(datap[i]); // Text bytes
_dev.write(ETX); // End of text cs += datap[i];
_dev.write(cs); }
_dev.write(EOT); _dev.write(ETX); // End of text
_dev.write(cs);
_dev.write(EOT);
#if defined(MY_RS485_DE_PIN) #if defined(MY_RS485_DE_PIN)
#ifdef __PIC32MX__ #ifdef __PIC32MX__
// MPIDE has nothing yet for this. It uses the hardware buffer, which // MPIDE has nothing yet for this. It uses the hardware buffer, which
// could be up to 8 levels deep. For now, let's just delay for 8 // could be up to 8 levels deep. For now, let's just delay for 8
// characters worth. // characters worth.
delayMicroseconds((F_CPU/9600)+1); delayMicroseconds((F_CPU/9600)+1);
#else #else
#if defined(ARDUINO) && ARDUINO >= 100 #if defined(ARDUINO) && ARDUINO >= 100
#if ARDUINO >= 104 #if ARDUINO >= 104
// Arduino 1.0.4 and upwards does it right // Arduino 1.0.4 and upwards does it right
_dev.flush(); _dev.flush();
#else #else
// Between 1.0.0 and 1.0.3 it almost does it - need to compensate // 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. // for the hardware buffer. Delay for 2 bytes worth of transmission.
_dev.flush(); _dev.flush();
delayMicroseconds((20000000UL/9600)+1); delayMicroseconds((20000000UL/9600)+1);
#endif #endif
#endif #elif defined(__linux__)
#endif _dev.flush();
digitalWrite(MY_RS485_DE_PIN, LOW); #endif
#endif #endif
return true; hwDigitalWrite(MY_RS485_DE_PIN, LOW);
#endif
return true;
} }
bool transportInit() { bool transportInit(void)
// Reset the state machine {
// Reset the state machine
_dev.begin(MY_RS485_BAUD_RATE); _dev.begin(MY_RS485_BAUD_RATE);
_serialReset(); _serialReset();
#if defined(MY_RS485_DE_PIN) #if defined(MY_RS485_DE_PIN)
pinMode(MY_RS485_DE_PIN, OUTPUT); hwPinMode(MY_RS485_DE_PIN, OUTPUT);
digitalWrite(MY_RS485_DE_PIN, LOW); hwDigitalWrite(MY_RS485_DE_PIN, LOW);
#endif #endif
return true; return true;
} }
void transportSetAddress(uint8_t address) { void transportSetAddress(const uint8_t address)
{
_nodeId = address; _nodeId = address;
} }
uint8_t transportGetAddress() { uint8_t transportGetAddress(void)
{
return _nodeId; return _nodeId;
} }
bool transportAvailable() { bool transportAvailable(void)
{
_serialProcess(); _serialProcess();
return _packet_received; return _packet_received;
} }
bool transportSanityCheck() { bool transportSanityCheck(void)
{
// not implemented yet // not implemented yet
return true; return true;
} }
uint8_t transportReceive(void* data) { uint8_t transportReceive(void* data)
{
if (_packet_received) { if (_packet_received) {
memcpy(data,_data,_packet_len); memcpy(data,_data,_packet_len);
_packet_received = false; _packet_received = false;
return _packet_len; return _packet_len;
} } else {
else {
return (0); return (0);
} }
} }
void transportPowerDown() { void transportPowerDown(void)
{
// Nothing to shut down here // Nothing to shut down here
} }
+19 -1
View File
@@ -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 <henrik.ekblad@mysensors.org>
* 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 * This file defines the Sensor library version number
* Normally, contributors should not modify this directly * Normally, contributors should not modify this directly
@@ -6,6 +24,6 @@
#ifndef Version_h #ifndef Version_h
#define Version_h #define Version_h
#define MYSENSORS_LIBRARY_VERSION "2.0.0" #define MYSENSORS_LIBRARY_VERSION "2.1.0"
#endif #endif
+324 -291
View File
@@ -36,13 +36,13 @@
/* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down /* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down
and tailored to 8-bit microcontroller abilities and Arduino datatypes. 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 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 other transformations are coded to save table space. Many efficiency
improvments to the routines mix_sub_columns() and inv_mix_sub_columns() improvments to the routines mix_sub_columns() and inv_mix_sub_columns()
(mainly common sub-expression elimination). (mainly common sub-expression elimination).
Only the routines with precalculated subkey schedule are retained (together 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) RAM, alas)
The CBC routines side-effect the iv argument (so that successive calls work The CBC routines side-effect the iv argument (so that successive calls work
@@ -65,84 +65,80 @@
#define WPOLY 0x011B #define WPOLY 0x011B
#define DPOLY 0x008D #define DPOLY 0x008D
const static byte s_fwd [0x100] PROGMEM = const static byte s_fwd [0x100] PROGMEM = {
{ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
} ; } ;
const static byte s_inv [0x100] PROGMEM = const static byte s_inv [0x100] PROGMEM = {
{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
} ; } ;
// times 2 in the GF(2^8) // 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)) #define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0))
static byte s_box (byte x) static byte s_box (byte x)
{ {
// return fwd_affine (pgm_read_byte (&inv [x])) ; // return fwd_affine (pgm_read_byte (&inv [x])) ;
return pgm_read_byte (& s_fwd [x]) ; return pgm_read_byte (& s_fwd [x]) ;
} }
// Inverse Sbox // Inverse Sbox
static byte is_box (byte x) static byte is_box (byte x)
{ {
// return pgm_read_byte (&inv [inv_affine (x)]) ; // return pgm_read_byte (&inv [inv_affine (x)]) ;
return pgm_read_byte (& s_inv [x]) ; return pgm_read_byte (& s_inv [x]) ;
} }
static void xor_block (byte * d, byte * s) static void xor_block (byte * d, byte * s)
{ {
for (byte i = 0 ; i < N_BLOCK ; i += 4) for (byte i = 0 ; i < N_BLOCK ; i += 4) {
{ *d++ ^= *s++ ; // some unrolling
*d++ ^= *s++ ; // some unrolling *d++ ^= *s++ ;
*d++ ^= *s++ ; *d++ ^= *s++ ;
*d++ ^= *s++ ; *d++ ^= *s++ ;
*d++ ^= *s++ ; }
}
} }
static void copy_and_key (byte * d, byte * s, byte * k) static void copy_and_key (byte * d, byte * s, byte * k)
{ {
for (byte i = 0 ; i < N_BLOCK ; i += 4) for (byte i = 0 ; i < N_BLOCK ; i += 4) {
{ *d++ = *s++ ^ *k++ ; // some unrolling
*d++ = *s++ ^ *k++ ; // some unrolling *d++ = *s++ ^ *k++ ;
*d++ = *s++ ^ *k++ ; *d++ = *s++ ^ *k++ ;
*d++ = *s++ ^ *k++ ; *d++ = *s++ ^ *k++ ;
*d++ = *s++ ^ *k++ ; }
}
} }
// #define add_round_key(d, k) xor_block (d, 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]) static void shift_sub_rows (byte st [N_BLOCK])
{ {
st [0] = s_box (st [0]) ; st [4] = s_box (st [4]) ; st [0] = s_box (st [0]) ;
st [8] = s_box (st [8]) ; st [12] = s_box (st [12]) ; st [4] = s_box (st [4]) ;
st [8] = s_box (st [8]) ;
st [12] = s_box (st [12]) ;
byte tt = st [1] ; byte tt = st [1] ;
st [1] = s_box (st [5]) ; st [5] = s_box (st [9]) ; st [1] = s_box (st [5]) ;
st [9] = s_box (st [13]) ; st [13] = s_box (tt) ; 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[2] ;
tt = st[6] ; st [6] = s_box (st [14]) ; st [14] = s_box (tt) ; 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] ; tt = st[15] ;
st [15] = s_box (st [11]) ; st [11] = s_box (st [7]) ; st [15] = s_box (st [11]) ;
st [7] = s_box (st [3]) ; st [3] = s_box (tt) ; 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]) static void inv_shift_sub_rows (byte st[N_BLOCK])
{ {
st [0] = is_box (st[0]) ; st [4] = is_box (st [4]); st [0] = is_box (st[0]) ;
st [8] = is_box (st[8]) ; st [12] = is_box (st [12]); st [4] = is_box (st [4]);
st [8] = is_box (st[8]) ;
st [12] = is_box (st [12]);
byte tt = st[13] ; byte tt = st[13] ;
st [13] = is_box (st [9]) ; st [9] = is_box (st [5]) ; st [13] = is_box (st [9]) ;
st [5] = is_box (st [1]) ; st [1] = is_box (tt) ; 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 [2] ;
tt = st [6] ; st [6] = is_box (st [14]) ; st [14] = is_box (tt) ; 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] ; tt = st [3] ;
st [3] = is_box (st [7]) ; st [7] = is_box (st [11]) ; st [3] = is_box (st [7]) ;
st [11] = is_box (st [15]) ; st [15] = is_box (tt) ; st [7] = is_box (st [11]) ;
st [11] = is_box (st [15]) ;
st [15] = is_box (tt) ;
} }
/* SUB COLUMNS PHASE */ /* SUB COLUMNS PHASE */
static void mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK]) static void mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
{ {
byte j = 5 ; byte j = 5 ;
byte k = 10 ; byte k = 10 ;
byte l = 15 ; byte l = 15 ;
for (byte i = 0 ; i < N_BLOCK ; i += N_COL) for (byte i = 0 ; i < N_BLOCK ; i += N_COL) {
{ byte a = st [i] ;
byte a = st [i] ; byte b = st [j] ;
byte b = st [j] ; j = (j+N_COL) & 15 ; j = (j+N_COL) & 15 ;
byte c = st [k] ; k = (k+N_COL) & 15 ; byte c = st [k] ;
byte d = st [l] ; l = (l+N_COL) & 15 ; k = (k+N_COL) & 15 ;
byte a1 = s_box (a), b1 = s_box (b), c1 = s_box (c), d1 = s_box (d) ; byte d = st [l] ;
byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ; l = (l+N_COL) & 15 ;
dt[i] = a2 ^ b2^b1 ^ c1 ^ d1 ; byte a1 = s_box (a), b1 = s_box (b), c1 = s_box (c), d1 = s_box (d) ;
dt[i+1] = a1 ^ b2 ^ c2^c1 ^ d1 ; byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
dt[i+2] = a1 ^ b1 ^ c2 ^ d2^d1 ; dt[i] = a2 ^ b2^b1 ^ c1 ^ d1 ;
dt[i+3] = a2^a1 ^ b1 ^ c1 ^ d2 ; 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]) static void inv_mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
{ {
for (byte i = 0 ; i < N_BLOCK ; i += N_COL) for (byte i = 0 ; i < N_BLOCK ; i += N_COL) {
{ byte a1 = st [i] ;
byte a1 = st [i] ; byte b1 = st [i+1] ;
byte b1 = st [i+1] ; byte c1 = st [i+2] ;
byte c1 = st [i+2] ; byte d1 = st [i+3] ;
byte d1 = st [i+3] ; byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
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 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 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 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 ;
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] = is_box (ac^a2 ^ b9^b2 ^ cc^c1 ^ d9) ; dt[(i+5)&15] = is_box (a9 ^ bc^b2 ^ c9^c2 ^ dc^d1) ;
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+10)&15] = is_box (ac^a1 ^ b9 ^ cc^c2 ^ d9^d2) ; dt[(i+15)&15] = is_box (a9^a2 ^ bc^b1 ^ c9 ^ dc^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 }; 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,ar_iv,8);
memcpy(iv+8,ar_iv,8); memcpy(iv+8,ar_iv,8);
arr_pad[0] = 0x01; arr_pad[0] = 0x01;
arr_pad[1] = 0x02; arr_pad[1] = 0x02;
arr_pad[2] = 0x03; arr_pad[2] = 0x03;
arr_pad[3] = 0x04; arr_pad[3] = 0x04;
@@ -253,200 +276,196 @@ AES::AES(){
byte AES::set_key (byte key [], int keylen) byte AES::set_key (byte key [], int keylen)
{ {
byte hi ; byte hi ;
switch (keylen) switch (keylen) {
{ case 16:
case 16: case 128:
case 128: keylen = 16; // 10 rounds
keylen = 16; // 10 rounds round = 10 ;
round = 10 ; break;
break; case 24:
case 24: case 192:
case 192: keylen = 24; // 12 rounds
keylen = 24; // 12 rounds round = 12 ;
round = 12 ; break;
break; case 32:
case 32: case 256:
case 256: keylen = 32; // 14 rounds
keylen = 32; // 14 rounds round = 14 ;
round = 14 ; break;
break; default:
default: round = 0;
round = 0; return AES_FAILURE;
return AES_FAILURE; }
} hi = (round + 1) << 4 ;
hi = (round + 1) << 4 ; copy_n_bytes (key_sched, key, keylen) ;
copy_n_bytes (key_sched, key, keylen) ; byte t[4] ;
byte t[4] ; byte next = keylen ;
byte next = keylen ; for (byte cc = keylen, rc = 1 ; cc < hi ; cc += N_COL) {
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] ;
for (byte i = 0 ; i < N_COL ; i++) }
t[i] = key_sched [cc-4+i] ; if (cc == next) {
if (cc == next) next += keylen ;
{ byte ttt = t[0] ;
next += keylen ; t[0] = s_box (t[1]) ^ rc ;
byte ttt = t[0] ; t[1] = s_box (t[2]) ;
t[0] = s_box (t[1]) ^ rc ; t[2] = s_box (t[3]) ;
t[1] = s_box (t[2]) ; t[3] = s_box (ttt) ;
t[2] = s_box (t[3]) ; rc = f2 (rc) ;
t[3] = s_box (ttt) ; } else if (keylen == 32 && (cc & 31) == 16) {
rc = f2 (rc) ; for (byte i = 0 ; i < 4 ; i++) {
} t[i] = s_box (t[i]) ;
else if (keylen == 32 && (cc & 31) == 16) }
{ }
for (byte i = 0 ; i < 4 ; i++) byte tt = cc - keylen ;
t[i] = s_box (t[i]) ; for (byte i = 0 ; i < N_COL ; i++) {
} key_sched [cc + i] = key_sched [tt + i] ^ 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 ;
}
return AES_SUCCESS ;
} }
/******************************************************************************/ /******************************************************************************/
void AES::clean () void AES::clean ()
{ {
for (byte i = 0 ; i < KEY_SCHEDULE_BYTES ; i++) for (byte i = 0 ; i < KEY_SCHEDULE_BYTES ; i++) {
key_sched [i] = 0 ; key_sched [i] = 0 ;
round = 0 ; }
round = 0 ;
} }
/******************************************************************************/ /******************************************************************************/
void AES::copy_n_bytes (byte * d, byte * s, byte nn) void AES::copy_n_bytes (byte * d, byte * s, byte nn)
{ {
while (nn >= 4) while (nn >= 4) {
{ *d++ = *s++ ; // some unrolling
*d++ = *s++ ; // some unrolling *d++ = *s++ ;
*d++ = *s++ ; *d++ = *s++ ;
*d++ = *s++ ; *d++ = *s++ ;
*d++ = *s++ ; nn -= 4 ;
nn -= 4 ; }
} while (nn--) {
while (nn--) *d++ = *s++ ;
*d++ = *s++ ; }
} }
/******************************************************************************/ /******************************************************************************/
byte AES::encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) byte AES::encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
{ {
if (round) if (round) {
{ byte s1 [N_BLOCK], r ;
byte s1 [N_BLOCK], r ; copy_and_key (s1, plain, (byte*) (key_sched)) ;
copy_and_key (s1, plain, (byte*) (key_sched)) ;
for (r = 1 ; r < round ; r++) for (r = 1 ; r < round ; r++) {
{ byte s2 [N_BLOCK] ;
byte s2 [N_BLOCK] ; mix_sub_columns (s2, s1) ;
mix_sub_columns (s2, s1) ; copy_and_key (s1, s2, (byte*) (key_sched + r * N_BLOCK)) ;
copy_and_key (s1, s2, (byte*) (key_sched + r * N_BLOCK)) ; }
} shift_sub_rows (s1) ;
shift_sub_rows (s1) ; copy_and_key (cipher, s1, (byte*) (key_sched + r * N_BLOCK)) ;
copy_and_key (cipher, s1, (byte*) (key_sched + r * N_BLOCK)) ; } else {
} return AES_FAILURE ;
else }
return AES_FAILURE ; return AES_SUCCESS ;
return AES_SUCCESS ;
} }
/******************************************************************************/ /******************************************************************************/
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK]) byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK])
{ {
while (n_block--) while (n_block--) {
{ xor_block (iv, plain) ;
xor_block (iv, plain) ; if (encrypt (iv, iv) != AES_SUCCESS) {
if (encrypt (iv, iv) != AES_SUCCESS) return AES_FAILURE ;
return AES_FAILURE ; }
copy_n_bytes (cipher, iv, N_BLOCK) ; copy_n_bytes (cipher, iv, N_BLOCK) ;
plain += N_BLOCK ; plain += N_BLOCK ;
cipher += N_BLOCK ; cipher += N_BLOCK ;
} }
return AES_SUCCESS ; return AES_SUCCESS ;
} }
/******************************************************************************/ /******************************************************************************/
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block) byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block)
{ {
while (n_block--) while (n_block--) {
{ xor_block (iv, plain) ;
xor_block (iv, plain) ; if (encrypt (iv, iv) != AES_SUCCESS) {
if (encrypt (iv, iv) != AES_SUCCESS) return AES_FAILURE ;
return AES_FAILURE ; }
copy_n_bytes (cipher, iv, N_BLOCK) ; copy_n_bytes (cipher, iv, N_BLOCK) ;
plain += N_BLOCK ; plain += N_BLOCK ;
cipher += N_BLOCK ; cipher += N_BLOCK ;
} }
return AES_SUCCESS ; return AES_SUCCESS ;
} }
/******************************************************************************/ /******************************************************************************/
byte AES::decrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) byte AES::decrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
{ {
if (round) if (round) {
{ byte s1 [N_BLOCK] ;
byte s1 [N_BLOCK] ; copy_and_key (s1, plain, (byte*) (key_sched + round * N_BLOCK)) ;
copy_and_key (s1, plain, (byte*) (key_sched + round * N_BLOCK)) ; inv_shift_sub_rows (s1) ;
inv_shift_sub_rows (s1) ;
for (byte r = round ; --r ; ) for (byte r = round ; --r ; ) {
{ byte s2 [N_BLOCK] ;
byte s2 [N_BLOCK] ; copy_and_key (s2, s1, (byte*) (key_sched + r * N_BLOCK)) ;
copy_and_key (s2, s1, (byte*) (key_sched + r * N_BLOCK)) ; inv_mix_sub_columns (s1, s2) ;
inv_mix_sub_columns (s1, s2) ; }
} copy_and_key (cipher, s1, (byte*) (key_sched)) ;
copy_and_key (cipher, s1, (byte*) (key_sched)) ; } else {
} return AES_FAILURE ;
else }
return AES_FAILURE ; return AES_SUCCESS ;
return AES_SUCCESS ;
} }
/******************************************************************************/ /******************************************************************************/
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK]) byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK])
{ {
while (n_block--) while (n_block--) {
{ byte tmp [N_BLOCK] ;
byte tmp [N_BLOCK] ; copy_n_bytes (tmp, cipher, N_BLOCK) ;
copy_n_bytes (tmp, cipher, N_BLOCK) ; if (decrypt (cipher, plain) != AES_SUCCESS) {
if (decrypt (cipher, plain) != AES_SUCCESS) return AES_FAILURE ;
return AES_FAILURE ; }
xor_block (plain, iv) ; xor_block (plain, iv) ;
copy_n_bytes (iv, tmp, N_BLOCK) ; copy_n_bytes (iv, tmp, N_BLOCK) ;
plain += N_BLOCK ; plain += N_BLOCK ;
cipher += N_BLOCK; cipher += N_BLOCK;
} }
return AES_SUCCESS ; return AES_SUCCESS ;
} }
/******************************************************************************/ /******************************************************************************/
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block) byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block)
{ {
while (n_block--) while (n_block--) {
{ byte tmp [N_BLOCK] ;
byte tmp [N_BLOCK] ; copy_n_bytes (tmp, cipher, N_BLOCK) ;
copy_n_bytes (tmp, cipher, N_BLOCK) ; if (decrypt (cipher, plain) != AES_SUCCESS) {
if (decrypt (cipher, plain) != AES_SUCCESS) return AES_FAILURE ;
return AES_FAILURE ; }
xor_block (plain, iv) ; xor_block (plain, iv) ;
copy_n_bytes (iv, tmp, N_BLOCK) ; copy_n_bytes (iv, tmp, N_BLOCK) ;
plain += N_BLOCK ; plain += N_BLOCK ;
cipher += N_BLOCK; cipher += N_BLOCK;
} }
return AES_SUCCESS ; 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,&IVCl,8);
memcpy(iv+8,&IVCl,8); memcpy(iv+8,&IVCl,8);
IVC = IVCl; 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; IVC += 1;
memcpy(iv,&IVC,8); memcpy(iv,&IVC,8);
memcpy(iv+8,&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; return size;
} }
/******************************************************************************/ /******************************************************************************/
void AES::set_size(int sizel){ void AES::set_size(int sizel)
{
size = sizel; size = sizel;
} }
/******************************************************************************/ /******************************************************************************/
void AES::get_IV(byte *out){ void AES::get_IV(byte *out)
{
memcpy(out,&IVC,8); memcpy(out,&IVC,8);
memcpy(out+8,&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; int s_of_p = p_size - 1;
if ( s_of_p % N_BLOCK == 0){ if ( s_of_p % N_BLOCK == 0) {
size = s_of_p; size = s_of_p;
}else{ } else {
size = s_of_p + (N_BLOCK-(s_of_p % N_BLOCK)); size = s_of_p + (N_BLOCK-(s_of_p % N_BLOCK));
} }
pad = size - s_of_p; 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) void AES::padPlaintext(void* in,byte* out)
{ {
memcpy(out,in,size); 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]; out[i] = arr_pad[pad - 1];
} }
} }
/******************************************************************************/ /******************************************************************************/
bool AES::CheckPad(byte* in,int lsize){ bool AES::CheckPad(byte* in,int lsize)
if (in[lsize-1] <= 0x0f){ {
if (in[lsize-1] <= 0x0f) {
int lpad = (int)in[lsize-1]; int lpad = (int)in[lsize-1];
for (int i = lsize - 1; i >= lsize-lpad; i--){ for (int i = lsize - 1; i >= lsize-lpad; i--) {
if (arr_pad[lpad - 1] != in[i]){ if (arr_pad[lpad - 1] != in[i]) {
return false; return false;
} }
} }
}else{ } else {
return true; return true;
} }
return true; return true;
} }
/******************************************************************************/ /******************************************************************************/
void AES::printArray(byte output[],bool p_pad) void AES::printArray(byte output[],bool p_pad)
{ {
uint8_t i,j; uint8_t i,j;
uint8_t loops = size/N_BLOCK; uint8_t loops = size/N_BLOCK;
uint8_t outp = N_BLOCK; uint8_t outp = N_BLOCK;
for (j = 0; j < loops; j += 1){ for (j = 0; j < loops; j += 1) {
if (p_pad && (j == (loops - 1)) ) { outp = N_BLOCK - pad; } if (p_pad && (j == (loops - 1)) ) {
for (i = 0; i < outp; i++) outp = N_BLOCK - pad;
{ }
printf_P(PSTR("%c"),output[j*N_BLOCK + i]); for (i = 0; i < outp; i++) {
} printf_P(PSTR("%c"),output[j*N_BLOCK + i]);
} }
printf_P(PSTR("\n")); }
printf_P(PSTR("\n"));
} }
/******************************************************************************/ /******************************************************************************/
void AES::printArray(byte output[],int sizel) void AES::printArray(byte output[],int sizel)
{ {
for (int i = 0; i < sizel; i++) for (int i = 0; i < sizel; i++) {
{ printf_P(PSTR("%x"),output[i]);
printf_P(PSTR("%x"),output[i]); }
} printf_P(PSTR("\n"));
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); calc_size_n_pad(size_p);
byte plain_p[get_size()]; byte plain_p[get_size()];
padPlaintext(plain,plain_p); 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); calc_size_n_pad(size_p);
byte plain_p[get_size()]; byte plain_p[get_size()];
padPlaintext(plain,plain_p); 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); set_size(size_c);
int blocks = size_c / N_BLOCK; int blocks = size_c / N_BLOCK;
set_key (key, bits); 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); set_size(size_c);
int blocks = size_c / N_BLOCK; int blocks = size_c / N_BLOCK;
set_key (key, bits); 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) #if defined(AES_LINUX)
double AES::millis(){ double AES::millis()
{
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
return (tv.tv_sec + 0.000001 * tv.tv_usec); return (tv.tv_sec + 0.000001 * tv.tv_usec);
} }
+93 -128
View File
@@ -31,44 +31,44 @@
This is an AES implementation that uses only 8-bit byte operations on the This is an AES implementation that uses only 8-bit byte operations on the
cipher state. cipher state.
*/ */
/* code was modified by george spanos <spaniakos@gmail.com> /* code was modified by george spanos <spaniakos@gmail.com>
* 16/12/14 * 16/12/14
*/ */
/** AES class */ /** AES class */
class AES 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 NOTE: If the length_type used for the key length is an
unsigned 8-bit character, a key length of 256 bits must unsigned 8-bit character, a key length of 256 bits must
be entered as a length in bytes (valid inputs are hence be entered as a length in bytes (valid inputs are hence
128, 192, 16, 24 and 32). 128, 192, 16, 24 and 32).
*/ */
/** \fn AES() /** \fn AES()
* \brief AES constructor * \brief AES constructor
* *
* This function initialized an instance of AES. * This function initialized an instance of AES.
*/ */
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 key[] pointer to the key string.
* @param keylen Integer that indicates the length of the key. * @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, * @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 * a key length of 256 bits must be entered as a length in bytes
* (valid inputs are hence 128, 192, 16, 24 and 32). * (valid inputs are hence 128, 192, 16, 24 and 32).
* *
*/ */
byte set_key (byte key[], int keylen) ; byte set_key (byte key[], int keylen) ;
/** clean up subkeys after use. /** clean up subkeys after use.
* *
*/ */
void clean () ; // delete key schedule after use void clean () ; // delete key schedule after use
/** copying and xoring utilities. /** copying and xoring utilities.
* @param *AESt byte pointer of the AEStination array. * @param *AESt byte pointer of the AEStination array.
* @param *src byte pointer of the source 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]) ; byte encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) ;
/** CBC encrypt a number of blocks (input and return an IV). /** CBC encrypt a number of blocks (input and return an IV).
* *
* @param *plain Pointer, points to the plaintex. * @param *plain Pointer, points to the plaintex.
* @param *cipher Pointer, points to the ciphertext that will be created. * @param *cipher Pointer, points to the ciphertext that will be created.
* @param n_block integer, indicated the number of blocks to be ciphered. * @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]) ; 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). /** CBC encrypt a number of blocks (input and return an IV).
* *
* @param *plain Pointer, points to the plaintex. * @param *plain Pointer, points to the plaintex.
* @param *cipher Pointer, points to the ciphertext that will be created. * @param *cipher Pointer, points to the ciphertext that will be created.
* @param n_block integer, indicated the number of blocks to be ciphered. * @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) ; 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 cipher Array of the ciphertext.
* @param plain Array of the plaintext. * @param plain Array of the plaintext.
* @note The N_BLOCK is defined in AES_config.h as, * @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]) ; 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 *cipher Pointer, points to the ciphertext that will be created.
* @param *plain Pointer, points to the plaintex. * @param *plain Pointer, points to the plaintex.
* @param n_block integer, indicated the number of blocks to be ciphered. * @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]) ; 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 *cipher Pointer, points to the ciphertext that will be created.
* @param *plain Pointer, points to the plaintex. * @param *plain Pointer, points to the plaintex.
* @param n_block integer, indicated the number of blocks to be ciphered. * @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) ; byte cbc_decrypt (byte * cipher, byte * plain, int n_block) ;
/** Sets IV (initialization vector) and IVC (IV counter). /** Sets IV (initialization vector) and IVC (IV counter).
* This function changes the ivc and iv variables needed for AES. * 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 * @code unsigned long long int my_iv = 01234567; @endcode
*/ */
void set_IV(unsigned long long int IVCl); void set_IV(unsigned long long int IVCl);
/** increase the iv (initialization vector) and IVC (IV counter) by 1 /** 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 * This function increased the VI by one step in order to have a different IV each time
* *
*/ */
void iv_inc(); void iv_inc();
/** Getter method for size /** Getter method for size
* *
* This function return the size * This function return the size
* @return an integer, that is the size of the of the padded plaintext, * @return an integer, that is the size of the of the padded plaintext,
* thus, the size of the ciphertext. * thus, the size of the ciphertext.
*/ */
int get_size(); int get_size();
/** Setter method for size /** Setter method for size
* *
* This function sets the size of the plaintext+pad * This function sets the size of the plaintext+pad
* *
*/ */
void set_size(int sizel); void set_size(int sizel);
/** Getter method for IV /** Getter method for IV
* *
* This function return the IV * This function return the IV
* @param out byte pointer that gets the IV. * @param out byte pointer that gets the IV.
* @return none, the IV is writed to the out pointer. * @return none, the IV is writed to the out pointer.
*/ */
void get_IV(byte *out); void get_IV(byte *out);
/** Calculates the size of the plaintext and the padding. /** Calculates the size of the plaintext and the padding.
* *
* Calculates the size of theplaintext with 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. * 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) * @param p_size the size of the byte array ex sizeof(plaintext)
*/ */
void calc_size_n_pad(int p_size); void calc_size_n_pad(int p_size);
/** Pads the plaintext /** Pads the plaintext
* *
* This function pads the plaintext and returns an char array with the * 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 * plaintext and the padding in order for the plaintext to be compatible with
* 16bit size blocks required by AES * 16bit size blocks required by AES
* *
* @param in the string of the plaintext in a byte array * @param in the string of the plaintext in a byte array
* @param out The string of the out array. * @param out The string of the out array.
* @return no return, The padded plaintext is stored in the out pointer. * @return no return, The padded plaintext is stored in the out pointer.
*/ */
void padPlaintext(void* in,byte* out); void padPlaintext(void* in,byte* out);
/** Check the if the padding is correct. /** Check the if the padding is correct.
* *
* This functions checks the padding of the plaintext. * This functions checks the padding of the plaintext.
* *
* @param in the string of the plaintext in a byte array * @param in the string of the plaintext in a byte array
* @param size the size of the string * @param size the size of the string
* @return true if correct / false if not * @return true if correct / false if not
@@ -221,26 +221,26 @@ class AES
bool CheckPad(byte* in,int size); bool CheckPad(byte* in,int size);
/** Prints the array given. /** 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. * It is mainlly used for debugging purpuses or to output the string.
* *
* @param output[] the string of the text in a byte array * @param output[] the string of the text in a byte array
* @param p_pad optional, used to print with out the padding characters * @param p_pad optional, used to print with out the padding characters
*/ */
void printArray(byte output[],bool p_pad = true); void printArray(byte output[],bool p_pad = true);
/** Prints the array given. /** Prints the array given.
* *
* This function prints the given array in Hexadecimal. * This function prints the given array in Hexadecimal.
* *
* @param output[] the string of the text in a byte array * @param output[] the string of the text in a byte array
* @param sizel the size of the array. * @param sizel the size of the array.
*/ */
void printArray(byte output[],int sizel); void printArray(byte output[],int sizel);
/** User friendly implementation of AES-CBC encryption. /** User friendly implementation of AES-CBC encryption.
* *
* @param *plain pointer to the plaintext * @param *plain pointer to the plaintext
* @param size_p size of the plaintext * @param size_p size of the plaintext
* @param *cipher pointer to the ciphertext * @param *cipher pointer to the ciphertext
@@ -250,9 +250,9 @@ class AES
* @note The key will be stored in class variable. * @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]); 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. /** User friendly implementation of AES-CBC encryption.
* *
* @param *plain pointer to the plaintext * @param *plain pointer to the plaintext
* @param size_p size of the plaintext * @param size_p size of the plaintext
* @param *cipher pointer to the ciphertext * @param *cipher pointer to the ciphertext
@@ -261,9 +261,9 @@ class AES
* @note The key will be stored in class variable. * @note The key will be stored in class variable.
*/ */
void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits); void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits);
/** User friendly implementation of AES-CBC decryption. /** User friendly implementation of AES-CBC decryption.
* *
* @param *cipher pointer to the ciphertext * @param *cipher pointer to the ciphertext
* @param size_c size of the ciphertext * @param size_c size of the ciphertext
* @param *plain pointer to the plaintext * @param *plain pointer to the plaintext
@@ -273,9 +273,9 @@ class AES
* @note The key will be stored in class variable. * @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]); 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. /** User friendly implementation of AES-CBC decryption.
* *
* @param *cipher pointer to the ciphertext * @param *cipher pointer to the ciphertext
* @param size_c size of the ciphertext * @param size_c size of the ciphertext
* @param *plain pointer to the plaintext * @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); void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits);
#if defined(AES_LINUX) #if defined(AES_LINUX)
/** /**
* used in linux in order to retrieve the time in milliseconds. * used in linux in order to retrieve the time in milliseconds.
* *
* @return returns the milliseconds in a double format. * @return returns the milliseconds in a double format.
*/ */
double millis(); double millis();
#endif #endif
private: private:
int round ;/**< holds the number of rounds to be used. */ 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. */ byte key_sched [KEY_SCHEDULE_BYTES]
unsigned long long int IVC;/**< holds the initialization vector counter in numerical format. */ ;/**< holds the pre-computed key for the encryption/decrpytion. */
byte iv[16];/**< holds the initialization vector that will be used in the cipher. */ unsigned long long int IVC;/**< holds the initialization vector counter in numerical format. */
int pad;/**< holds the size of the padding. */ byte iv[16];/**< holds the initialization vector that will be used in the cipher. */
int size;/**< hold the size of the plaintext to be ciphered */ int pad;/**< holds the size of the padding. */
#if defined(AES_LINUX) int size;/**< hold the size of the plaintext to be ciphered */
#if defined(AES_LINUX)
timeval tv;/**< holds the time value on linux */ timeval tv;/**< holds the time value on linux */
byte arr_pad[15];/**< holds the hexadecimal padding values 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 */ 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 #endif
/**
* @example aes.pde
* <b>For Arduino</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* 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.<br />
*/
/**
* @example aes.cpp
* <b>For Rasberry pi</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* 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.<br />
*/
/**
* @example test_vectors.pde
* <b>For Arduino</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.<br />
* plus is a classical approach to the AES encryption library with out the easy to use add-on modifications.
*/
/**
* @example test_vectors.cpp
* <b>For Rasberry pi</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.<br />
* 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 * @defgroup aeslib AES library for Arduino and Raspberry pi
* @ingroup internals * @ingroup internals
@@ -362,21 +327,21 @@ class AES
* *
* @section Acknowledgements Acknowledgements * @section Acknowledgements Acknowledgements
* This is an AES library for the Arduino, based on tzikis's AES library, which you can find <a href= "https://github.com/tzikis/arduino">here:</a>.<br /> * This is an AES library for the Arduino, based on tzikis's AES library, which you can find <a href= "https://github.com/tzikis/arduino">here:</a>.<br />
* Tzikis library was based on scottmac`s library, which you can find <a href="https://github.com/scottmac/arduino">here:</a><br /> * Tzikis library was based on scottmac`s library, which you can find <a href="https://github.com/scottmac/arduino">here:</a><br />
* *
* @section Installation Installation * @section Installation Installation
* <h3>Arduino</h3> * <h3>Arduino</h3>
* 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.<br /> * libraries folder doesn't exist, create it. Then copy everything inside. (re)launch the Arduino IDE.<br />
* You're done. Time for a mojito * You're done. Time for a mojito
* *
* <h3>Raspberry pi</h3> * <h3>Raspberry pi</h3>
* <b>install</b><br /><br /> * <b>install</b><br /><br />
* *
* sudo make install<br /> * sudo make install<br />
* cd examples_Rpi<br /> * cd examples_Rpi<br />
* make<br /><br /> * make<br /><br />
* *
* <b>What to do after changes to the library</b><br /><br /> * <b>What to do after changes to the library</b><br /><br />
* sudo make clean<br /> * sudo make clean<br />
* sudo make install<br /> * sudo make install<br />
@@ -392,7 +357,7 @@ class AES
* <b>How to start a sketch</b><br /><br /> * <b>How to start a sketch</b><br /><br />
* cd examples_Rpi<br /> * cd examples_Rpi<br />
* sudo ./\<sketch\><br /><br /> * sudo ./\<sketch\><br /><br />
* *
* @section AesNews News * @section AesNews News
* *
* If issues are discovered with the documentation, please report them <a href="https://github.com/spaniakos/spaniakos.github.io/issues"> here</a> * If issues are discovered with the documentation, please report them <a href="https://github.com/spaniakos/spaniakos.github.io/issues"> here</a>
@@ -411,6 +376,6 @@ class AES
* - Arduino * - Arduino
* - Intel Galileo support * - Intel Galileo support
* - Raspberry Pi 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 * - The library has not been tested to other boards, but it should suppport ATMega 328 based boards,Mega Boards,Arduino Due,ATTiny board
*/ */
+17 -17
View File
@@ -7,34 +7,34 @@
#if (defined(__linux) || defined(linux)) && !defined(__ARDUINO_X86__) #if (defined(__linux) || defined(linux)) && !defined(__ARDUINO_X86__)
#define AES_LINUX #define AES_LINUX
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h> #include <unistd.h>
#else #else
#include <Arduino.h> #include <Arduino.h>
#endif #endif
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#if defined(__ARDUINO_X86__) || (defined (__linux) || defined (linux)) #if defined(__ARDUINO_X86__) || (defined (__linux) || defined (linux))
#undef PROGMEM #undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") )) #define PROGMEM __attribute__(( section(".progmem.data") ))
#define pgm_read_byte(p) (*(p)) #define pgm_read_byte(p) (*(p))
typedef unsigned char byte; typedef unsigned char byte;
#define printf_P printf #define printf_P printf
#define PSTR(x) (x) #define PSTR(x) (x)
#elif defined(ARDUINO_ARCH_ESP8266) #elif defined(ARDUINO_ARCH_ESP8266)
#include <pgmspace.h> #include <pgmspace.h>
#elif defined(ARDUINO_ARCH_SAMD) #elif defined(ARDUINO_ARCH_SAMD)
#define printf_P printf #define printf_P printf
#else #else
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#endif #endif
#define N_ROW 4 #define N_ROW 4
-2372
View File
File diff suppressed because it is too large Load Diff
-62
View File
@@ -1,62 +0,0 @@
#############################################################################
#
# Makefile for AES on Raspberry Pi
#
# License: GPL (General Public License)
# Author: Georgios Spanos (spaniakos) <spaniakos@gmail.com>
# Date: 02/12/2014
#
# Description:
# ------------
# use make all and mak install to install the library
# You can change the install directory by editing the LIBDIR line
#
PREFIX=/usr/local
# Library parameters
# where to put the lib
LIBDIR=$(PREFIX)/lib
# lib name
LIB=libAES
# shared library name
LIBNAME=$(LIB).so.1.0
# Where to put the header files
HEADER_DIR=${PREFIX}/include/AES
# The recommended compiler flags for the Raspberry Pi
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
# make all
# reinstall the library after each recompilation
all: libAES
# Make the library
libAES: AES.o
g++ -shared -Wl,-soname,$@.so.1 ${CCFLAGS} -o ${LIBNAME} $^
# Library parts
AES.o: AES.cpp
g++ -Wall -fPIC ${CCFLAGS} -c $^
# clear build files
clean:
rm -rf *.o ${LIB}.*
rm -rf ${LIBDIR}/${LIB}.*
rm -rf ${HEADER_DIR}
install: all install-libs install-headers
# Install the library to LIBPATH
install-libs:
@echo "[Installing Libs]"
@if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi
@install -m 0755 ${LIBNAME} ${LIBDIR}
@ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so.1
@ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so
@ldconfig
install-headers:
@echo "[Installing Headers]"
@if ( test ! -d ${HEADER_DIR} ) ; then mkdir -p ${HEADER_DIR} ; fi
@install -m 0644 *.h ${HEADER_DIR}
-63
View File
@@ -1,63 +0,0 @@
#include <AES.h>
#include "./printf.h"
AES aes ;
byte *key = (unsigned char*)"0123456789010123";
byte plain[] = "Add NodeAdd NodeAdd NodeAdd NodeAdd Node";
//real iv = iv x2 ex: 01234567 = 0123456701234567
unsigned long long int my_iv = 36753562;
void setup ()
{
Serial.begin (57600) ;
printf_begin();
delay(500);
printf("\n===testng mode\n") ;
// otfly_test () ;
// otfly_test256 () ;
}
void loop ()
{
prekey_test () ;
delay(2000);
}
void prekey (int bits)
{
aes.iv_inc();
byte iv [N_BLOCK] ;
byte plain_p[48];
byte cipher [48] ;
byte check [48] ;
unsigned long ms = micros ();
aes.set_IV(my_iv);
aes.get_IV(iv);
aes.do_aes_encrypt(plain,41,cipher,key,bits,iv);
Serial.print("Encryption took: ");
Serial.println(micros() - ms);
ms = micros ();
aes.set_IV(my_iv);
aes.get_IV(iv);
aes.do_aes_decrypt(cipher,48,check,key,bits,iv);
Serial.print("Decryption took: ");
Serial.println(micros() - ms);
printf("\n\nPLAIN :");
aes.printArray(plain,(bool)true);
printf("\nCIPHER:");
aes.printArray(cipher,(bool)false);
printf("\nCHECK :");
aes.printArray(check,(bool)true);
printf("\nIV :");
aes.printArray(iv,16);
printf("\n============================================================\n");
}
void prekey_test ()
{
prekey (128) ;
}
@@ -1,16 +0,0 @@
Monte Carlo test vectors:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmct.zip
(Compare the Monte Carlo output to the ECBMCT128.rsp file)
Known Answer Tests:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
- Compare the other test outputs to
ECBVarTxt128.rsp
ECBVarKey128.rsp
ECBVarTxt192.rsp
ECBVarKey192.rsp
ECBVarTxt256.rsp
ECBVarKey256.rsp
File diff suppressed because it is too large Load Diff
@@ -1,191 +0,0 @@
#include <AES.h>
AES aes ;
byte key [2*N_BLOCK] ;
byte plain [N_BLOCK] ;
byte iv [N_BLOCK] ;
byte cipher [N_BLOCK] ;
byte check [N_BLOCK] ;
void loop ()
{}
void setup ()
{
Serial.begin (57600) ;
Serial.print ("AES library test vectors") ;
monte_carlo (128) ;
for (int keysize = 128 ; keysize <= 256 ; keysize += 64)
{
prekey_test_var_plaintext (keysize) ;
prekey_test_var_key (keysize) ;
}
}
void prekey_test_var_plaintext (int bits)
{
Serial.println () ;
Serial.print ("ECB Varying Plaintext ") ;
Serial.print (bits) ;
Serial.println (" bits") ;
Serial.println () ;
byte succ ;
set_bits (bits, key, 0) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
Serial.println ("Failure set_key") ;
for (int bitcount = 1 ; bitcount <= 128 ; bitcount++)
{
Serial.print ("COUNT = ") ; Serial.println (bitcount-1) ;
print_value ("KEY = ", key, bits) ;
set_bits (128, plain, bitcount) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
Serial.println ("Failure encrypt") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
Serial.println ("Failure decrypt") ;
//print_value ("CHECK = ", check, 128) ;
check_same (plain, check, 128) ;
Serial.println () ;
}
}
void prekey_test_var_key (int bits)
{
Serial.println () ;
Serial.print ("ECB Varying Key ") ;
Serial.print (bits) ;
Serial.println (" bits") ;
Serial.println () ;
byte succ ;
set_bits (128, plain, 0) ;
for (int bitcount = 1 ; bitcount <= bits ; bitcount++)
{
set_bits (bits, key, bitcount) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
Serial.println ("Failure set_key") ;
Serial.print ("COUNT = ") ; Serial.println (bitcount-1) ;
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
Serial.println ("Failure encrypt") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
Serial.println ("Failure decrypt") ;
check_same (plain, check, 128) ;
Serial.println () ;
}
}
void set_bits (int bits, byte * a, int count)
{
bits >>= 3 ;
byte bcount = count >> 3 ;
for (byte i = 0 ; i < bcount ; i++)
a [i] = 0xFF ;
if ((count & 7) != 0)
a [bcount++] = 0xFF & (0xFF00 >> (count & 7)) ;
for (byte i = bcount ; i < bits ; i++)
a [i] = 0x00 ;
}
void check_same (byte * a, byte * b, int bits)
{
bits >>= 3 ;
for (byte i = 0 ; i < bits ; i++)
if (a[i] != b[i])
{
Serial.println ("Failure plain != check") ;
return ;
}
}
char hex[] = "0123456789abcdef" ;
void print_value (const char * str, byte * a, int bits)
{
Serial.print (str) ;
bits >>= 3 ;
for (int i = 0 ; i < bits ; i++)
{
byte b = a[i] ;
Serial.print (hex [b >> 4]) ;
Serial.print (hex [b & 15]) ;
}
Serial.println () ;
}
byte monteplain [] =
{ 0xb9, 0x14, 0x5a, 0x76, 0x8b, 0x7d, 0xc4, 0x89,
0xa0, 0x96, 0xb5, 0x46, 0xf4, 0x3b, 0x23, 0x1f } ;
byte montekey [] =
{ 0x13, 0x9a, 0x35, 0x42, 0x2f, 0x1d, 0x61, 0xde,
0x3c, 0x91, 0x78, 0x7f, 0xe0, 0x50, 0x7a, 0xfd } ;
void monte_carlo (int bits)
{
Serial.println () ;
Serial.print ("Monte Carlo ") ;
Serial.print (bits) ;
Serial.println (" bits") ;
Serial.println () ;
byte succ ;
for (int i = 0 ; i < 16 ; i++)
{
plain [i] = monteplain [i] ;
key [i] = montekey [i] ;
}
for (int i = 0 ; i < 100 ; i++)
{
Serial.print ("COUNT = ") ; Serial.println (i) ;
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.set_key (key, bits) ;
for (int j = 0 ; j < 1000 ; j++)
{
succ = aes.encrypt (plain, cipher) ;
aes.copy_n_bytes (plain, cipher, 16) ;
}
print_value ("CIPHERTEXT = ", cipher, 128) ;
Serial.println () ;
if (bits == 128)
{
for (byte k = 0 ; k < 16 ; k++)
key [k] ^= cipher [k] ;
}
else if (bits == 192)
{
}
else
{
}
}
}
-39
View File
@@ -1,39 +0,0 @@
#############################################################################
#
# Makefile for AES examples on Raspberry Pi
#
# License: GPL (General Public License)
# Author: gnulnulf <arco@appeltaart.mine.nu>
# Date: 2013/02/07 (version 1.0)
#
# Description:
# ------------
# use make all and make install to install the examples
# You can change the install directory by editing the prefix line
#
prefix := /usr/local
# The recommended compiler flags for the Raspberry Pi
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
#CCFLAGS=
# define all programs
PROGRAMS = aes test_vectors
SOURCES = ${PROGRAMS:=.cpp}
all: ${PROGRAMS}
${PROGRAMS}: ${SOURCES}
g++ ${CCFLAGS} -Wall -I../ -lAES $@.cpp -o $@
clean:
rm -rf $(PROGRAMS)
install: all
test -d $(prefix) || mkdir $(prefix)
test -d $(prefix)/bin || mkdir $(prefix)/bin
for prog in $(PROGRAMS); do \
install -m 0755 $$prog $(prefix)/bin; \
done
.PHONY: install
-16
View File
@@ -1,16 +0,0 @@
Monte Carlo test vectors:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmct.zip
(Compare the Monte Carlo output to the ECBMCT128.rsp file)
Known Answer Tests:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
- Compare the other test outputs to
ECBVarTxt128.rsp
ECBVarKey128.rsp
ECBVarTxt192.rsp
ECBVarKey192.rsp
ECBVarTxt256.rsp
ECBVarKey256.rsp
-47
View File
@@ -1,47 +0,0 @@
#include <AES.h>
#include "printf.h"
AES aes;
void prekey_test ();
void prekey (int bits, int blocks);
byte key[] = "01234567899876543210012345678998";
byte plain[] = "TESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTEST";
//real iv = iv x2 ex: 01234567 = 0123456701234567
unsigned long long int my_iv = 01234567;
int main(int argc, char** argv)
{
printf("\n===testng mode\n") ;
for (int i=0;i<1;i++){
prekey_test () ;
}
}
void prekey (int bits)
{
byte iv [N_BLOCK] ;
byte plain_p[sizeof(plain) + (N_BLOCK - (sizeof(plain) % 16)) - 1];
byte cipher[sizeof(plain_p)];
aes.do_aes_encrypt(plain,sizeof(plain),cipher,key,bits);
aes.get_IV(iv);
aes.do_aes_dencrypt(cipher,aes.get_size(),plain_p,key,bits,iv);
//normally u have sizeof(cipher) but if its in the same sketch you cannot determin it dynamically
printf("\n\nPLAIN :");
aes.printArray(plain);
printf("\nCIPHER:");
aes.printArray(cipher);
printf("\nPlain2:");
aes.printArray(plain_p);
printf("\n============================================================\n");
}
void prekey_test ()
{
prekey (128) ;
}
File diff suppressed because it is too large Load Diff
-183
View File
@@ -1,183 +0,0 @@
#include <AES.h>
#include "printf.h"
AES aes ;
byte key [2*N_BLOCK] ;
byte plain [N_BLOCK] ;
byte iv [N_BLOCK] ;
byte cipher [N_BLOCK] ;
byte check [N_BLOCK] ;
void print_value (const char * str, byte * a, int bits);
void prekey_test_var_plaintext (int bits);
void prekey_test_var_key (int bits);
void set_bits (int bits, byte * a, int count);
void check_same (byte * a, byte * b, int bits);
void monte_carlo (int bits);
int main(int argc, char** argv)
{
printf ("AES library test vectors") ;
monte_carlo (128) ;
for (int keysize = 128 ; keysize <= 256 ; keysize += 64)
{
prekey_test_var_plaintext (keysize) ;
prekey_test_var_key (keysize) ;
}
}
void prekey_test_var_plaintext (int bits)
{
printf ("\nECB Varying Plaintext %i bits\n",bits) ;
byte succ ;
set_bits (bits, key, 0) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
printf("Failure set_key\n") ;
for (int bitcount = 1 ; bitcount <= 128 ; bitcount++)
{
printf ("COUNT = %i \n",bitcount-1);
print_value ("KEY = ", key, bits) ;
set_bits (128, plain, bitcount) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
printf ("Failure encrypt\n") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
printf ("Failure decrypt\n") ;
//print_value ("CHECK = ", check, 128) ;
check_same (plain, check, 128) ;
printf ("\n") ;
}
}
void prekey_test_var_key (int bits)
{
printf ("\nECB Varying key %i bits\n",bits);
byte succ ;
set_bits (128, plain, 0) ;
for (int bitcount = 1 ; bitcount <= bits ; bitcount++)
{
set_bits (bits, key, bitcount) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
printf ("Failure set_key\n") ;
printf ("COUNT = %i\n",bitcount-1);
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
printf ("Failure encrypt\n") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
printf ("Failure decrypt\n") ;
check_same (plain, check, 128) ;
printf("\n");
}
}
void set_bits (int bits, byte * a, int count)
{
bits >>= 3 ;
byte bcount = count >> 3 ;
for (byte i = 0 ; i < bcount ; i++)
a [i] = 0xFF ;
if ((count & 7) != 0)
a [bcount++] = 0xFF & (0xFF00 >> (count & 7)) ;
for (byte i = bcount ; i < bits ; i++)
a [i] = 0x00 ;
}
void check_same (byte * a, byte * b, int bits)
{
bits >>= 3 ;
for (byte i = 0 ; i < bits ; i++)
if (a[i] != b[i])
{
printf ("Failure plain != check\n") ;
return ;
}
}
char hex[] = "0123456789abcdef" ;
void print_value (const char * str, byte * a, int bits)
{
printf ("%s",str) ;
bits >>= 3 ;
for (int i = 0 ; i < bits ; i++)
{
byte b = a[i] ;
printf("%c%c",hex[b >> 4],hex [b & 15]);
//Serial.print (hex [b >> 4]) ;
//Serial.print (hex [b & 15]) ;
}
printf("\n") ;
}
byte monteplain [] =
{ 0xb9, 0x14, 0x5a, 0x76, 0x8b, 0x7d, 0xc4, 0x89,
0xa0, 0x96, 0xb5, 0x46, 0xf4, 0x3b, 0x23, 0x1f } ;
byte montekey [] =
{ 0x13, 0x9a, 0x35, 0x42, 0x2f, 0x1d, 0x61, 0xde,
0x3c, 0x91, 0x78, 0x7f, 0xe0, 0x50, 0x7a, 0xfd } ;
void monte_carlo (int bits)
{
printf ("\nMonte Carlo %i bits\n",bits);
byte succ;
for (int i = 0 ; i < 16 ; i++)
{
plain [i] = monteplain [i] ;
key [i] = montekey [i] ;
}
for (int i = 0 ; i < 100 ; i++)
{
printf ("COUNT = %i\n",i);
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.set_key (key, bits) ;
for (int j = 0 ; j < 1000 ; j++)
{
succ = aes.encrypt (plain, cipher) ;
aes.copy_n_bytes (plain, cipher, 16) ;
}
print_value ("CIPHERTEXT = ", cipher, 128) ;
printf("\n");
if (bits == 128)
{
for (byte k = 0 ; k < 16 ; k++)
key [k] ^= cipher [k] ;
}
else if (bits == 192)
{
}
else
{
}
}
}
-59
View File
@@ -1,59 +0,0 @@
/*
Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
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.
*/
/* Galileo support from spaniakos <spaniakos@gmail.com> */
/**
* @file printf.h
*
* Setup necessary to direct stdout to the Arduino Serial library, which
* enables 'printf'
*/
#ifndef __PRINTF_H__
#define __PRINTF_H__
#if defined (ARDUINO) && !defined (__arm__) && !defined(__ARDUINO_X86__)
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
#elif defined (__arm__)
void printf_begin(void){}
#elif defined(__ARDUINO_X86__)
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
//JESUS - For reddirect stdout to /dev/ttyGS0 (Serial Monitor port)
stdout = freopen("/dev/ttyGS0","w",stdout);
delay(500);
printf("redirecting to Serial...");
//JESUS -----------------------------------------------------------
}
#else
#error This example is only for use on Arduino.
#endif // ARDUINO
#endif // __PRINTF_H__
+408 -410
View File
@@ -16,366 +16,361 @@ static uint8_t swi_send_byte(uint8_t value);
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response); static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response);
static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address); static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);
static uint8_t sha204c_resync(uint8_t size, uint8_t *response); static uint8_t sha204c_resync(uint8_t size, uint8_t *response);
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout); static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer,
uint8_t execution_delay, uint8_t execution_timeout);
/* SWI bit bang functions */ /* SWI bit bang functions */
static void swi_set_signal_pin(uint8_t is_high) static void swi_set_signal_pin(uint8_t is_high)
{ {
SHA204_SET_OUTPUT(); SHA204_SET_OUTPUT();
if (is_high) if (is_high) {
SHA204_POUT_HIGH(); SHA204_POUT_HIGH();
else } else {
SHA204_POUT_LOW(); SHA204_POUT_LOW();
}
} }
static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer) static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer)
{ {
uint8_t i, bit_mask; uint8_t i, bit_mask;
// Disable interrupts while sending. // Disable interrupts while sending.
noInterrupts(); //swi_disable_interrupts(); noInterrupts(); //swi_disable_interrupts();
// Set signal pin as output. // Set signal pin as output.
SHA204_POUT_HIGH(); SHA204_POUT_HIGH();
SHA204_SET_OUTPUT(); SHA204_SET_OUTPUT();
// Wait turn around time. // Wait turn around time.
delayMicroseconds(RX_TX_DELAY); //RX_TX_DELAY; delayMicroseconds(RX_TX_DELAY); //RX_TX_DELAY;
for (i = 0; i < count; i++) for (i = 0; i < count; i++) {
{ for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) if (bit_mask & buffer[i]) {
{ SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
if (bit_mask & buffer[i]) delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
{ SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin; delayMicroseconds(7*BIT_DELAY); //BIT_DELAY_7;
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1; } else {
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin; // Send a zero bit.
delayMicroseconds(7*BIT_DELAY); //BIT_DELAY_7; SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
} delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
else SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
{ delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
// Send a zero bit. SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin; delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1; SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin; delayMicroseconds(5*BIT_DELAY); //BIT_DELAY_5;
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1; }
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin; }
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1; }
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin; interrupts(); //swi_enable_interrupts();
delayMicroseconds(5*BIT_DELAY); //BIT_DELAY_5; return SWI_FUNCTION_RETCODE_SUCCESS;
}
}
}
interrupts(); //swi_enable_interrupts();
return SWI_FUNCTION_RETCODE_SUCCESS;
} }
static uint8_t swi_send_byte(uint8_t value) static uint8_t swi_send_byte(uint8_t value)
{ {
return swi_send_bytes(1, &value); return swi_send_bytes(1, &value);
} }
static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer) static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer)
{ {
uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS; uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS;
uint8_t i; uint8_t i;
uint8_t bit_mask; uint8_t bit_mask;
uint8_t pulse_count; uint8_t pulse_count;
uint8_t timeout_count; uint8_t timeout_count;
// Disable interrupts while receiving. // Disable interrupts while receiving.
noInterrupts(); //swi_disable_interrupts(); noInterrupts(); //swi_disable_interrupts();
// Configure signal pin as input. // Configure signal pin as input.
SHA204_SET_INPUT(); SHA204_SET_INPUT();
// Receive bits and store in buffer. // Receive bits and store in buffer.
for (i = 0; i < count; i++) for (i = 0; i < count; i++) {
{ for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) pulse_count = 0;
{
pulse_count = 0;
// Make sure that the variable below is big enough. // Make sure that the variable below is big enough.
// Change it to uint16_t if 255 is too small, but be aware that // Change it to uint16_t if 255 is too small, but be aware that
// the loop resolution decreases on an 8-bit controller in that case. // the loop resolution decreases on an 8-bit controller in that case.
timeout_count = START_PULSE_TIME_OUT; timeout_count = START_PULSE_TIME_OUT;
// Detect start bit. // Detect start bit.
while (--timeout_count > 0) while (--timeout_count > 0) {
{ // Wait for falling edge.
// Wait for falling edge. if (SHA204_PIN_READ() == 0) {
if (SHA204_PIN_READ() == 0) break;
break; }
} }
if (timeout_count == 0) if (timeout_count == 0) {
{ status = SWI_FUNCTION_RETCODE_TIMEOUT;
status = SWI_FUNCTION_RETCODE_TIMEOUT; break;
break; }
}
do do {
{ // Wait for rising edge.
// Wait for rising edge. if (SHA204_PIN_READ() != 0) {
if (SHA204_PIN_READ() != 0) // For an Atmel microcontroller this might be faster than "pulse_count++".
{ pulse_count = 1;
// For an Atmel microcontroller this might be faster than "pulse_count++". break;
pulse_count = 1; }
break; } while (--timeout_count > 0);
}
} while (--timeout_count > 0);
if (pulse_count == 0) if (pulse_count == 0) {
{ status = SWI_FUNCTION_RETCODE_TIMEOUT;
status = SWI_FUNCTION_RETCODE_TIMEOUT; break;
break; }
}
// Trying to measure the time of start bit and calculating the timeout // Trying to measure the time of start bit and calculating the timeout
// for zero bit detection is not accurate enough for an 8 MHz 8-bit CPU. // for zero bit detection is not accurate enough for an 8 MHz 8-bit CPU.
// So let's just wait the maximum time for the falling edge of a zero bit // So let's just wait the maximum time for the falling edge of a zero bit
// to arrive after we have detected the rising edge of the start bit. // to arrive after we have detected the rising edge of the start bit.
timeout_count = ZERO_PULSE_TIME_OUT; timeout_count = ZERO_PULSE_TIME_OUT;
// Detect possible edge indicating zero bit. // Detect possible edge indicating zero bit.
do do {
{ if (SHA204_PIN_READ() == 0) {
if (SHA204_PIN_READ() == 0) // For an Atmel microcontroller this might be faster than "pulse_count++".
{ pulse_count = 2;
// For an Atmel microcontroller this might be faster than "pulse_count++". break;
pulse_count = 2; }
break; } while (--timeout_count > 0);
}
} while (--timeout_count > 0);
// Wait for rising edge of zero pulse before returning. Otherwise we might interpret // Wait for rising edge of zero pulse before returning. Otherwise we might interpret
// its rising edge as the next start pulse. // its rising edge as the next start pulse.
if (pulse_count == 2) if (pulse_count == 2) {
{ do {
do if (SHA204_PIN_READ() != 0) {
{ break;
if (SHA204_PIN_READ() != 0) }
break; } while (timeout_count-- > 0);
} while (timeout_count-- > 0); }
}
// Update byte at current buffer index. // Update byte at current buffer index.
else else {
buffer[i] |= bit_mask; // received "one" bit buffer[i] |= bit_mask; // received "one" bit
} }
}
if (status != SWI_FUNCTION_RETCODE_SUCCESS) if (status != SWI_FUNCTION_RETCODE_SUCCESS) {
break; break;
} }
interrupts(); //swi_enable_interrupts(); }
interrupts(); //swi_enable_interrupts();
if (status == SWI_FUNCTION_RETCODE_TIMEOUT) if (status == SWI_FUNCTION_RETCODE_TIMEOUT) {
{ if (i > 0) {
if (i > 0) // Indicate that we timed out after having received at least one byte.
// Indicate that we timed out after having received at least one byte. status = SWI_FUNCTION_RETCODE_RX_FAIL;
status = SWI_FUNCTION_RETCODE_RX_FAIL; }
} }
return status; return status;
} }
/* Physical functions */ /* Physical functions */
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response) static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response)
{ {
uint8_t count_byte; uint8_t i;
uint8_t i; uint8_t ret_code;
uint8_t ret_code;
for (i = 0; i < size; i++) for (i = 0; i < size; i++) {
response[i] = 0; response[i] = 0;
}
(void) swi_send_byte(SHA204_SWI_FLAG_TX); (void) swi_send_byte(SHA204_SWI_FLAG_TX);
ret_code = swi_receive_bytes(size, response); ret_code = swi_receive_bytes(size, response);
if (ret_code == SWI_FUNCTION_RETCODE_SUCCESS || ret_code == SWI_FUNCTION_RETCODE_RX_FAIL) if (ret_code == SWI_FUNCTION_RETCODE_SUCCESS || ret_code == SWI_FUNCTION_RETCODE_RX_FAIL) {
{ uint8_t count_byte;
count_byte = response[SHA204_BUFFER_POS_COUNT]; count_byte = response[SHA204_BUFFER_POS_COUNT];
if ((count_byte < SHA204_RSP_SIZE_MIN) || (count_byte > size)) if ((count_byte < SHA204_RSP_SIZE_MIN) || (count_byte > size)) {
return SHA204_INVALID_SIZE; return SHA204_INVALID_SIZE;
}
return SHA204_SUCCESS; return SHA204_SUCCESS;
} }
// Translate error so that the Communication layer // Translate error so that the Communication layer
// can distinguish between a real error or the // can distinguish between a real error or the
// device being busy executing a command. // device being busy executing a command.
if (ret_code == SWI_FUNCTION_RETCODE_TIMEOUT) if (ret_code == SWI_FUNCTION_RETCODE_TIMEOUT) {
return SHA204_RX_NO_RESPONSE; return SHA204_RX_NO_RESPONSE;
else } else {
return SHA204_RX_FAIL; return SHA204_RX_FAIL;
}
} }
/* Communication functions */ /* Communication functions */
static uint8_t sha204c_resync(uint8_t size, uint8_t *response) static uint8_t sha204c_resync(uint8_t size, uint8_t *response)
{ {
// Try to re-synchronize without sending a Wake token // Try to re-synchronize without sending a Wake token
// (step 1 of the re-synchronization process). // (step 1 of the re-synchronization process).
delay(SHA204_SYNC_TIMEOUT); delay(SHA204_SYNC_TIMEOUT);
uint8_t ret_code = sha204p_receive_response(size, response); uint8_t ret_code = sha204p_receive_response(size, response);
if (ret_code == SHA204_SUCCESS) if (ret_code == SHA204_SUCCESS) {
return ret_code; return ret_code;
}
// We lost communication. Send a Wake pulse and try // We lost communication. Send a Wake pulse and try
// to receive a response (steps 2 and 3 of the // to receive a response (steps 2 and 3 of the
// re-synchronization process). // re-synchronization process).
atsha204_sleep(); atsha204_sleep();
ret_code = atsha204_wakeup(response); ret_code = atsha204_wakeup(response);
// Translate a return value of success into one // Translate a return value of success into one
// that indicates that the device had to be woken up // that indicates that the device had to be woken up
// and might have lost its TempKey. // and might have lost its TempKey.
return (ret_code == SHA204_SUCCESS ? SHA204_RESYNC_WITH_WAKEUP : ret_code); return (ret_code == SHA204_SUCCESS ? SHA204_RESYNC_WITH_WAKEUP : ret_code);
} }
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout) static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer,
uint8_t execution_delay, uint8_t execution_timeout)
{ {
uint8_t ret_code = SHA204_FUNC_FAIL; uint8_t ret_code = SHA204_FUNC_FAIL;
uint8_t ret_code_resync; uint8_t ret_code_resync;
uint8_t n_retries_send; uint8_t n_retries_send;
uint8_t n_retries_receive; uint8_t n_retries_receive;
uint8_t i; uint8_t i;
uint8_t status_byte; uint8_t status_byte;
uint8_t count = tx_buffer[SHA204_BUFFER_POS_COUNT]; uint8_t count = tx_buffer[SHA204_BUFFER_POS_COUNT];
uint8_t count_minus_crc = count - SHA204_CRC_SIZE; uint8_t count_minus_crc = count - SHA204_CRC_SIZE;
uint16_t execution_timeout_us = (uint16_t) (execution_timeout * 1000) + SHA204_RESPONSE_TIMEOUT; uint16_t execution_timeout_us = (uint16_t) (execution_timeout * 1000) + SHA204_RESPONSE_TIMEOUT;
volatile uint16_t timeout_countdown; volatile uint16_t timeout_countdown;
// Append CRC. // Append CRC.
sha204c_calculate_crc(count_minus_crc, tx_buffer, tx_buffer + count_minus_crc); sha204c_calculate_crc(count_minus_crc, tx_buffer, tx_buffer + count_minus_crc);
// Retry loop for sending a command and receiving a response. // Retry loop for sending a command and receiving a response.
n_retries_send = SHA204_RETRY_COUNT + 1; n_retries_send = SHA204_RETRY_COUNT + 1;
while ((n_retries_send-- > 0) && (ret_code != SHA204_SUCCESS)) while ((n_retries_send-- > 0) && (ret_code != SHA204_SUCCESS)) {
{ // Send command.
// Send command. ret_code = swi_send_byte(SHA204_SWI_FLAG_CMD);
ret_code = swi_send_byte(SHA204_SWI_FLAG_CMD); if (ret_code != SWI_FUNCTION_RETCODE_SUCCESS) {
if (ret_code != SWI_FUNCTION_RETCODE_SUCCESS) ret_code = SHA204_COMM_FAIL;
ret_code = SHA204_COMM_FAIL; } else {
else ret_code = swi_send_bytes(count, tx_buffer);
ret_code = swi_send_bytes(count, tx_buffer); }
if (ret_code != SHA204_SUCCESS) if (ret_code != SHA204_SUCCESS) {
{ if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE) {
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE) return ret_code; // The device seems to be dead in the water.
return ret_code; // The device seems to be dead in the water. } else {
else continue;
continue; }
} }
// Wait minimum command execution time and then start polling for a response. // Wait minimum command execution time and then start polling for a response.
delay(execution_delay); delay(execution_delay);
// Retry loop for receiving a response. // Retry loop for receiving a response.
n_retries_receive = SHA204_RETRY_COUNT + 1; n_retries_receive = SHA204_RETRY_COUNT + 1;
while (n_retries_receive-- > 0) while (n_retries_receive-- > 0) {
{ // Reset response buffer.
// Reset response buffer. for (i = 0; i < rx_size; i++) {
for (i = 0; i < rx_size; i++) rx_buffer[i] = 0;
rx_buffer[i] = 0; }
// Poll for response. // Poll for response.
timeout_countdown = execution_timeout_us; timeout_countdown = execution_timeout_us;
do do {
{ ret_code = sha204p_receive_response(rx_size, rx_buffer);
ret_code = sha204p_receive_response(rx_size, rx_buffer); timeout_countdown -= SHA204_RESPONSE_TIMEOUT;
timeout_countdown -= SHA204_RESPONSE_TIMEOUT; } while ((timeout_countdown > SHA204_RESPONSE_TIMEOUT) && (ret_code == SHA204_RX_NO_RESPONSE));
}
while ((timeout_countdown > SHA204_RESPONSE_TIMEOUT) && (ret_code == SHA204_RX_NO_RESPONSE));
if (ret_code == SHA204_RX_NO_RESPONSE) if (ret_code == SHA204_RX_NO_RESPONSE) {
{ // We did not receive a response. Re-synchronize and send command again.
// We did not receive a response. Re-synchronize and send command again. if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE) {
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE) // The device seems to be dead in the water.
// The device seems to be dead in the water. return ret_code;
return ret_code; } else {
else break;
break; }
} }
// Check whether we received a valid response. // Check whether we received a valid response.
if (ret_code == SHA204_INVALID_SIZE) if (ret_code == SHA204_INVALID_SIZE) {
{ // We see 0xFF for the count when communication got out of sync.
// We see 0xFF for the count when communication got out of sync. ret_code_resync = sha204c_resync(rx_size, rx_buffer);
ret_code_resync = sha204c_resync(rx_size, rx_buffer); if (ret_code_resync == SHA204_SUCCESS) {
if (ret_code_resync == SHA204_SUCCESS) // We did not have to wake up the device. Try receiving response again.
// We did not have to wake up the device. Try receiving response again. continue;
continue; }
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP) if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP) {
// We could re-synchronize, but only after waking up the device. // We could re-synchronize, but only after waking up the device.
// Re-send command. // Re-send command.
break; break;
else } else {
// We failed to re-synchronize. // We failed to re-synchronize.
return ret_code; return ret_code;
} }
}
// We received a response of valid size. // We received a response of valid size.
// Check the consistency of the response. // Check the consistency of the response.
ret_code = sha204c_check_crc(rx_buffer); ret_code = sha204c_check_crc(rx_buffer);
if (ret_code == SHA204_SUCCESS) if (ret_code == SHA204_SUCCESS) {
{ // Received valid response.
// Received valid response. if (rx_buffer[SHA204_BUFFER_POS_COUNT] > SHA204_RSP_SIZE_MIN) {
if (rx_buffer[SHA204_BUFFER_POS_COUNT] > SHA204_RSP_SIZE_MIN) // Received non-status response. We are done.
// Received non-status response. We are done. return ret_code;
return ret_code; }
// Received status response. // Received status response.
status_byte = rx_buffer[SHA204_BUFFER_POS_STATUS]; status_byte = rx_buffer[SHA204_BUFFER_POS_STATUS];
// Translate the three possible device status error codes // Translate the three possible device status error codes
// into library return codes. // into library return codes.
if (status_byte == SHA204_STATUS_BYTE_PARSE) if (status_byte == SHA204_STATUS_BYTE_PARSE) {
return SHA204_PARSE_ERROR; return SHA204_PARSE_ERROR;
if (status_byte == SHA204_STATUS_BYTE_EXEC) }
return SHA204_CMD_FAIL; if (status_byte == SHA204_STATUS_BYTE_EXEC) {
if (status_byte == SHA204_STATUS_BYTE_COMM) return SHA204_CMD_FAIL;
{ }
// In case of the device status byte indicating a communication if (status_byte == SHA204_STATUS_BYTE_COMM) {
// error this function exits the retry loop for receiving a response // In case of the device status byte indicating a communication
// and enters the overall retry loop // error this function exits the retry loop for receiving a response
// (send command / receive response). // and enters the overall retry loop
ret_code = SHA204_STATUS_CRC; // (send command / receive response).
break; ret_code = SHA204_STATUS_CRC;
} break;
}
// Received status response from CheckMAC, DeriveKey, GenDig, // Received status response from CheckMAC, DeriveKey, GenDig,
// Lock, Nonce, Pause, UpdateExtra, or Write command. // Lock, Nonce, Pause, UpdateExtra, or Write command.
return ret_code; return ret_code;
} }
else else {
{ // Received response with incorrect CRC.
// Received response with incorrect CRC. ret_code_resync = sha204c_resync(rx_size, rx_buffer);
ret_code_resync = sha204c_resync(rx_size, rx_buffer); if (ret_code_resync == SHA204_SUCCESS) {
if (ret_code_resync == SHA204_SUCCESS) // We did not have to wake up the device. Try receiving response again.
// We did not have to wake up the device. Try receiving response again. continue;
continue; }
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP) if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP) {
// We could re-synchronize, but only after waking up the device. // We could re-synchronize, but only after waking up the device.
// Re-send command. // Re-send command.
break; break;
else } else {
// We failed to re-synchronize. // We failed to re-synchronize.
return ret_code; return ret_code;
} // block end of check response consistency }
} // block end of check response consistency
} // block end of receive retry loop } // block end of receive retry loop
} // block end of send and receive retry loop } // block end of send and receive retry loop
return ret_code; return ret_code;
} }
@@ -383,59 +378,59 @@ static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uin
static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address) static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)
{ {
uint8_t rx_size; uint8_t rx_size;
address >>= 2; address >>= 2;
tx_buffer[SHA204_COUNT_IDX] = READ_COUNT; tx_buffer[SHA204_COUNT_IDX] = READ_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_READ; tx_buffer[SHA204_OPCODE_IDX] = SHA204_READ;
tx_buffer[READ_ZONE_IDX] = zone; tx_buffer[READ_ZONE_IDX] = zone;
tx_buffer[READ_ADDR_IDX] = (uint8_t) (address & SHA204_ADDRESS_MASK); tx_buffer[READ_ADDR_IDX] = (uint8_t) (address & SHA204_ADDRESS_MASK);
tx_buffer[READ_ADDR_IDX + 1] = 0; tx_buffer[READ_ADDR_IDX + 1] = 0;
rx_size = (zone & SHA204_ZONE_COUNT_FLAG) ? READ_32_RSP_SIZE : READ_4_RSP_SIZE; rx_size = (zone & SHA204_ZONE_COUNT_FLAG) ? READ_32_RSP_SIZE : READ_4_RSP_SIZE;
return sha204c_send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY, READ_EXEC_MAX - READ_DELAY); return sha204c_send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY,
READ_EXEC_MAX - READ_DELAY);
} }
/* CRC Calculator and Checker */ /* CRC Calculator and Checker */
static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc) static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
{ {
uint8_t counter; uint8_t counter;
uint16_t crc_register = 0; uint16_t crc_register = 0;
uint16_t polynom = 0x8005; uint16_t polynom = 0x8005;
uint8_t shift_register; uint8_t shift_register;
uint8_t data_bit, crc_bit; uint8_t data_bit, crc_bit;
for (counter = 0; counter < length; counter++) for (counter = 0; counter < length; counter++) {
{ for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) {
for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) data_bit = (data[counter] & shift_register) ? 1 : 0;
{ crc_bit = crc_register >> 15;
data_bit = (data[counter] & shift_register) ? 1 : 0;
crc_bit = crc_register >> 15;
// Shift CRC to the left by 1. // Shift CRC to the left by 1.
crc_register <<= 1; crc_register <<= 1;
if ((data_bit ^ crc_bit) != 0) if ((data_bit ^ crc_bit) != 0) {
crc_register ^= polynom; crc_register ^= polynom;
} }
} }
crc[0] = (uint8_t) (crc_register & 0x00FF); }
crc[1] = (uint8_t) (crc_register >> 8); crc[0] = (uint8_t) (crc_register & 0x00FF);
crc[1] = (uint8_t) (crc_register >> 8);
} }
static uint8_t sha204c_check_crc(uint8_t *response) static uint8_t sha204c_check_crc(uint8_t *response)
{ {
uint8_t crc[SHA204_CRC_SIZE]; uint8_t crc[SHA204_CRC_SIZE];
uint8_t count = response[SHA204_BUFFER_POS_COUNT]; uint8_t count = response[SHA204_BUFFER_POS_COUNT];
count -= SHA204_CRC_SIZE; count -= SHA204_CRC_SIZE;
sha204c_calculate_crc(count, response, crc); sha204c_calculate_crc(count, response, crc);
return (crc[0] == response[count] && crc[1] == response[count + 1]) return (crc[0] == response[count] && crc[1] == response[count + 1])
? SHA204_SUCCESS : SHA204_BAD_CRC; ? SHA204_SUCCESS : SHA204_BAD_CRC;
} }
/* Public functions */ /* Public functions */
@@ -443,111 +438,113 @@ static uint8_t sha204c_check_crc(uint8_t *response)
void atsha204_init(uint8_t pin) void atsha204_init(uint8_t pin)
{ {
#if defined(ARDUINO_ARCH_AVR) #if defined(ARDUINO_ARCH_AVR)
device_pin = digitalPinToBitMask(pin); // Find the bit value of the pin device_pin = digitalPinToBitMask(pin); // Find the bit value of the pin
uint8_t port = digitalPinToPort(pin); // temoporarily used to get the next three registers uint8_t port = digitalPinToPort(pin); // temoporarily used to get the next three registers
// Point to data direction register port of pin // Point to data direction register port of pin
device_port_DDR = portModeRegister(port); device_port_DDR = portModeRegister(port);
// Point to output register of pin // Point to output register of pin
device_port_OUT = portOutputRegister(port); device_port_OUT = portOutputRegister(port);
// Point to input register of pin // Point to input register of pin
device_port_IN = portInputRegister(port); device_port_IN = portInputRegister(port);
#else #else
device_pin = pin; device_pin = pin;
#endif #endif
} }
void atsha204_idle(void) void atsha204_idle(void)
{ {
swi_send_byte(SHA204_SWI_FLAG_IDLE); swi_send_byte(SHA204_SWI_FLAG_IDLE);
} }
void atsha204_sleep(void) void atsha204_sleep(void)
{ {
swi_send_byte(SHA204_SWI_FLAG_SLEEP); swi_send_byte(SHA204_SWI_FLAG_SLEEP);
} }
uint8_t atsha204_wakeup(uint8_t *response) uint8_t atsha204_wakeup(uint8_t *response)
{ {
swi_set_signal_pin(0); swi_set_signal_pin(0);
delayMicroseconds(10*SHA204_WAKEUP_PULSE_WIDTH); delayMicroseconds(10*SHA204_WAKEUP_PULSE_WIDTH);
swi_set_signal_pin(1); swi_set_signal_pin(1);
delay(SHA204_WAKEUP_DELAY); delay(SHA204_WAKEUP_DELAY);
uint8_t ret_code = sha204p_receive_response(SHA204_RSP_SIZE_MIN, response); uint8_t ret_code = sha204p_receive_response(SHA204_RSP_SIZE_MIN, response);
if (ret_code != SHA204_SUCCESS) if (ret_code != SHA204_SUCCESS) {
return ret_code; return ret_code;
}
// Verify status response. // Verify status response.
if (response[SHA204_BUFFER_POS_COUNT] != SHA204_RSP_SIZE_MIN) if (response[SHA204_BUFFER_POS_COUNT] != SHA204_RSP_SIZE_MIN) {
ret_code = SHA204_INVALID_SIZE; ret_code = SHA204_INVALID_SIZE;
else if (response[SHA204_BUFFER_POS_STATUS] != SHA204_STATUS_BYTE_WAKEUP) } else if (response[SHA204_BUFFER_POS_STATUS] != SHA204_STATUS_BYTE_WAKEUP) {
ret_code = SHA204_COMM_FAIL; ret_code = SHA204_COMM_FAIL;
else } else {
{ if ((response[SHA204_RSP_SIZE_MIN - SHA204_CRC_SIZE] != 0x33)
if ((response[SHA204_RSP_SIZE_MIN - SHA204_CRC_SIZE] != 0x33) || (response[SHA204_RSP_SIZE_MIN + 1 - SHA204_CRC_SIZE] != 0x43)) {
|| (response[SHA204_RSP_SIZE_MIN + 1 - SHA204_CRC_SIZE] != 0x43)) ret_code = SHA204_BAD_CRC;
ret_code = SHA204_BAD_CRC; }
} }
if (ret_code != SHA204_SUCCESS) if (ret_code != SHA204_SUCCESS) {
delay(SHA204_COMMAND_EXEC_MAX); delay(SHA204_COMMAND_EXEC_MAX);
}
return ret_code; return ret_code;
} }
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer) uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size,
uint8_t *rx_buffer)
{ {
uint8_t poll_delay, poll_timeout, response_size; uint8_t poll_delay, poll_timeout, response_size;
uint8_t *p_buffer; uint8_t *p_buffer;
uint8_t len; uint8_t len;
(void)tx_size; (void)tx_size;
// Supply delays and response size. // Supply delays and response size.
switch (op_code) switch (op_code) {
{ case SHA204_GENDIG:
case SHA204_GENDIG: poll_delay = GENDIG_DELAY;
poll_delay = GENDIG_DELAY; poll_timeout = GENDIG_EXEC_MAX - GENDIG_DELAY;
poll_timeout = GENDIG_EXEC_MAX - GENDIG_DELAY; response_size = GENDIG_RSP_SIZE;
response_size = GENDIG_RSP_SIZE; break;
break;
case SHA204_HMAC: case SHA204_HMAC:
poll_delay = HMAC_DELAY; poll_delay = HMAC_DELAY;
poll_timeout = HMAC_EXEC_MAX - HMAC_DELAY; poll_timeout = HMAC_EXEC_MAX - HMAC_DELAY;
response_size = HMAC_RSP_SIZE; response_size = HMAC_RSP_SIZE;
break; break;
case SHA204_NONCE: case SHA204_NONCE:
poll_delay = NONCE_DELAY; poll_delay = NONCE_DELAY;
poll_timeout = NONCE_EXEC_MAX - NONCE_DELAY; poll_timeout = NONCE_EXEC_MAX - NONCE_DELAY;
response_size = param1 == NONCE_MODE_PASSTHROUGH response_size = param1 == NONCE_MODE_PASSTHROUGH
? NONCE_RSP_SIZE_SHORT : NONCE_RSP_SIZE_LONG; ? NONCE_RSP_SIZE_SHORT : NONCE_RSP_SIZE_LONG;
break; break;
case SHA204_RANDOM: case SHA204_RANDOM:
poll_delay = RANDOM_DELAY; poll_delay = RANDOM_DELAY;
poll_timeout = RANDOM_EXEC_MAX - RANDOM_DELAY; poll_timeout = RANDOM_EXEC_MAX - RANDOM_DELAY;
response_size = RANDOM_RSP_SIZE; response_size = RANDOM_RSP_SIZE;
break; break;
case SHA204_SHA: case SHA204_SHA:
poll_delay = SHA_DELAY; poll_delay = SHA_DELAY;
poll_timeout = SHA_EXEC_MAX - SHA_DELAY; poll_timeout = SHA_EXEC_MAX - SHA_DELAY;
response_size = param1 == SHA_INIT response_size = param1 == SHA_INIT
? SHA_RSP_SIZE_SHORT : SHA_RSP_SIZE_LONG; ? SHA_RSP_SIZE_SHORT : SHA_RSP_SIZE_LONG;
break; break;
case SHA204_WRITE: case SHA204_WRITE:
poll_delay = WRITE_DELAY; poll_delay = WRITE_DELAY;
poll_timeout = WRITE_EXEC_MAX - WRITE_DELAY; poll_timeout = WRITE_EXEC_MAX - WRITE_DELAY;
response_size = WRITE_RSP_SIZE; response_size = WRITE_RSP_SIZE;
break; break;
default: default:
poll_delay = 0; poll_delay = 0;
poll_timeout = SHA204_COMMAND_EXEC_MAX; poll_timeout = SHA204_COMMAND_EXEC_MAX;
response_size = rx_size; response_size = rx_size;
} }
// Assemble command. // Assemble command.
@@ -568,33 +565,34 @@ uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
// Send command and receive response. // Send command and receive response.
return sha204c_send_and_receive(&tx_buffer[0], response_size, return sha204c_send_and_receive(&tx_buffer[0], response_size,
&rx_buffer[0], poll_delay, poll_timeout); &rx_buffer[0], poll_delay, poll_timeout);
} }
uint8_t atsha204_getSerialNumber(uint8_t * response) uint8_t atsha204_getSerialNumber(uint8_t * response)
{ {
uint8_t readCommand[READ_COUNT]; uint8_t readCommand[READ_COUNT];
uint8_t readResponse[READ_4_RSP_SIZE]; uint8_t readResponse[READ_4_RSP_SIZE];
/* read from bytes 0->3 of config zone */ /* read from bytes 0->3 of config zone */
uint8_t returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03); uint8_t returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03);
if (!returnCode) if (!returnCode) {
{ for (int i=0; i<4; i++) {// store bytes 0-3 into respones array
for (int i=0; i<4; i++) // store bytes 0-3 into respones array response[i] = readResponse[SHA204_BUFFER_POS_DATA+i];
response[i] = readResponse[SHA204_BUFFER_POS_DATA+i]; }
/* read from bytes 8->11 of config zone */ /* read from bytes 8->11 of config zone */
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47); returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47);
for (int i=4; i<8; i++) // store bytes 4-7 of SN into response array for (int i=4; i<8; i++) {// store bytes 4-7 of SN into response array
response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)]; response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)];
}
if (!returnCode) if (!returnCode) {
{ /* Finally if last two reads were successful, read byte 8 of the SN */ /* Finally if last two reads were successful, read byte 8 of the SN */
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8); returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8);
response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE
} }
} }
return returnCode; return returnCode;
} }
+2 -2
View File
@@ -244,8 +244,8 @@ void atsha204_idle(void);
void atsha204_sleep(void); void atsha204_sleep(void);
uint8_t atsha204_wakeup(uint8_t *response); uint8_t atsha204_wakeup(uint8_t *response);
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t datalen1, uint8_t *data1, uint8_t tx_size,
uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer); uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
uint8_t atsha204_getSerialNumber(uint8_t *response); uint8_t atsha204_getSerialNumber(uint8_t *response);
#endif #endif
+179 -142
View File
@@ -1,7 +1,7 @@
#include <string.h> #include <string.h>
#if defined(__AVR__) #if defined(__AVR__)
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#define PRIPSTR "%S" #define PRIPSTR "%S"
#elif defined(ESP8266) #elif defined(ESP8266)
#include <pgmspace.h> #include <pgmspace.h>
#endif #endif
@@ -9,130 +9,159 @@
const uint32_t sha256K[] PROGMEM = { const uint32_t sha256K[] PROGMEM = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
}; };
#define BUFFER_SIZE 64 #define BUFFER_SIZE 64
const uint8_t sha256InitState[] PROGMEM = { const uint8_t sha256InitState[] PROGMEM = {
0x67,0xe6,0x09,0x6a, // H0 0x67,0xe6,0x09,0x6a, // H0
0x85,0xae,0x67,0xbb, // H1 0x85,0xae,0x67,0xbb, // H1
0x72,0xf3,0x6e,0x3c, // H2 0x72,0xf3,0x6e,0x3c, // H2
0x3a,0xf5,0x4f,0xa5, // H3 0x3a,0xf5,0x4f,0xa5, // H3
0x7f,0x52,0x0e,0x51, // H4 0x7f,0x52,0x0e,0x51, // H4
0x8c,0x68,0x05,0x9b, // H5 0x8c,0x68,0x05,0x9b, // H5
0xab,0xd9,0x83,0x1f, // H6 0xab,0xd9,0x83,0x1f, // H6
0x19,0xcd,0xe0,0x5b // H7 0x19,0xcd,0xe0,0x5b // H7
}; };
void Sha256Class::init(void) { Sha256Class::Sha256Class()
memcpy_P(state.b,sha256InitState,32); {
byteCount = 0; /*
bufferOffset = 0; memset(keyBuffer, 0, BLOCK_LENGTH);
} memset(innerHash, 0, HASH_LENGTH);
memset(buffer.b, 0, BLOCK_LENGTH);
uint32_t Sha256Class::ror32(uint32_t number, uint8_t bits) { memset(state.b, 0, BLOCK_LENGTH);
return ((number << (32-bits)) | (number >> bits)); bufferOffset = 0;
} byteCount = 0;
*/
void Sha256Class::hashBlock() {
uint8_t i;
uint32_t a,b,c,d,e,f,g,h,t1,t2;
a=state.w[0];
b=state.w[1];
c=state.w[2];
d=state.w[3];
e=state.w[4];
f=state.w[5];
g=state.w[6];
h=state.w[7];
for (i=0; i<64; i++) {
if (i>=16) {
t1 = buffer.w[i&15] + buffer.w[(i-7)&15];
t2 = buffer.w[(i-2)&15];
t1 += ror32(t2,17) ^ ror32(t2,19) ^ (t2>>10);
t2 = buffer.w[(i-15)&15];
t1 += ror32(t2,7) ^ ror32(t2,18) ^ (t2>>3);
buffer.w[i&15] = t1;
}
t1 = h;
t1 += ror32(e,6) ^ ror32(e,11) ^ ror32(e,25); // ∑1(e)
t1 += g ^ (e & (g ^ f)); // Ch(e,f,g)
t1 += pgm_read_dword(sha256K+i); // Ki
t1 += buffer.w[i&15]; // Wi
t2 = ror32(a,2) ^ ror32(a,13) ^ ror32(a,22); // ∑0(a)
t2 += ((b & c) | (a & (b | c))); // Maj(a,b,c)
h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2;
}
state.w[0] += a;
state.w[1] += b;
state.w[2] += c;
state.w[3] += d;
state.w[4] += e;
state.w[5] += f;
state.w[6] += g;
state.w[7] += h;
}
void Sha256Class::addUncounted(uint8_t data) {
buffer.b[bufferOffset ^ 3] = data;
bufferOffset++;
if (bufferOffset == BUFFER_SIZE) {
hashBlock();
bufferOffset = 0;
}
}
void Sha256Class::write(uint8_t data) {
++byteCount;
addUncounted(data);
}
void Sha256Class::pad() {
// Implement SHA-256 padding (fips180-2 §5.1.1)
// Pad with 0x80 followed by 0x00 until the end of the block
addUncounted(0x80);
while (bufferOffset != 56) addUncounted(0x00);
// Append length in the last 8 bytes
addUncounted(0); // We're only using 32 bit lengths
addUncounted(0); // But SHA-1 supports 64 bit lengths
addUncounted(0); // So zero pad the top bits
addUncounted(byteCount >> 29); // Shifting to multiply by 8
addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
addUncounted(byteCount >> 13); // byte.
addUncounted(byteCount >> 5);
addUncounted(byteCount << 3);
} }
uint8_t* Sha256Class::result(void) { void Sha256Class::init(void)
// Pad to complete the last block {
pad(); memcpy_P(state.b,sha256InitState,32);
byteCount = 0;
// Swap byte order back bufferOffset = 0;
for (int i=0; i<8; i++) { }
uint32_t a,b;
a=state.w[i]; uint32_t Sha256Class::ror32(uint32_t number, uint8_t bits)
b=a<<24; {
b|=(a<<8) & 0x00ff0000; return ((number << (32-bits)) | (number >> bits));
b|=(a>>8) & 0x0000ff00; }
b|=a>>24;
state.w[i]=b; void Sha256Class::hashBlock()
} {
uint8_t i;
// Return pointer to hash (20 characters) uint32_t a,b,c,d,e,f,g,h,t1,t2;
return state.b;
a=state.w[0];
b=state.w[1];
c=state.w[2];
d=state.w[3];
e=state.w[4];
f=state.w[5];
g=state.w[6];
h=state.w[7];
for (i=0; i<64; i++) {
if (i>=16) {
t1 = buffer.w[i&15] + buffer.w[(i-7)&15];
t2 = buffer.w[(i-2)&15];
t1 += ror32(t2,17) ^ ror32(t2,19) ^ (t2>>10);
t2 = buffer.w[(i-15)&15];
t1 += ror32(t2,7) ^ ror32(t2,18) ^ (t2>>3);
buffer.w[i&15] = t1;
}
t1 = h;
t1 += ror32(e,6) ^ ror32(e,11) ^ ror32(e,25); // ∑1(e)
t1 += g ^ (e & (g ^ f)); // Ch(e,f,g)
t1 += pgm_read_dword(sha256K+i); // Ki
t1 += buffer.w[i&15]; // Wi
t2 = ror32(a,2) ^ ror32(a,13) ^ ror32(a,22); // ∑0(a)
t2 += ((b & c) | (a & (b | c))); // Maj(a,b,c)
h=g;
g=f;
f=e;
e=d+t1;
d=c;
c=b;
b=a;
a=t1+t2;
}
state.w[0] += a;
state.w[1] += b;
state.w[2] += c;
state.w[3] += d;
state.w[4] += e;
state.w[5] += f;
state.w[6] += g;
state.w[7] += h;
}
void Sha256Class::addUncounted(uint8_t data)
{
buffer.b[bufferOffset ^ 3] = data;
bufferOffset++;
if (bufferOffset == BUFFER_SIZE) {
hashBlock();
bufferOffset = 0;
}
}
void Sha256Class::write(uint8_t data)
{
++byteCount;
addUncounted(data);
}
void Sha256Class::pad()
{
// Implement SHA-256 padding (fips180-2 §5.1.1)
// Pad with 0x80 followed by 0x00 until the end of the block
addUncounted(0x80);
while (bufferOffset != 56) {
addUncounted(0x00);
}
// Append length in the last 8 bytes
addUncounted(0); // We're only using 32 bit lengths
addUncounted(0); // But SHA-1 supports 64 bit lengths
addUncounted(0); // So zero pad the top bits
addUncounted(byteCount >> 29); // Shifting to multiply by 8
addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
addUncounted(byteCount >> 13); // byte.
addUncounted(byteCount >> 5);
addUncounted(byteCount << 3);
}
uint8_t* Sha256Class::result(void)
{
// Pad to complete the last block
pad();
// Swap byte order back
for (int i=0; i<8; i++) {
uint32_t a,b;
a=state.w[i];
b=a<<24;
b|=(a<<8) & 0x00ff0000;
b|=(a>>8) & 0x0000ff00;
b|=a>>24;
state.w[i]=b;
}
// Return pointer to hash (20 characters)
return state.b;
} }
#define HMAC_IPAD 0x36 #define HMAC_IPAD 0x36
@@ -141,32 +170,40 @@ uint8_t* Sha256Class::result(void) {
uint8_t keyBuffer[BLOCK_LENGTH]; // K0 in FIPS-198a uint8_t keyBuffer[BLOCK_LENGTH]; // K0 in FIPS-198a
uint8_t innerHash[HASH_LENGTH]; uint8_t innerHash[HASH_LENGTH];
void Sha256Class::initHmac(const uint8_t* key, int keyLength) { void Sha256Class::initHmac(const uint8_t* key, int keyLength)
uint8_t i; {
memset(keyBuffer,0,BLOCK_LENGTH); uint8_t i;
if (keyLength > BLOCK_LENGTH) { memset(keyBuffer,0,BLOCK_LENGTH);
// Hash long keys if (keyLength > BLOCK_LENGTH) {
init(); // Hash long keys
for (;keyLength--;) write(*key++); init();
memcpy(keyBuffer,result(),HASH_LENGTH); for (; keyLength--;) {
} else { write(*key++);
// Block length keys are used as is }
memcpy(keyBuffer,key,keyLength); memcpy(keyBuffer,result(),HASH_LENGTH);
} } else {
// Start inner hash // Block length keys are used as is
init(); memcpy(keyBuffer,key,keyLength);
for (i=0; i<BLOCK_LENGTH; i++) { }
write(keyBuffer[i] ^ HMAC_IPAD); // Start inner hash
} init();
for (i=0; i<BLOCK_LENGTH; i++) {
write(keyBuffer[i] ^ HMAC_IPAD);
}
} }
uint8_t* Sha256Class::resultHmac(void) { uint8_t* Sha256Class::resultHmac(void)
uint8_t i; {
// Complete inner hash uint8_t i;
memcpy(innerHash,result(),HASH_LENGTH); // Complete inner hash
// Calculate outer hash memcpy(innerHash,result(),HASH_LENGTH);
init(); // Calculate outer hash
for (i=0; i<BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD); init();
for (i=0; i<HASH_LENGTH; i++) write(innerHash[i]); for (i=0; i<BLOCK_LENGTH; i++) {
return result(); write(keyBuffer[i] ^ HMAC_OPAD);
}
for (i=0; i<HASH_LENGTH; i++) {
write(innerHash[i]);
}
return result();
} }
+22 -21
View File
@@ -8,33 +8,34 @@
#define BLOCK_LENGTH 64 #define BLOCK_LENGTH 64
union _buffer { union _buffer {
uint8_t b[BLOCK_LENGTH]; uint8_t b[BLOCK_LENGTH];
uint32_t w[BLOCK_LENGTH/4]; uint32_t w[BLOCK_LENGTH/4];
}; };
union _state { union _state {
uint8_t b[HASH_LENGTH]; uint8_t b[HASH_LENGTH];
uint32_t w[HASH_LENGTH/4]; uint32_t w[HASH_LENGTH/4];
}; };
class Sha256Class class Sha256Class
{ {
public: public:
void init(void); Sha256Class(); // Constructor
void initHmac(const uint8_t* secret, int secretLength); void init(void);
uint8_t* result(void); void initHmac(const uint8_t* secret, int secretLength);
uint8_t* resultHmac(void); uint8_t* result(void);
void write(uint8_t); uint8_t* resultHmac(void);
private: void write(uint8_t);
void pad(); private:
void addUncounted(uint8_t data); void pad();
void hashBlock(); void addUncounted(uint8_t data);
uint32_t ror32(uint32_t number, uint8_t bits); void hashBlock();
_buffer buffer; uint32_t ror32(uint32_t number, uint8_t bits);
uint8_t bufferOffset; _buffer buffer;
_state state; uint8_t bufferOffset;
uint32_t byteCount; _state state;
uint8_t keyBuffer[BLOCK_LENGTH]; uint32_t byteCount;
uint8_t innerHash[HASH_LENGTH]; uint8_t keyBuffer[BLOCK_LENGTH];
uint8_t innerHash[HASH_LENGTH];
}; };
#endif #endif
+444 -421
View File
@@ -39,10 +39,10 @@
* @brief struct for mapping digital pins * @brief struct for mapping digital pins
*/ */
struct pin_map_t { struct pin_map_t {
volatile uint8_t* ddr; /**< address of DDR for this pin */ volatile uint8_t* ddr; /**< address of DDR for this pin */
volatile uint8_t* pin; /**< address of PIN for this pin */ volatile uint8_t* pin; /**< address of PIN for this pin */
volatile uint8_t* port; /**< address of PORT for this pin */ volatile uint8_t* port; /**< address of PORT for this pin */
uint8_t bit; /**< bit number for this pin */ uint8_t bit; /**< bit number for this pin */
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__AVR_ATmega168__)\ #if defined(__AVR_ATmega168__)\
@@ -50,102 +50,102 @@ struct pin_map_t {
||defined(__AVR_ATmega328P__) ||defined(__AVR_ATmega328P__)
// 168 and 328 Arduinos // 168 and 328 Arduinos
const static pin_map_t pinMap[] = { const static pin_map_t pinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0 {&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1 {&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2 {&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3 {&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4 {&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5 {&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6 {&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7 {&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRB, &PINB, &PORTB, 0}, // B0 8 {&DDRB, &PINB, &PORTB, 0}, // B0 8
{&DDRB, &PINB, &PORTB, 1}, // B1 9 {&DDRB, &PINB, &PORTB, 1}, // B1 9
{&DDRB, &PINB, &PORTB, 2}, // B2 10 {&DDRB, &PINB, &PORTB, 2}, // B2 10
{&DDRB, &PINB, &PORTB, 3}, // B3 11 {&DDRB, &PINB, &PORTB, 3}, // B3 11
{&DDRB, &PINB, &PORTB, 4}, // B4 12 {&DDRB, &PINB, &PORTB, 4}, // B4 12
{&DDRB, &PINB, &PORTB, 5}, // B5 13 {&DDRB, &PINB, &PORTB, 5}, // B5 13
{&DDRC, &PINC, &PORTC, 0}, // C0 14 {&DDRC, &PINC, &PORTC, 0}, // C0 14
{&DDRC, &PINC, &PORTC, 1}, // C1 15 {&DDRC, &PINC, &PORTC, 1}, // C1 15
{&DDRC, &PINC, &PORTC, 2}, // C2 16 {&DDRC, &PINC, &PORTC, 2}, // C2 16
{&DDRC, &PINC, &PORTC, 3}, // C3 17 {&DDRC, &PINC, &PORTC, 3}, // C3 17
{&DDRC, &PINC, &PORTC, 4}, // C4 18 {&DDRC, &PINC, &PORTC, 4}, // C4 18
{&DDRC, &PINC, &PORTC, 5} // C5 19 {&DDRC, &PINC, &PORTC, 5} // C5 19
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#elif defined(__AVR_ATmega1280__)\ #elif defined(__AVR_ATmega1280__)\
|| defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2560__)
// Mega // Mega
static const pin_map_t pinMap[] = { static const pin_map_t pinMap[] = {
{&DDRE, &PINE, &PORTE, 0}, // E0 0 {&DDRE, &PINE, &PORTE, 0}, // E0 0
{&DDRE, &PINE, &PORTE, 1}, // E1 1 {&DDRE, &PINE, &PORTE, 1}, // E1 1
{&DDRE, &PINE, &PORTE, 4}, // E4 2 {&DDRE, &PINE, &PORTE, 4}, // E4 2
{&DDRE, &PINE, &PORTE, 5}, // E5 3 {&DDRE, &PINE, &PORTE, 5}, // E5 3
{&DDRG, &PING, &PORTG, 5}, // G5 4 {&DDRG, &PING, &PORTG, 5}, // G5 4
{&DDRE, &PINE, &PORTE, 3}, // E3 5 {&DDRE, &PINE, &PORTE, 3}, // E3 5
{&DDRH, &PINH, &PORTH, 3}, // H3 6 {&DDRH, &PINH, &PORTH, 3}, // H3 6
{&DDRH, &PINH, &PORTH, 4}, // H4 7 {&DDRH, &PINH, &PORTH, 4}, // H4 7
{&DDRH, &PINH, &PORTH, 5}, // H5 8 {&DDRH, &PINH, &PORTH, 5}, // H5 8
{&DDRH, &PINH, &PORTH, 6}, // H6 9 {&DDRH, &PINH, &PORTH, 6}, // H6 9
{&DDRB, &PINB, &PORTB, 4}, // B4 10 {&DDRB, &PINB, &PORTB, 4}, // B4 10
{&DDRB, &PINB, &PORTB, 5}, // B5 11 {&DDRB, &PINB, &PORTB, 5}, // B5 11
{&DDRB, &PINB, &PORTB, 6}, // B6 12 {&DDRB, &PINB, &PORTB, 6}, // B6 12
{&DDRB, &PINB, &PORTB, 7}, // B7 13 {&DDRB, &PINB, &PORTB, 7}, // B7 13
{&DDRJ, &PINJ, &PORTJ, 1}, // J1 14 {&DDRJ, &PINJ, &PORTJ, 1}, // J1 14
{&DDRJ, &PINJ, &PORTJ, 0}, // J0 15 {&DDRJ, &PINJ, &PORTJ, 0}, // J0 15
{&DDRH, &PINH, &PORTH, 1}, // H1 16 {&DDRH, &PINH, &PORTH, 1}, // H1 16
{&DDRH, &PINH, &PORTH, 0}, // H0 17 {&DDRH, &PINH, &PORTH, 0}, // H0 17
{&DDRD, &PIND, &PORTD, 3}, // D3 18 {&DDRD, &PIND, &PORTD, 3}, // D3 18
{&DDRD, &PIND, &PORTD, 2}, // D2 19 {&DDRD, &PIND, &PORTD, 2}, // D2 19
{&DDRD, &PIND, &PORTD, 1}, // D1 20 {&DDRD, &PIND, &PORTD, 1}, // D1 20
{&DDRD, &PIND, &PORTD, 0}, // D0 21 {&DDRD, &PIND, &PORTD, 0}, // D0 21
{&DDRA, &PINA, &PORTA, 0}, // A0 22 {&DDRA, &PINA, &PORTA, 0}, // A0 22
{&DDRA, &PINA, &PORTA, 1}, // A1 23 {&DDRA, &PINA, &PORTA, 1}, // A1 23
{&DDRA, &PINA, &PORTA, 2}, // A2 24 {&DDRA, &PINA, &PORTA, 2}, // A2 24
{&DDRA, &PINA, &PORTA, 3}, // A3 25 {&DDRA, &PINA, &PORTA, 3}, // A3 25
{&DDRA, &PINA, &PORTA, 4}, // A4 26 {&DDRA, &PINA, &PORTA, 4}, // A4 26
{&DDRA, &PINA, &PORTA, 5}, // A5 27 {&DDRA, &PINA, &PORTA, 5}, // A5 27
{&DDRA, &PINA, &PORTA, 6}, // A6 28 {&DDRA, &PINA, &PORTA, 6}, // A6 28
{&DDRA, &PINA, &PORTA, 7}, // A7 29 {&DDRA, &PINA, &PORTA, 7}, // A7 29
{&DDRC, &PINC, &PORTC, 7}, // C7 30 {&DDRC, &PINC, &PORTC, 7}, // C7 30
{&DDRC, &PINC, &PORTC, 6}, // C6 31 {&DDRC, &PINC, &PORTC, 6}, // C6 31
{&DDRC, &PINC, &PORTC, 5}, // C5 32 {&DDRC, &PINC, &PORTC, 5}, // C5 32
{&DDRC, &PINC, &PORTC, 4}, // C4 33 {&DDRC, &PINC, &PORTC, 4}, // C4 33
{&DDRC, &PINC, &PORTC, 3}, // C3 34 {&DDRC, &PINC, &PORTC, 3}, // C3 34
{&DDRC, &PINC, &PORTC, 2}, // C2 35 {&DDRC, &PINC, &PORTC, 2}, // C2 35
{&DDRC, &PINC, &PORTC, 1}, // C1 36 {&DDRC, &PINC, &PORTC, 1}, // C1 36
{&DDRC, &PINC, &PORTC, 0}, // C0 37 {&DDRC, &PINC, &PORTC, 0}, // C0 37
{&DDRD, &PIND, &PORTD, 7}, // D7 38 {&DDRD, &PIND, &PORTD, 7}, // D7 38
{&DDRG, &PING, &PORTG, 2}, // G2 39 {&DDRG, &PING, &PORTG, 2}, // G2 39
{&DDRG, &PING, &PORTG, 1}, // G1 40 {&DDRG, &PING, &PORTG, 1}, // G1 40
{&DDRG, &PING, &PORTG, 0}, // G0 41 {&DDRG, &PING, &PORTG, 0}, // G0 41
{&DDRL, &PINL, &PORTL, 7}, // L7 42 {&DDRL, &PINL, &PORTL, 7}, // L7 42
{&DDRL, &PINL, &PORTL, 6}, // L6 43 {&DDRL, &PINL, &PORTL, 6}, // L6 43
{&DDRL, &PINL, &PORTL, 5}, // L5 44 {&DDRL, &PINL, &PORTL, 5}, // L5 44
{&DDRL, &PINL, &PORTL, 4}, // L4 45 {&DDRL, &PINL, &PORTL, 4}, // L4 45
{&DDRL, &PINL, &PORTL, 3}, // L3 46 {&DDRL, &PINL, &PORTL, 3}, // L3 46
{&DDRL, &PINL, &PORTL, 2}, // L2 47 {&DDRL, &PINL, &PORTL, 2}, // L2 47
{&DDRL, &PINL, &PORTL, 1}, // L1 48 {&DDRL, &PINL, &PORTL, 1}, // L1 48
{&DDRL, &PINL, &PORTL, 0}, // L0 49 {&DDRL, &PINL, &PORTL, 0}, // L0 49
{&DDRB, &PINB, &PORTB, 3}, // B3 50 {&DDRB, &PINB, &PORTB, 3}, // B3 50
{&DDRB, &PINB, &PORTB, 2}, // B2 51 {&DDRB, &PINB, &PORTB, 2}, // B2 51
{&DDRB, &PINB, &PORTB, 1}, // B1 52 {&DDRB, &PINB, &PORTB, 1}, // B1 52
{&DDRB, &PINB, &PORTB, 0}, // B0 53 {&DDRB, &PINB, &PORTB, 0}, // B0 53
{&DDRF, &PINF, &PORTF, 0}, // F0 54 {&DDRF, &PINF, &PORTF, 0}, // F0 54
{&DDRF, &PINF, &PORTF, 1}, // F1 55 {&DDRF, &PINF, &PORTF, 1}, // F1 55
{&DDRF, &PINF, &PORTF, 2}, // F2 56 {&DDRF, &PINF, &PORTF, 2}, // F2 56
{&DDRF, &PINF, &PORTF, 3}, // F3 57 {&DDRF, &PINF, &PORTF, 3}, // F3 57
{&DDRF, &PINF, &PORTF, 4}, // F4 58 {&DDRF, &PINF, &PORTF, 4}, // F4 58
{&DDRF, &PINF, &PORTF, 5}, // F5 59 {&DDRF, &PINF, &PORTF, 5}, // F5 59
{&DDRF, &PINF, &PORTF, 6}, // F6 60 {&DDRF, &PINF, &PORTF, 6}, // F6 60
{&DDRF, &PINF, &PORTF, 7}, // F7 61 {&DDRF, &PINF, &PORTF, 7}, // F7 61
{&DDRK, &PINK, &PORTK, 0}, // K0 62 {&DDRK, &PINK, &PORTK, 0}, // K0 62
{&DDRK, &PINK, &PORTK, 1}, // K1 63 {&DDRK, &PINK, &PORTK, 1}, // K1 63
{&DDRK, &PINK, &PORTK, 2}, // K2 64 {&DDRK, &PINK, &PORTK, 2}, // K2 64
{&DDRK, &PINK, &PORTK, 3}, // K3 65 {&DDRK, &PINK, &PORTK, 3}, // K3 65
{&DDRK, &PINK, &PORTK, 4}, // K4 66 {&DDRK, &PINK, &PORTK, 4}, // K4 66
{&DDRK, &PINK, &PORTK, 5}, // K5 67 {&DDRK, &PINK, &PORTK, 5}, // K5 67
{&DDRK, &PINK, &PORTK, 6}, // K6 68 {&DDRK, &PINK, &PORTK, 6}, // K6 68
{&DDRK, &PINK, &PORTK, 7} // K7 69 {&DDRK, &PINK, &PORTK, 7} // K7 69
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#elif defined(__AVR_ATmega1284P__)\ #elif defined(__AVR_ATmega1284P__)\
@@ -160,110 +160,110 @@ static const pin_map_t pinMap[] = {
#ifdef defined(VARIANT_MIGHTY) #ifdef defined(VARIANT_MIGHTY)
// Mighty Layout // Mighty Layout
static const pin_map_t pinMap[] = { static const pin_map_t pinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0 {&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1 {&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2 {&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3 {&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 4}, // B4 4 {&DDRB, &PINB, &PORTB, 4}, // B4 4
{&DDRB, &PINB, &PORTB, 5}, // B5 5 {&DDRB, &PINB, &PORTB, 5}, // B5 5
{&DDRB, &PINB, &PORTB, 6}, // B6 6 {&DDRB, &PINB, &PORTB, 6}, // B6 6
{&DDRB, &PINB, &PORTB, 7}, // B7 7 {&DDRB, &PINB, &PORTB, 7}, // B7 7
{&DDRD, &PIND, &PORTD, 0}, // D0 8 {&DDRD, &PIND, &PORTD, 0}, // D0 8
{&DDRD, &PIND, &PORTD, 1}, // D1 9 {&DDRD, &PIND, &PORTD, 1}, // D1 9
{&DDRD, &PIND, &PORTD, 2}, // D2 10 {&DDRD, &PIND, &PORTD, 2}, // D2 10
{&DDRD, &PIND, &PORTD, 3}, // D3 11 {&DDRD, &PIND, &PORTD, 3}, // D3 11
{&DDRD, &PIND, &PORTD, 4}, // D4 12 {&DDRD, &PIND, &PORTD, 4}, // D4 12
{&DDRD, &PIND, &PORTD, 5}, // D5 13 {&DDRD, &PIND, &PORTD, 5}, // D5 13
{&DDRD, &PIND, &PORTD, 6}, // D6 14 {&DDRD, &PIND, &PORTD, 6}, // D6 14
{&DDRD, &PIND, &PORTD, 7}, // D7 15 {&DDRD, &PIND, &PORTD, 7}, // D7 15
{&DDRC, &PINC, &PORTC, 0}, // C0 16 {&DDRC, &PINC, &PORTC, 0}, // C0 16
{&DDRC, &PINC, &PORTC, 1}, // C1 17 {&DDRC, &PINC, &PORTC, 1}, // C1 17
{&DDRC, &PINC, &PORTC, 2}, // C2 18 {&DDRC, &PINC, &PORTC, 2}, // C2 18
{&DDRC, &PINC, &PORTC, 3}, // C3 19 {&DDRC, &PINC, &PORTC, 3}, // C3 19
{&DDRC, &PINC, &PORTC, 4}, // C4 20 {&DDRC, &PINC, &PORTC, 4}, // C4 20
{&DDRC, &PINC, &PORTC, 5}, // C5 21 {&DDRC, &PINC, &PORTC, 5}, // C5 21
{&DDRC, &PINC, &PORTC, 6}, // C6 22 {&DDRC, &PINC, &PORTC, 6}, // C6 22
{&DDRC, &PINC, &PORTC, 7}, // C7 23 {&DDRC, &PINC, &PORTC, 7}, // C7 23
{&DDRA, &PINA, &PORTA, 0}, // A0 24 {&DDRA, &PINA, &PORTA, 0}, // A0 24
{&DDRA, &PINA, &PORTA, 1}, // A1 25 {&DDRA, &PINA, &PORTA, 1}, // A1 25
{&DDRA, &PINA, &PORTA, 2}, // A2 26 {&DDRA, &PINA, &PORTA, 2}, // A2 26
{&DDRA, &PINA, &PORTA, 3}, // A3 27 {&DDRA, &PINA, &PORTA, 3}, // A3 27
{&DDRA, &PINA, &PORTA, 4}, // A4 28 {&DDRA, &PINA, &PORTA, 4}, // A4 28
{&DDRA, &PINA, &PORTA, 5}, // A5 29 {&DDRA, &PINA, &PORTA, 5}, // A5 29
{&DDRA, &PINA, &PORTA, 6}, // A6 30 {&DDRA, &PINA, &PORTA, 6}, // A6 30
{&DDRA, &PINA, &PORTA, 7} // A7 31 {&DDRA, &PINA, &PORTA, 7} // A7 31
}; };
#elif defined(VARIANT_BOBUINO) #elif defined(VARIANT_BOBUINO)
// Bobuino Layout // Bobuino Layout
static const pin_map_t pinMap[] = { static const pin_map_t pinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0 {&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1 {&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2 {&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3 {&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRB, &PINB, &PORTB, 0}, // B0 4 {&DDRB, &PINB, &PORTB, 0}, // B0 4
{&DDRB, &PINB, &PORTB, 1}, // B1 5 {&DDRB, &PINB, &PORTB, 1}, // B1 5
{&DDRB, &PINB, &PORTB, 2}, // B2 6 {&DDRB, &PINB, &PORTB, 2}, // B2 6
{&DDRB, &PINB, &PORTB, 3}, // B3 7 {&DDRB, &PINB, &PORTB, 3}, // B3 7
{&DDRD, &PIND, &PORTD, 5}, // D5 8 {&DDRD, &PIND, &PORTD, 5}, // D5 8
{&DDRD, &PIND, &PORTD, 6}, // D6 9 {&DDRD, &PIND, &PORTD, 6}, // D6 9
{&DDRB, &PINB, &PORTB, 4}, // B4 10 {&DDRB, &PINB, &PORTB, 4}, // B4 10
{&DDRB, &PINB, &PORTB, 5}, // B5 11 {&DDRB, &PINB, &PORTB, 5}, // B5 11
{&DDRB, &PINB, &PORTB, 6}, // B6 12 {&DDRB, &PINB, &PORTB, 6}, // B6 12
{&DDRB, &PINB, &PORTB, 7}, // B7 13 {&DDRB, &PINB, &PORTB, 7}, // B7 13
{&DDRA, &PINA, &PORTA, 7}, // A7 14 {&DDRA, &PINA, &PORTA, 7}, // A7 14
{&DDRA, &PINA, &PORTA, 6}, // A6 15 {&DDRA, &PINA, &PORTA, 6}, // A6 15
{&DDRA, &PINA, &PORTA, 5}, // A5 16 {&DDRA, &PINA, &PORTA, 5}, // A5 16
{&DDRA, &PINA, &PORTA, 4}, // A4 17 {&DDRA, &PINA, &PORTA, 4}, // A4 17
{&DDRA, &PINA, &PORTA, 3}, // A3 18 {&DDRA, &PINA, &PORTA, 3}, // A3 18
{&DDRA, &PINA, &PORTA, 2}, // A2 19 {&DDRA, &PINA, &PORTA, 2}, // A2 19
{&DDRA, &PINA, &PORTA, 1}, // A1 20 {&DDRA, &PINA, &PORTA, 1}, // A1 20
{&DDRA, &PINA, &PORTA, 0}, // A0 21 {&DDRA, &PINA, &PORTA, 0}, // A0 21
{&DDRC, &PINC, &PORTC, 0}, // C0 22 {&DDRC, &PINC, &PORTC, 0}, // C0 22
{&DDRC, &PINC, &PORTC, 1}, // C1 23 {&DDRC, &PINC, &PORTC, 1}, // C1 23
{&DDRC, &PINC, &PORTC, 2}, // C2 24 {&DDRC, &PINC, &PORTC, 2}, // C2 24
{&DDRC, &PINC, &PORTC, 3}, // C3 25 {&DDRC, &PINC, &PORTC, 3}, // C3 25
{&DDRC, &PINC, &PORTC, 4}, // C4 26 {&DDRC, &PINC, &PORTC, 4}, // C4 26
{&DDRC, &PINC, &PORTC, 5}, // C5 27 {&DDRC, &PINC, &PORTC, 5}, // C5 27
{&DDRC, &PINC, &PORTC, 6}, // C6 28 {&DDRC, &PINC, &PORTC, 6}, // C6 28
{&DDRC, &PINC, &PORTC, 7}, // C7 29 {&DDRC, &PINC, &PORTC, 7}, // C7 29
{&DDRD, &PIND, &PORTD, 4}, // D4 30 {&DDRD, &PIND, &PORTD, 4}, // D4 30
{&DDRD, &PIND, &PORTD, 7} // D7 31 {&DDRD, &PIND, &PORTD, 7} // D7 31
}; };
#elif defined(VARIANT_STANDARD) #elif defined(VARIANT_STANDARD)
// Standard Layout // Standard Layout
static const pin_map_t pinMap[] = { static const pin_map_t pinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0 {&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1 {&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2 {&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3 {&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 4}, // B4 4 {&DDRB, &PINB, &PORTB, 4}, // B4 4
{&DDRB, &PINB, &PORTB, 5}, // B5 5 {&DDRB, &PINB, &PORTB, 5}, // B5 5
{&DDRB, &PINB, &PORTB, 6}, // B6 6 {&DDRB, &PINB, &PORTB, 6}, // B6 6
{&DDRB, &PINB, &PORTB, 7}, // B7 7 {&DDRB, &PINB, &PORTB, 7}, // B7 7
{&DDRD, &PIND, &PORTD, 0}, // D0 8 {&DDRD, &PIND, &PORTD, 0}, // D0 8
{&DDRD, &PIND, &PORTD, 1}, // D1 9 {&DDRD, &PIND, &PORTD, 1}, // D1 9
{&DDRD, &PIND, &PORTD, 2}, // D2 10 {&DDRD, &PIND, &PORTD, 2}, // D2 10
{&DDRD, &PIND, &PORTD, 3}, // D3 11 {&DDRD, &PIND, &PORTD, 3}, // D3 11
{&DDRD, &PIND, &PORTD, 4}, // D4 12 {&DDRD, &PIND, &PORTD, 4}, // D4 12
{&DDRD, &PIND, &PORTD, 5}, // D5 13 {&DDRD, &PIND, &PORTD, 5}, // D5 13
{&DDRD, &PIND, &PORTD, 6}, // D6 14 {&DDRD, &PIND, &PORTD, 6}, // D6 14
{&DDRD, &PIND, &PORTD, 7}, // D7 15 {&DDRD, &PIND, &PORTD, 7}, // D7 15
{&DDRC, &PINC, &PORTC, 0}, // C0 16 {&DDRC, &PINC, &PORTC, 0}, // C0 16
{&DDRC, &PINC, &PORTC, 1}, // C1 17 {&DDRC, &PINC, &PORTC, 1}, // C1 17
{&DDRC, &PINC, &PORTC, 2}, // C2 18 {&DDRC, &PINC, &PORTC, 2}, // C2 18
{&DDRC, &PINC, &PORTC, 3}, // C3 19 {&DDRC, &PINC, &PORTC, 3}, // C3 19
{&DDRC, &PINC, &PORTC, 4}, // C4 20 {&DDRC, &PINC, &PORTC, 4}, // C4 20
{&DDRC, &PINC, &PORTC, 5}, // C5 21 {&DDRC, &PINC, &PORTC, 5}, // C5 21
{&DDRC, &PINC, &PORTC, 6}, // C6 22 {&DDRC, &PINC, &PORTC, 6}, // C6 22
{&DDRC, &PINC, &PORTC, 7}, // C7 23 {&DDRC, &PINC, &PORTC, 7}, // C7 23
{&DDRA, &PINA, &PORTA, 7}, // A7 24 {&DDRA, &PINA, &PORTA, 7}, // A7 24
{&DDRA, &PINA, &PORTA, 6}, // A6 25 {&DDRA, &PINA, &PORTA, 6}, // A6 25
{&DDRA, &PINA, &PORTA, 5}, // A5 26 {&DDRA, &PINA, &PORTA, 5}, // A5 26
{&DDRA, &PINA, &PORTA, 4}, // A4 27 {&DDRA, &PINA, &PORTA, 4}, // A4 27
{&DDRA, &PINA, &PORTA, 3}, // A3 28 {&DDRA, &PINA, &PORTA, 3}, // A3 28
{&DDRA, &PINA, &PORTA, 2}, // A2 29 {&DDRA, &PINA, &PORTA, 2}, // A2 29
{&DDRA, &PINA, &PORTA, 1}, // A1 30 {&DDRA, &PINA, &PORTA, 1}, // A1 30
{&DDRA, &PINA, &PORTA, 0} // A0 31 {&DDRA, &PINA, &PORTA, 0} // A0 31
}; };
#else // VARIANT_MIGHTY #else // VARIANT_MIGHTY
#error Undefined variant 1284, 644, 324, 64, 32 #error Undefined variant 1284, 644, 324, 64, 32
@@ -273,66 +273,66 @@ static const pin_map_t pinMap[] = {
#ifdef CORE_TEENSY #ifdef CORE_TEENSY
// Teensy 2.0 // Teensy 2.0
static const pin_map_t pinMap[] = { static const pin_map_t pinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0 {&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1 {&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2 {&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3 {&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 7}, // B7 4 {&DDRB, &PINB, &PORTB, 7}, // B7 4
{&DDRD, &PIND, &PORTD, 0}, // D0 5 {&DDRD, &PIND, &PORTD, 0}, // D0 5
{&DDRD, &PIND, &PORTD, 1}, // D1 6 {&DDRD, &PIND, &PORTD, 1}, // D1 6
{&DDRD, &PIND, &PORTD, 2}, // D2 7 {&DDRD, &PIND, &PORTD, 2}, // D2 7
{&DDRD, &PIND, &PORTD, 3}, // D3 8 {&DDRD, &PIND, &PORTD, 3}, // D3 8
{&DDRC, &PINC, &PORTC, 6}, // C6 9 {&DDRC, &PINC, &PORTC, 6}, // C6 9
{&DDRC, &PINC, &PORTC, 7}, // C7 10 {&DDRC, &PINC, &PORTC, 7}, // C7 10
{&DDRD, &PIND, &PORTD, 6}, // D6 11 {&DDRD, &PIND, &PORTD, 6}, // D6 11
{&DDRD, &PIND, &PORTD, 7}, // D7 12 {&DDRD, &PIND, &PORTD, 7}, // D7 12
{&DDRB, &PINB, &PORTB, 4}, // B4 13 {&DDRB, &PINB, &PORTB, 4}, // B4 13
{&DDRB, &PINB, &PORTB, 5}, // B5 14 {&DDRB, &PINB, &PORTB, 5}, // B5 14
{&DDRB, &PINB, &PORTB, 6}, // B6 15 {&DDRB, &PINB, &PORTB, 6}, // B6 15
{&DDRF, &PINF, &PORTF, 7}, // F7 16 {&DDRF, &PINF, &PORTF, 7}, // F7 16
{&DDRF, &PINF, &PORTF, 6}, // F6 17 {&DDRF, &PINF, &PORTF, 6}, // F6 17
{&DDRF, &PINF, &PORTF, 5}, // F5 18 {&DDRF, &PINF, &PORTF, 5}, // F5 18
{&DDRF, &PINF, &PORTF, 4}, // F4 19 {&DDRF, &PINF, &PORTF, 4}, // F4 19
{&DDRF, &PINF, &PORTF, 1}, // F1 20 {&DDRF, &PINF, &PORTF, 1}, // F1 20
{&DDRF, &PINF, &PORTF, 0}, // F0 21 {&DDRF, &PINF, &PORTF, 0}, // F0 21
{&DDRD, &PIND, &PORTD, 4}, // D4 22 {&DDRD, &PIND, &PORTD, 4}, // D4 22
{&DDRD, &PIND, &PORTD, 5}, // D5 23 {&DDRD, &PIND, &PORTD, 5}, // D5 23
{&DDRE, &PINE, &PORTE, 6} // E6 24 {&DDRE, &PINE, &PORTE, 6} // E6 24
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#else // CORE_TEENSY #else // CORE_TEENSY
// Leonardo // Leonardo
static const pin_map_t pinMap[] = { static const pin_map_t pinMap[] = {
{&DDRD, &PIND, &PORTD, 2}, // D2 0 {&DDRD, &PIND, &PORTD, 2}, // D2 0
{&DDRD, &PIND, &PORTD, 3}, // D3 1 {&DDRD, &PIND, &PORTD, 3}, // D3 1
{&DDRD, &PIND, &PORTD, 1}, // D1 2 {&DDRD, &PIND, &PORTD, 1}, // D1 2
{&DDRD, &PIND, &PORTD, 0}, // D0 3 {&DDRD, &PIND, &PORTD, 0}, // D0 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4 {&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRC, &PINC, &PORTC, 6}, // C6 5 {&DDRC, &PINC, &PORTC, 6}, // C6 5
{&DDRD, &PIND, &PORTD, 7}, // D7 6 {&DDRD, &PIND, &PORTD, 7}, // D7 6
{&DDRE, &PINE, &PORTE, 6}, // E6 7 {&DDRE, &PINE, &PORTE, 6}, // E6 7
{&DDRB, &PINB, &PORTB, 4}, // B4 8 {&DDRB, &PINB, &PORTB, 4}, // B4 8
{&DDRB, &PINB, &PORTB, 5}, // B5 9 {&DDRB, &PINB, &PORTB, 5}, // B5 9
{&DDRB, &PINB, &PORTB, 6}, // B6 10 {&DDRB, &PINB, &PORTB, 6}, // B6 10
{&DDRB, &PINB, &PORTB, 7}, // B7 11 {&DDRB, &PINB, &PORTB, 7}, // B7 11
{&DDRD, &PIND, &PORTD, 6}, // D6 12 {&DDRD, &PIND, &PORTD, 6}, // D6 12
{&DDRC, &PINC, &PORTC, 7}, // C7 13 {&DDRC, &PINC, &PORTC, 7}, // C7 13
{&DDRB, &PINB, &PORTB, 3}, // B3 14 {&DDRB, &PINB, &PORTB, 3}, // B3 14
{&DDRB, &PINB, &PORTB, 1}, // B1 15 {&DDRB, &PINB, &PORTB, 1}, // B1 15
{&DDRB, &PINB, &PORTB, 2}, // B2 16 {&DDRB, &PINB, &PORTB, 2}, // B2 16
{&DDRB, &PINB, &PORTB, 0}, // B0 17 {&DDRB, &PINB, &PORTB, 0}, // B0 17
{&DDRF, &PINF, &PORTF, 7}, // F7 18 {&DDRF, &PINF, &PORTF, 7}, // F7 18
{&DDRF, &PINF, &PORTF, 6}, // F6 19 {&DDRF, &PINF, &PORTF, 6}, // F6 19
{&DDRF, &PINF, &PORTF, 5}, // F5 20 {&DDRF, &PINF, &PORTF, 5}, // F5 20
{&DDRF, &PINF, &PORTF, 4}, // F4 21 {&DDRF, &PINF, &PORTF, 4}, // F4 21
{&DDRF, &PINF, &PORTF, 1}, // F1 22 {&DDRF, &PINF, &PORTF, 1}, // F1 22
{&DDRF, &PINF, &PORTF, 0}, // F0 23 {&DDRF, &PINF, &PORTF, 0}, // F0 23
{&DDRD, &PIND, &PORTD, 4}, // D4 24 {&DDRD, &PIND, &PORTD, 4}, // D4 24
{&DDRD, &PIND, &PORTD, 7}, // D7 25 {&DDRD, &PIND, &PORTD, 7}, // D7 25
{&DDRB, &PINB, &PORTB, 4}, // B4 26 {&DDRB, &PINB, &PORTB, 4}, // B4 26
{&DDRB, &PINB, &PORTB, 5}, // B5 27 {&DDRB, &PINB, &PORTB, 5}, // B5 27
{&DDRB, &PINB, &PORTB, 6}, // B6 28 {&DDRB, &PINB, &PORTB, 6}, // B6 28
{&DDRD, &PIND, &PORTD, 6} // D6 29 {&DDRD, &PIND, &PORTD, 6} // D6 29
}; };
#endif // CORE_TEENSY #endif // CORE_TEENSY
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -340,52 +340,52 @@ static const pin_map_t pinMap[] = {
|| defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1286__)
// Teensy++ 1.0 & 2.0 // Teensy++ 1.0 & 2.0
static const pin_map_t pinMap[] = { static const pin_map_t pinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0 {&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1 {&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2 {&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3 {&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4 {&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5 {&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6 {&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7 {&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRE, &PINE, &PORTE, 0}, // E0 8 {&DDRE, &PINE, &PORTE, 0}, // E0 8
{&DDRE, &PINE, &PORTE, 1}, // E1 9 {&DDRE, &PINE, &PORTE, 1}, // E1 9
{&DDRC, &PINC, &PORTC, 0}, // C0 10 {&DDRC, &PINC, &PORTC, 0}, // C0 10
{&DDRC, &PINC, &PORTC, 1}, // C1 11 {&DDRC, &PINC, &PORTC, 1}, // C1 11
{&DDRC, &PINC, &PORTC, 2}, // C2 12 {&DDRC, &PINC, &PORTC, 2}, // C2 12
{&DDRC, &PINC, &PORTC, 3}, // C3 13 {&DDRC, &PINC, &PORTC, 3}, // C3 13
{&DDRC, &PINC, &PORTC, 4}, // C4 14 {&DDRC, &PINC, &PORTC, 4}, // C4 14
{&DDRC, &PINC, &PORTC, 5}, // C5 15 {&DDRC, &PINC, &PORTC, 5}, // C5 15
{&DDRC, &PINC, &PORTC, 6}, // C6 16 {&DDRC, &PINC, &PORTC, 6}, // C6 16
{&DDRC, &PINC, &PORTC, 7}, // C7 17 {&DDRC, &PINC, &PORTC, 7}, // C7 17
{&DDRE, &PINE, &PORTE, 6}, // E6 18 {&DDRE, &PINE, &PORTE, 6}, // E6 18
{&DDRE, &PINE, &PORTE, 7}, // E7 19 {&DDRE, &PINE, &PORTE, 7}, // E7 19
{&DDRB, &PINB, &PORTB, 0}, // B0 20 {&DDRB, &PINB, &PORTB, 0}, // B0 20
{&DDRB, &PINB, &PORTB, 1}, // B1 21 {&DDRB, &PINB, &PORTB, 1}, // B1 21
{&DDRB, &PINB, &PORTB, 2}, // B2 22 {&DDRB, &PINB, &PORTB, 2}, // B2 22
{&DDRB, &PINB, &PORTB, 3}, // B3 23 {&DDRB, &PINB, &PORTB, 3}, // B3 23
{&DDRB, &PINB, &PORTB, 4}, // B4 24 {&DDRB, &PINB, &PORTB, 4}, // B4 24
{&DDRB, &PINB, &PORTB, 5}, // B5 25 {&DDRB, &PINB, &PORTB, 5}, // B5 25
{&DDRB, &PINB, &PORTB, 6}, // B6 26 {&DDRB, &PINB, &PORTB, 6}, // B6 26
{&DDRB, &PINB, &PORTB, 7}, // B7 27 {&DDRB, &PINB, &PORTB, 7}, // B7 27
{&DDRA, &PINA, &PORTA, 0}, // A0 28 {&DDRA, &PINA, &PORTA, 0}, // A0 28
{&DDRA, &PINA, &PORTA, 1}, // A1 29 {&DDRA, &PINA, &PORTA, 1}, // A1 29
{&DDRA, &PINA, &PORTA, 2}, // A2 30 {&DDRA, &PINA, &PORTA, 2}, // A2 30
{&DDRA, &PINA, &PORTA, 3}, // A3 31 {&DDRA, &PINA, &PORTA, 3}, // A3 31
{&DDRA, &PINA, &PORTA, 4}, // A4 32 {&DDRA, &PINA, &PORTA, 4}, // A4 32
{&DDRA, &PINA, &PORTA, 5}, // A5 33 {&DDRA, &PINA, &PORTA, 5}, // A5 33
{&DDRA, &PINA, &PORTA, 6}, // A6 34 {&DDRA, &PINA, &PORTA, 6}, // A6 34
{&DDRA, &PINA, &PORTA, 7}, // A7 35 {&DDRA, &PINA, &PORTA, 7}, // A7 35
{&DDRE, &PINE, &PORTE, 4}, // E4 36 {&DDRE, &PINE, &PORTE, 4}, // E4 36
{&DDRE, &PINE, &PORTE, 5}, // E5 37 {&DDRE, &PINE, &PORTE, 5}, // E5 37
{&DDRF, &PINF, &PORTF, 0}, // F0 38 {&DDRF, &PINF, &PORTF, 0}, // F0 38
{&DDRF, &PINF, &PORTF, 1}, // F1 39 {&DDRF, &PINF, &PORTF, 1}, // F1 39
{&DDRF, &PINF, &PORTF, 2}, // F2 40 {&DDRF, &PINF, &PORTF, 2}, // F2 40
{&DDRF, &PINF, &PORTF, 3}, // F3 41 {&DDRF, &PINF, &PORTF, 3}, // F3 41
{&DDRF, &PINF, &PORTF, 4}, // F4 42 {&DDRF, &PINF, &PORTF, 4}, // F4 42
{&DDRF, &PINF, &PORTF, 5}, // F5 43 {&DDRF, &PINF, &PORTF, 5}, // F5 43
{&DDRF, &PINF, &PORTF, 6}, // F6 44 {&DDRF, &PINF, &PORTF, 6}, // F6 44
{&DDRF, &PINF, &PORTF, 7} // F7 45 {&DDRF, &PINF, &PORTF, 7} // F7 45
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#else // CPU type #else // CPU type
@@ -397,15 +397,16 @@ static const uint8_t digitalPinCount = sizeof(pinMap)/sizeof(pin_map_t);
//============================================================================== //==============================================================================
/** generate bad pin number error */ /** generate bad pin number error */
void badPinNumber(void) void badPinNumber(void)
__attribute__((error("Pin number is too large or not a constant"))); __attribute__((error("Pin number is too large or not a constant")));
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Check for valid pin number /** Check for valid pin number
* @param[in] pin Number of pin to be checked. * @param[in] pin Number of pin to be checked.
*/ */
static ALWAYS_INLINE void badPinCheck(uint8_t pin) { static ALWAYS_INLINE void badPinCheck(uint8_t pin)
if (!__builtin_constant_p(pin) || pin >= digitalPinCount) { {
badPinNumber(); if (!__builtin_constant_p(pin) || pin >= digitalPinCount) {
} badPinNumber();
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** fast write helper /** fast write helper
@@ -413,29 +414,31 @@ static ALWAYS_INLINE void badPinCheck(uint8_t pin) {
* @param[in] bit bit number to write * @param[in] bit bit number to write
* @param[in] level value for bit * @param[in] level value for bit
*/ */
static ALWAYS_INLINE void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) { static ALWAYS_INLINE void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level)
uint8_t oldSREG; {
if (address > (uint8_t*)0X5F) { uint8_t oldSREG;
oldSREG = SREG; if (address > (uint8_t*)0X5F) {
cli(); oldSREG = SREG;
} cli();
if (level) { }
*address |= 1 << bit; if (level) {
} else { *address |= 1 << bit;
*address &= ~(1 << bit); } else {
} *address &= ~(1 << bit);
if (address > (uint8_t*)0X5F) { }
SREG = oldSREG; if (address > (uint8_t*)0X5F) {
} SREG = oldSREG;
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** read pin value /** read pin value
* @param[in] pin Arduino pin number * @param[in] pin Arduino pin number
* @return value read * @return value read
*/ */
static ALWAYS_INLINE bool fastDigitalRead(uint8_t pin) { static ALWAYS_INLINE bool fastDigitalRead(uint8_t pin)
badPinCheck(pin); {
return (*pinMap[pin].pin >> pinMap[pin].bit) & 1; badPinCheck(pin);
return (*pinMap[pin].pin >> pinMap[pin].bit) & 1;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** toggle a pin /** toggle a pin
@@ -444,24 +447,26 @@ static ALWAYS_INLINE bool fastDigitalRead(uint8_t pin) {
* If the pin is in output mode toggle the pin level. * If the pin is in output mode toggle the pin level.
* If the pin is in input mode toggle the state of the 20K pullup. * If the pin is in input mode toggle the state of the 20K pullup.
*/ */
static ALWAYS_INLINE void fastDigitalToggle(uint8_t pin) { static ALWAYS_INLINE void fastDigitalToggle(uint8_t pin)
badPinCheck(pin); {
if (pinMap[pin].pin > (uint8_t*)0X5F) { badPinCheck(pin);
// must write bit to high address port if (pinMap[pin].pin > (uint8_t*)0X5F) {
*pinMap[pin].pin = 1 << pinMap[pin].bit; // must write bit to high address port
} else { *pinMap[pin].pin = 1 << pinMap[pin].bit;
// will compile to sbi and PIN register will not be read. } else {
*pinMap[pin].pin |= 1 << pinMap[pin].bit; // will compile to sbi and PIN register will not be read.
} *pinMap[pin].pin |= 1 << pinMap[pin].bit;
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Set pin value /** Set pin value
* @param[in] pin Arduino pin number * @param[in] pin Arduino pin number
* @param[in] level value to write * @param[in] level value to write
*/ */
static ALWAYS_INLINE void fastDigitalWrite(uint8_t pin, bool level) { static ALWAYS_INLINE void fastDigitalWrite(uint8_t pin, bool level)
badPinCheck(pin); {
fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, level); badPinCheck(pin);
fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, level);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** set pin mode /** set pin mode
@@ -470,9 +475,10 @@ static ALWAYS_INLINE void fastDigitalWrite(uint8_t pin, bool level) {
* *
* fastPinMode does not enable or disable the 20K pullup for input mode. * fastPinMode does not enable or disable the 20K pullup for input mode.
*/ */
static ALWAYS_INLINE void fastPinMode(uint8_t pin, bool mode) { static ALWAYS_INLINE void fastPinMode(uint8_t pin, bool mode)
badPinCheck(pin); {
fastBitWriteSafe(pinMap[pin].ddr, pinMap[pin].bit, mode); badPinCheck(pin);
fastBitWriteSafe(pinMap[pin].ddr, pinMap[pin].bit, mode);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** set pin configuration /** set pin configuration
@@ -481,9 +487,10 @@ static ALWAYS_INLINE void fastPinMode(uint8_t pin, bool mode) {
* @param[in] level If mode is output, set level high/low. * @param[in] level If mode is output, set level high/low.
* If mode is input, enable or disable the pin's 20K pullup. * If mode is input, enable or disable the pin's 20K pullup.
*/ */
static ALWAYS_INLINE void fastPinConfig(uint8_t pin, bool mode, bool level) { static ALWAYS_INLINE void fastPinConfig(uint8_t pin, bool mode, bool level)
fastPinMode(pin, mode); {
fastDigitalWrite(pin, level); fastPinMode(pin, mode);
fastDigitalWrite(pin, level);
} }
//============================================================================== //==============================================================================
/** /**
@@ -491,96 +498,112 @@ static ALWAYS_INLINE void fastPinConfig(uint8_t pin, bool mode, bool level) {
* @brief Fast AVR digital port I/O * @brief Fast AVR digital port I/O
*/ */
template<uint8_t PinNumber> template<uint8_t PinNumber>
class DigitalPin { class DigitalPin
public: {
//---------------------------------------------------------------------------- public:
/** Constructor */ //----------------------------------------------------------------------------
DigitalPin() {} /** Constructor */
//---------------------------------------------------------------------------- DigitalPin() {}
/** Constructor //----------------------------------------------------------------------------
* @param[in] pinMode if true set output mode else input mode. /** Constructor
*/ * @param[in] pinMode if true set output mode else input mode.
explicit DigitalPin(bool pinMode) { */
mode(pinMode); explicit DigitalPin(bool pinMode)
} {
//---------------------------------------------------------------------------- mode(pinMode);
/** Constructor }
* @param[in] mode If true set output mode else input mode //----------------------------------------------------------------------------
* @param[in] level If mode is output, set level high/low. /** Constructor
* If mode is input, enable or disable the pin's 20K pullup. * @param[in] mode If true set output mode else input mode
*/ * @param[in] level If mode is output, set level high/low.
DigitalPin(bool mode, bool level) { * If mode is input, enable or disable the pin's 20K pullup.
config(mode, level); */
} DigitalPin(bool mode, bool level)
//---------------------------------------------------------------------------- {
/** Asignment operator config(mode, level);
* @param[in] value If true set the pin's level high else set the }
* pin's level low. //----------------------------------------------------------------------------
* /** Asignment operator
* @return This DigitalPin instance. * @param[in] value If true set the pin's level high else set the
*/ * pin's level low.
inline DigitalPin & operator = (bool value) __attribute__((always_inline)) { *
write(value); * @return This DigitalPin instance.
return *this; */
} inline DigitalPin & operator = (bool value) __attribute__((always_inline))
//---------------------------------------------------------------------------- {
/** Parenthesis operator write(value);
* @return Pin's level return *this;
*/ }
inline operator bool () const __attribute__((always_inline)) { //----------------------------------------------------------------------------
return read(); /** Parenthesis operator
} * @return Pin's level
//---------------------------------------------------------------------------- */
/** set pin configuration inline operator bool () const __attribute__((always_inline))
* @param[in] mode If true set output mode else input mode {
* @param[in] level If mode is output, set level high/low. return read();
* If mode is input, enable or disable the pin's 20K pullup. }
*/ //----------------------------------------------------------------------------
ALWAYS_INLINE void config(bool mode, bool level) { /** set pin configuration
fastPinConfig(PinNumber, mode, level); * @param[in] mode If true set output mode else input mode
} * @param[in] level If mode is output, set level high/low.
//---------------------------------------------------------------------------- * If mode is input, enable or disable the pin's 20K pullup.
/** */
* Set pin level high if output mode or enable 20K pullup if input mode. ALWAYS_INLINE void config(bool mode, bool level)
*/ {
ALWAYS_INLINE void high() {write(true);} fastPinConfig(PinNumber, mode, level);
//---------------------------------------------------------------------------- }
/** //----------------------------------------------------------------------------
* Set pin level low if output mode or disable 20K pullup if input mode. /**
*/ * Set pin level high if output mode or enable 20K pullup if input mode.
ALWAYS_INLINE void low() {write(false);} */
//---------------------------------------------------------------------------- ALWAYS_INLINE void high()
/** {
* Set pin mode write(true);
* @param[in] pinMode if true set output mode else input mode. }
* //----------------------------------------------------------------------------
* mode() does not enable or disable the 20K pullup for input mode. /**
*/ * Set pin level low if output mode or disable 20K pullup if input mode.
ALWAYS_INLINE void mode(bool pinMode) { */
fastPinMode(PinNumber, pinMode); ALWAYS_INLINE void low()
} {
//---------------------------------------------------------------------------- write(false);
/** @return Pin's level */ }
ALWAYS_INLINE bool read() const { //----------------------------------------------------------------------------
return fastDigitalRead(PinNumber); /**
} * Set pin mode
//---------------------------------------------------------------------------- * @param[in] pinMode if true set output mode else input mode.
/** toggle a pin *
* * mode() does not enable or disable the 20K pullup for input mode.
* If the pin is in output mode toggle the pin's level. */
* If the pin is in input mode toggle the state of the 20K pullup. ALWAYS_INLINE void mode(bool pinMode)
*/ {
ALWAYS_INLINE void toggle() { fastPinMode(PinNumber, pinMode);
fastDigitalToggle(PinNumber); }
} //----------------------------------------------------------------------------
//---------------------------------------------------------------------------- /** @return Pin's level */
/** Write the pin's level. ALWAYS_INLINE bool read() const
* @param[in] value If true set the pin's level high else set the {
* pin's level low. return fastDigitalRead(PinNumber);
*/ }
ALWAYS_INLINE void write(bool value) { //----------------------------------------------------------------------------
fastDigitalWrite(PinNumber, value); /** toggle a pin
} *
* If the pin is in output mode toggle the pin's level.
* If the pin is in input mode toggle the state of the 20K pullup.
*/
ALWAYS_INLINE void toggle()
{
fastDigitalToggle(PinNumber);
}
//----------------------------------------------------------------------------
/** Write the pin's level.
* @param[in] value If true set the pin's level high else set the
* pin's level low.
*/
ALWAYS_INLINE void write(bool value)
{
fastDigitalWrite(PinNumber, value);
}
}; };
#endif // DigitalPin_h #endif // DigitalPin_h
/** @} */ /** @} */
+20 -15
View File
@@ -32,22 +32,26 @@
/** Constructor /** Constructor
* @param[in] pin Pin assigned to this object. * @param[in] pin Pin assigned to this object.
*/ */
PinIO::PinIO(uint8_t pin) { PinIO::PinIO(uint8_t pin)
begin(pin); {
begin(pin);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Initialize pin bit mask and port address. /** Initialize pin bit mask and port address.
* @param[in] pin Arduino board pin number. * @param[in] pin Arduino board pin number.
* @return true for success or false if invalid pin number. * @return true for success or false if invalid pin number.
*/ */
bool PinIO::begin(uint8_t pin) { bool PinIO::begin(uint8_t pin)
if (pin >= NUM_DIGITAL_PINS) return false; {
uint8_t port = digitalPinToPort(pin); if (pin >= NUM_DIGITAL_PINS) {
pinReg_ = portInputRegister(port); return false;
bit_ = digitalPinToBitMask(pin); }
mask_ = ~bit_; uint8_t port = digitalPinToPort(pin);
portReg_ = pinReg_ + 2; pinReg_ = portInputRegister(port);
return true; bit_ = digitalPinToBitMask(pin);
mask_ = ~bit_;
portReg_ = pinReg_ + 2;
return true;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Configure the pin /** Configure the pin
@@ -59,10 +63,11 @@ bool PinIO::begin(uint8_t pin) {
* This function may be used with interrupts enabled or disabled. * This function may be used with interrupts enabled or disabled.
* The previous interrupt state will be restored. * The previous interrupt state will be restored.
*/ */
void PinIO::config(bool mode, bool data) { void PinIO::config(bool mode, bool data)
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { {
modeI(mode); ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
writeI(data); modeI(mode);
} writeI(data);
}
} }
/** @} */ /** @} */
+147 -112
View File
@@ -37,118 +37,153 @@
* @class PinIO * @class PinIO
* @brief AVR port I/O with runtime pin numbers. * @brief AVR port I/O with runtime pin numbers.
*/ */
class PinIO { class PinIO
public: {
/** Create a PinIO object with no assigned pin. */ public:
PinIO() : bit_(0), mask_(0XFF) {} /** Create a PinIO object with no assigned pin. */
/** Constructor PinIO() : bit_(0), mask_(0XFF) {}
* @param[in] pin Pin assigned to this object. /** Constructor
*/ * @param[in] pin Pin assigned to this object.
explicit PinIO(uint8_t pin); */
/** Initialize pin bit mask and port address. explicit PinIO(uint8_t pin);
* @param[in] pin Arduino board pin number. /** Initialize pin bit mask and port address.
* @return true for success or false if invalid pin number. * @param[in] pin Arduino board pin number.
*/ * @return true for success or false if invalid pin number.
bool begin(uint8_t pin); */
/** Configure the pin bool begin(uint8_t pin);
* /** Configure the pin
* @param[in] mode Configure as output mode if true else input mode. *
* @param[in] data For output mode set pin high if true else low. * @param[in] mode Configure as output mode if true else input mode.
* For input mode enable 20K pullup if true else Hi-Z. * @param[in] data For output mode set pin high if true else low.
* * For input mode enable 20K pullup if true else Hi-Z.
* This function may be used with interrupts enabled or disabled. *
* The previous interrupt state will be restored. * This function may be used with interrupts enabled or disabled.
*/ * The previous interrupt state will be restored.
void config(bool mode, bool data); */
//---------------------------------------------------------------------------- void config(bool mode, bool data);
/** @return Pin's level */ //----------------------------------------------------------------------------
ALWAYS_INLINE bool read() {return *pinReg_ & bit_;} /** @return Pin's level */
//---------------------------------------------------------------------------- ALWAYS_INLINE bool read()
/** toggle a pin {
* return *pinReg_ & bit_;
* If the pin is in output mode toggle the pin's level. }
* If the pin is in input mode toggle the state of the 20K pullup. //----------------------------------------------------------------------------
*/ /** toggle a pin
ALWAYS_INLINE void toggle() {*pinReg_ = bit_;} *
//============================================================================ * If the pin is in output mode toggle the pin's level.
/** * If the pin is in input mode toggle the state of the 20K pullup.
* Set pin high if output mode or enable 20K pullup if input mode. */
* ALWAYS_INLINE void toggle()
* This function must be called with interrupts disabled. {
* This function will not change the interrupt state. *pinReg_ = bit_;
*/ }
ALWAYS_INLINE void highI() {writeI(1);} //============================================================================
/** /**
* Set pin low if output mode or disable 20K pullup if input mode. * Set pin high if output mode or enable 20K pullup if input mode.
* *
* This function must be called with interrupts disabled. * This function must be called with interrupts disabled.
* This function will not change the interrupt state. * This function will not change the interrupt state.
*/ */
ALWAYS_INLINE void lowI() {writeI(0);} ALWAYS_INLINE void highI()
/** {
* Set pin mode writeI(1);
* @param[in] mode if true set output mode else input mode. }
* /**
* mode() does not enable or disable the 20K pullup for input mode. * Set pin low if output mode or disable 20K pullup if input mode.
* *
* This function must be called with interrupts disabled. * This function must be called with interrupts disabled.
* This function will not change the interrupt state. * This function will not change the interrupt state.
*/ */
ALWAYS_INLINE void modeI(bool mode) { ALWAYS_INLINE void lowI()
volatile uint8_t* ddrReg = pinReg_ + 1; {
*ddrReg = mode ? *ddrReg | bit_ : *ddrReg & mask_; writeI(0);
} }
/** Write pin. /**
* * Set pin mode
* @param[in] level If output mode set pin high if true else low. * @param[in] mode if true set output mode else input mode.
* If input mode enable 20K pullup if true else disable pullup. *
* * mode() does not enable or disable the 20K pullup for input mode.
* This function must be called with interrupts disabled. *
* This function will not change the interrupt state. * This function must be called with interrupts disabled.
*/ * This function will not change the interrupt state.
ALWAYS_INLINE void writeI(bool level) { */
*portReg_ = level ? *portReg_ | bit_ : *portReg_ & mask_; ALWAYS_INLINE void modeI(bool mode)
} {
//============================================================================ volatile uint8_t* ddrReg = pinReg_ + 1;
/** *ddrReg = mode ? *ddrReg | bit_ : *ddrReg & mask_;
* Set pin level high if output mode or enable 20K pullup if input mode. }
* /** Write pin.
* This function will enable interrupts. This function should not be *
* called in an ISR or where interrupts are disabled. * @param[in] level If output mode set pin high if true else low.
*/ * If input mode enable 20K pullup if true else disable pullup.
ALWAYS_INLINE void high() {ATOMIC_BLOCK(ATOMIC_FORCEON) {highI();}} *
/** * This function must be called with interrupts disabled.
* Set pin level low if output mode or disable 20K pullup if input mode. * This function will not change the interrupt state.
* */
* This function will enable interrupts. This function should not be ALWAYS_INLINE void writeI(bool level)
* called in an ISR or where interrupts are disabled. {
*/ *portReg_ = level ? *portReg_ | bit_ : *portReg_ & mask_;
ALWAYS_INLINE void low() {ATOMIC_BLOCK(ATOMIC_FORCEON) {lowI();}} }
/** //============================================================================
* Set pin mode /**
* @param[in] pinMode if true set output mode else input mode. * Set pin level high if output mode or enable 20K pullup if input mode.
* *
* mode() does not enable or disable the 20K pullup for input mode. * This function will enable interrupts. This function should not be
* * called in an ISR or where interrupts are disabled.
* This function will enable interrupts. This function should not be */
* called in an ISR or where interrupts are disabled. ALWAYS_INLINE void high()
*/ {
ALWAYS_INLINE void mode(bool pinMode) {ATOMIC_BLOCK(ATOMIC_FORCEON) {modeI(pinMode);}} ATOMIC_BLOCK(ATOMIC_FORCEON) {
/** Write pin. highI();
* }
* @param[in] level If output mode set pin high if true else low. }
* If input mode enable 20K pullup if true else disable pullup. /**
* * Set pin level low if output mode or disable 20K pullup if input mode.
* This function will enable interrupts. This function should not be *
* called in an ISR or where interrupts are disabled. * This function will enable interrupts. This function should not be
*/ * called in an ISR or where interrupts are disabled.
ALWAYS_INLINE void write(bool level) {ATOMIC_BLOCK(ATOMIC_FORCEON) {writeI(level);}} */
//---------------------------------------------------------------------------- ALWAYS_INLINE void low()
private: {
uint8_t bit_; ATOMIC_BLOCK(ATOMIC_FORCEON) {
uint8_t mask_; lowI();
volatile uint8_t* pinReg_; }
volatile uint8_t* portReg_; }
/**
* Set pin mode
* @param[in] pinMode if true set output mode else input mode.
*
* mode() does not enable or disable the 20K pullup for input mode.
*
* This function will enable interrupts. This function should not be
* called in an ISR or where interrupts are disabled.
*/
ALWAYS_INLINE void mode(bool pinMode)
{
ATOMIC_BLOCK(ATOMIC_FORCEON) {
modeI(pinMode);
}
}
/** Write pin.
*
* @param[in] level If output mode set pin high if true else low.
* If input mode enable 20K pullup if true else disable pullup.
*
* This function will enable interrupts. This function should not be
* called in an ISR or where interrupts are disabled.
*/
ALWAYS_INLINE void write(bool level)
{
ATOMIC_BLOCK(ATOMIC_FORCEON) {
writeI(level);
}
}
//----------------------------------------------------------------------------
private:
uint8_t bit_;
uint8_t mask_;
volatile uint8_t* pinReg_;
volatile uint8_t* portReg_;
}; };
#endif // PinIO_h #endif // PinIO_h
/** @} */ /** @} */
+129 -119
View File
@@ -49,16 +49,17 @@
* @return true for success else false. * @return true for success else false.
*/ */
bool I2cMasterBase::transfer(uint8_t addrRW, bool I2cMasterBase::transfer(uint8_t addrRW,
void *buf, size_t nbytes, uint8_t option) { void *buf, size_t nbytes, uint8_t option)
if (_state != STATE_REP_START) { {
start(); if (_state != STATE_REP_START) {
} start();
if (!write(addrRW)) { }
_state = addrRW & I2C_READ ? STATE_RX_ADDR_NACK : STATE_TX_ADDR_NACK; if (!write(addrRW)) {
return false; _state = addrRW & I2C_READ ? STATE_RX_ADDR_NACK : STATE_TX_ADDR_NACK;
} return false;
_state = addrRW & I2C_READ ? STATE_RX_DATA : STATE_TX_DATA; }
return transferContinue(buf, nbytes, option); _state = addrRW & I2C_READ ? STATE_RX_DATA : STATE_TX_DATA;
return transferContinue(buf, nbytes, option);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
@@ -76,30 +77,31 @@ bool I2cMasterBase::transfer(uint8_t addrRW,
* . * .
* @return true for success else false. * @return true for success else false.
*/ */
bool I2cMasterBase::transferContinue(void *buf, size_t nbytes, uint8_t option) { bool I2cMasterBase::transferContinue(void *buf, size_t nbytes, uint8_t option)
uint8_t* p = reinterpret_cast<uint8_t*>(buf); {
if (_state == STATE_RX_DATA) { uint8_t* p = reinterpret_cast<uint8_t*>(buf);
for (size_t i = 0; i < nbytes; i++) { if (_state == STATE_RX_DATA) {
p[i] = read(i == (nbytes - 1) && option != I2C_CONTINUE); for (size_t i = 0; i < nbytes; i++) {
} p[i] = read(i == (nbytes - 1) && option != I2C_CONTINUE);
} else if (_state == STATE_TX_DATA) { }
for (size_t i = 0; i < nbytes; i++) { } else if (_state == STATE_TX_DATA) {
if (!write(p[i])) { for (size_t i = 0; i < nbytes; i++) {
_state = STATE_TX_DATA_NACK; if (!write(p[i])) {
return false; _state = STATE_TX_DATA_NACK;
} return false;
} }
} else { }
return false; } else {
} return false;
if (option == I2C_STOP) { }
stop(); if (option == I2C_STOP) {
_state = STATE_STOP; stop();
} else if (option == I2C_REP_START) { _state = STATE_STOP;
start(); } else if (option == I2C_REP_START) {
_state = STATE_STOP; start();
} _state = STATE_STOP;
return true; }
return true;
} }
//============================================================================== //==============================================================================
// WARNING don't change SoftI2cMaster unless you verify the change with a scope // WARNING don't change SoftI2cMaster unless you verify the change with a scope
@@ -111,8 +113,9 @@ bool I2cMasterBase::transferContinue(void *buf, size_t nbytes, uint8_t option) {
* *
* @param[in] sclPin The software SCL pin number. * @param[in] sclPin The software SCL pin number.
*/ */
SoftI2cMaster::SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin) { SoftI2cMaster::SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin)
begin(sclPin, sdaPin); {
begin(sclPin, sdaPin);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
@@ -122,32 +125,33 @@ SoftI2cMaster::SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin) {
* *
* @param[in] sclPin The software SCL pin number. * @param[in] sclPin The software SCL pin number.
*/ */
void SoftI2cMaster::begin(uint8_t sclPin, uint8_t sdaPin) { void SoftI2cMaster::begin(uint8_t sclPin, uint8_t sdaPin)
uint8_t port; {
uint8_t port;
// Get bit mask and address of scl registers. // Get bit mask and address of scl registers.
_sclBit = digitalPinToBitMask(sclPin); _sclBit = digitalPinToBitMask(sclPin);
port = digitalPinToPort(sclPin); port = digitalPinToPort(sclPin);
_sclDDR = portModeRegister(port); _sclDDR = portModeRegister(port);
volatile uint8_t* sclOutReg = portOutputRegister(port); volatile uint8_t* sclOutReg = portOutputRegister(port);
// Get bit mask and address of sda registers. // Get bit mask and address of sda registers.
_sdaBit = digitalPinToBitMask(sdaPin); _sdaBit = digitalPinToBitMask(sdaPin);
port = digitalPinToPort(sdaPin); port = digitalPinToPort(sdaPin);
_sdaDDR = portModeRegister(port); _sdaDDR = portModeRegister(port);
_sdaInReg = portInputRegister(port); _sdaInReg = portInputRegister(port);
volatile uint8_t* sdaOutReg = portOutputRegister(port); volatile uint8_t* sdaOutReg = portOutputRegister(port);
// Clear PORT bit for scl and sda. // Clear PORT bit for scl and sda.
uint8_t s = SREG; uint8_t s = SREG;
noInterrupts(); noInterrupts();
*sclOutReg &= ~_sclBit; *sclOutReg &= ~_sclBit;
*sdaOutReg &= ~_sdaBit; *sdaOutReg &= ~_sdaBit;
SREG = s; SREG = s;
// Set scl and sda high. // Set scl and sda high.
writeScl(HIGH); writeScl(HIGH);
writeSda(HIGH); writeSda(HIGH);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/* Read a byte and send ACK if more reads follow else NACK to terminate read. /* Read a byte and send ACK if more reads follow else NACK to terminate read.
@@ -156,51 +160,56 @@ void SoftI2cMaster::begin(uint8_t sclPin, uint8_t sdaPin) {
* *
* @return The byte read from the I2C bus. * @return The byte read from the I2C bus.
*/ */
uint8_t SoftI2cMaster::read(uint8_t last) { uint8_t SoftI2cMaster::read(uint8_t last)
uint8_t b = 0; {
uint8_t b = 0;
// Set sda to high Z mode for read. // Set sda to high Z mode for read.
writeSda(HIGH); writeSda(HIGH);
// Read a byte. // Read a byte.
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
// Don't change this loop unless you verify the change with a scope. // Don't change this loop unless you verify the change with a scope.
b <<= 1; b <<= 1;
sclDelay(16); sclDelay(16);
writeScl(HIGH); writeScl(HIGH);
sclDelay(12); sclDelay(12);
if (readSda()) b |= 1; if (readSda()) {
writeScl(LOW); b |= 1;
} }
// send ACK or NACK writeScl(LOW);
writeSda(last); }
sclDelay(12); // send ACK or NACK
writeScl(HIGH); writeSda(last);
sclDelay(18); sclDelay(12);
writeScl(LOW); writeScl(HIGH);
writeSda(LOW); sclDelay(18);
return b; writeScl(LOW);
writeSda(LOW);
return b;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/* Issue a start condition. */ /* Issue a start condition. */
void SoftI2cMaster::start() { void SoftI2cMaster::start()
if (!readSda()) { {
writeSda(HIGH); if (!readSda()) {
writeScl(HIGH); writeSda(HIGH);
sclDelay(20); writeScl(HIGH);
} sclDelay(20);
writeSda(LOW); }
sclDelay(20); writeSda(LOW);
writeScl(LOW); sclDelay(20);
writeScl(LOW);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/* Issue a stop condition. */ /* Issue a stop condition. */
void SoftI2cMaster::stop(void) { void SoftI2cMaster::stop(void)
writeSda(LOW); {
sclDelay(20); writeSda(LOW);
writeScl(HIGH); sclDelay(20);
sclDelay(20); writeScl(HIGH);
writeSda(HIGH); sclDelay(20);
sclDelay(20); writeSda(HIGH);
sclDelay(20);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/* /*
@@ -210,30 +219,31 @@ void SoftI2cMaster::stop(void) {
* *
* @return The value true, 1, if the slave returned an ACK or false for NACK. * @return The value true, 1, if the slave returned an ACK or false for NACK.
*/ */
bool SoftI2cMaster::write(uint8_t data) { bool SoftI2cMaster::write(uint8_t data)
// write byte {
for (uint8_t m = 0X80; m != 0; m >>= 1) { // write byte
// don't change this loop unless you verify the change with a scope for (uint8_t m = 0X80; m != 0; m >>= 1) {
writeSda(m & data); // don't change this loop unless you verify the change with a scope
sclDelay(8); writeSda(m & data);
writeScl(HIGH); sclDelay(8);
sclDelay(18); writeScl(HIGH);
writeScl(LOW); sclDelay(18);
} writeScl(LOW);
sclDelay(8); }
// Go to sda high Z mode for input. sclDelay(8);
writeSda(HIGH); // Go to sda high Z mode for input.
writeScl(HIGH); writeSda(HIGH);
sclDelay(16); writeScl(HIGH);
sclDelay(16);
// Get ACK or NACK. // Get ACK or NACK.
uint8_t rtn = readSda(); uint8_t rtn = readSda();
// pull scl low. // pull scl low.
writeScl(LOW); writeScl(LOW);
// Pull sda low. // Pull sda low.
writeSda(LOW); writeSda(LOW);
return rtn == 0; return rtn == 0;
} }
/** @} */ /** @} */
+272 -240
View File
@@ -63,151 +63,161 @@ const uint8_t STATE_TX_DATA_NACK = 6;
* @class I2cMasterBase * @class I2cMasterBase
* @brief Base class for FastI2cMaster, SoftI2cMaster * @brief Base class for FastI2cMaster, SoftI2cMaster
*/ */
class I2cMasterBase { class I2cMasterBase
public: {
I2cMasterBase() : _state(STATE_STOP) {} public:
/** Read a byte I2cMasterBase() : _state(STATE_STOP) {}
* /** Read a byte
* @note This function should only be used by experts. Data should be *
* accessed by calling transfer() and transferContinue() * @note This function should only be used by experts. Data should be
* * accessed by calling transfer() and transferContinue()
* @param[in] last send a NACK to terminate read if last is true else *
* send an ACK to continue the read. * @param[in] last send a NACK to terminate read if last is true else
* * send an ACK to continue the read.
* @return byte read from I2C bus *
*/ * @return byte read from I2C bus
virtual uint8_t read(uint8_t last) = 0; */
virtual uint8_t read(uint8_t last) = 0;
/** Issue a start condition /** Issue a start condition
* *
* @note This function should only be used by experts. Data should be * @note This function should only be used by experts. Data should be
* accessed by calling transfer() and transferContinue() * accessed by calling transfer() and transferContinue()
*/ */
virtual void start() = 0; virtual void start() = 0;
/** Issue a stop condition. /** Issue a stop condition.
* *
* @note This function should only be used by experts. Data should be * @note This function should only be used by experts. Data should be
* accessed by calling transfer() and transferContinue() * accessed by calling transfer() and transferContinue()
*/ */
virtual void stop() = 0; virtual void stop() = 0;
/** /**
* Start an I2C transfer with possible continuation. * Start an I2C transfer with possible continuation.
* *
* @param[in] addressRW I2C slave address plus R/W bit. * @param[in] addressRW I2C slave address plus R/W bit.
* The I2C slave address is in the high seven bits * The I2C slave address is in the high seven bits
* and is ORed with on of the following: * and is ORed with on of the following:
* - I2C_READ for a read transfer. * - I2C_READ for a read transfer.
* - I2C_WRITE for a write transfer. * - I2C_WRITE for a write transfer.
* . * .
* @param[in,out] buf Source or destination for transfer. * @param[in,out] buf Source or destination for transfer.
* @param[in] nbyte Number of bytes to transfer (may be zero). * @param[in] nbyte Number of bytes to transfer (may be zero).
* @param[in] option Option for ending the transfer, one of: * @param[in] option Option for ending the transfer, one of:
* - I2C_STOP end the transfer with an I2C stop * - I2C_STOP end the transfer with an I2C stop
* condition. * condition.
* - I2C_REP_START end the transfer with an I2C * - I2C_REP_START end the transfer with an I2C
* repeated start condition. * repeated start condition.
* - I2C_CONTINUE allow additional transferContinue() * - I2C_CONTINUE allow additional transferContinue()
* calls. * calls.
* . * .
* @return true for success else false. * @return true for success else false.
*/ */
bool transfer(uint8_t addressRW, void *buf, bool transfer(uint8_t addressRW, void *buf,
size_t nbyte, uint8_t option = I2C_STOP); size_t nbyte, uint8_t option = I2C_STOP);
/** /**
* Continue an I2C transfer. * Continue an I2C transfer.
* *
* @param[in,out] buf Source or destination for transfer. * @param[in,out] buf Source or destination for transfer.
* @param[in] nbyte Number of bytes to transfer (may be zero). * @param[in] nbyte Number of bytes to transfer (may be zero).
* @param[in] option Option for ending the transfer, one of: * @param[in] option Option for ending the transfer, one of:
* - I2C_STOP end the transfer with an I2C stop * - I2C_STOP end the transfer with an I2C stop
* condition. * condition.
* - I2C_REP_START end the transfer with an I2C * - I2C_REP_START end the transfer with an I2C
* repeated start condition. * repeated start condition.
* - I2C_CONTINUE allow additional transferContinue() * - I2C_CONTINUE allow additional transferContinue()
* calls. * calls.
* . * .
* @return true for success else false. * @return true for success else false.
*/ */
bool transferContinue(void *buf, size_t nbyte, uint8_t option = I2C_STOP); bool transferContinue(void *buf, size_t nbyte, uint8_t option = I2C_STOP);
/** Write a byte /** Write a byte
* *
* @note This function should only be used by experts. Data should be * @note This function should only be used by experts. Data should be
* accessed by calling transfer() and transferContinue() * accessed by calling transfer() and transferContinue()
* *
* @param[in] data byte to write * @param[in] data byte to write
* @return true for ACK or false for NACK */ * @return true for ACK or false for NACK */
virtual bool write(uint8_t data) = 0; virtual bool write(uint8_t data) = 0;
private: private:
uint8_t _state; uint8_t _state;
}; };
//============================================================================== //==============================================================================
/** /**
* @class SoftI2cMaster * @class SoftI2cMaster
* @brief Software I2C master class * @brief Software I2C master class
*/ */
class SoftI2cMaster : public I2cMasterBase { class SoftI2cMaster : public I2cMasterBase
public: {
SoftI2cMaster() {} public:
/** SoftI2cMaster() {}
* Constructor, initialize SCL/SDA pins and set the bus high. /**
* * Constructor, initialize SCL/SDA pins and set the bus high.
* @param[in] sdaPin The software SDA pin number. *
* * @param[in] sdaPin The software SDA pin number.
* @param[in] sclPin The software SCL pin number. *
*/ * @param[in] sclPin The software SCL pin number.
SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin); */
/** SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin);
* Initialize SCL/SDA pins and set the bus high. /**
* * Initialize SCL/SDA pins and set the bus high.
* @param[in] sdaPin The software SDA pin number. *
* * @param[in] sdaPin The software SDA pin number.
* @param[in] sclPin The software SCL pin number. *
*/ * @param[in] sclPin The software SCL pin number.
void begin(uint8_t sclPin, uint8_t sdaPin); */
uint8_t read(uint8_t last); void begin(uint8_t sclPin, uint8_t sdaPin);
void start(); uint8_t read(uint8_t last);
void stop(void); void start();
bool write(uint8_t b); void stop(void);
bool write(uint8_t b);
private: private:
uint8_t _sclBit; uint8_t _sclBit;
uint8_t _sdaBit; uint8_t _sdaBit;
volatile uint8_t* _sclDDR; volatile uint8_t* _sclDDR;
volatile uint8_t* _sdaDDR; volatile uint8_t* _sdaDDR;
volatile uint8_t* _sdaInReg; volatile uint8_t* _sdaInReg;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool readSda() {return *_sdaInReg & _sdaBit;} bool readSda()
//---------------------------------------------------------------------------- {
void sclDelay(uint8_t n) {_delay_loop_1(n);} return *_sdaInReg & _sdaBit;
//---------------------------------------------------------------------------- }
void writeScl(bool value) { //----------------------------------------------------------------------------
uint8_t s = SREG; void sclDelay(uint8_t n)
noInterrupts(); {
if (value == LOW) { _delay_loop_1(n);
// Pull scl low. }
*_sclDDR |= _sclBit; //----------------------------------------------------------------------------
} else { void writeScl(bool value)
// Put scl in high Z input mode. {
*_sclDDR &= ~_sclBit; uint8_t s = SREG;
} noInterrupts();
SREG = s; if (value == LOW) {
} // Pull scl low.
//---------------------------------------------------------------------------- *_sclDDR |= _sclBit;
void writeSda(bool value) { } else {
uint8_t s = SREG; // Put scl in high Z input mode.
noInterrupts(); *_sclDDR &= ~_sclBit;
if (value == LOW) { }
// Pull sda low. SREG = s;
*_sdaDDR |= _sdaBit; }
} else { //----------------------------------------------------------------------------
// Put sda in high Z input mode. void writeSda(bool value)
*_sdaDDR &= ~_sdaBit; {
} uint8_t s = SREG;
SREG = s; noInterrupts();
} if (value == LOW) {
// Pull sda low.
*_sdaDDR |= _sdaBit;
} else {
// Put sda in high Z input mode.
*_sdaDDR &= ~_sdaBit;
}
SREG = s;
}
}; };
//============================================================================== //==============================================================================
// Template based fast software I2C // Template based fast software I2C
@@ -217,118 +227,140 @@ class SoftI2cMaster : public I2cMasterBase {
* @brief Fast software I2C master class. * @brief Fast software I2C master class.
*/ */
template<uint8_t sclPin, uint8_t sdaPin> template<uint8_t sclPin, uint8_t sdaPin>
class FastI2cMaster : public I2cMasterBase { class FastI2cMaster : public I2cMasterBase
public: {
//---------------------------------------------------------------------------- public:
FastI2cMaster() { //----------------------------------------------------------------------------
begin(); FastI2cMaster()
} {
//---------------------------------------------------------------------------- begin();
/** Initialize I2C bus pins. */ }
void begin() { //----------------------------------------------------------------------------
fastDigitalWrite(sclPin, LOW); /** Initialize I2C bus pins. */
fastDigitalWrite(sdaPin, LOW); void begin()
{
fastDigitalWrite(sclPin, LOW);
fastDigitalWrite(sdaPin, LOW);
sclWrite(HIGH); sclWrite(HIGH);
sdaWrite(HIGH); sdaWrite(HIGH);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
uint8_t read(uint8_t last) { uint8_t read(uint8_t last)
uint8_t data = 0; {
sdaWrite(HIGH); uint8_t data = 0;
sdaWrite(HIGH);
readBit(7, &data); readBit(7, &data);
readBit(6, &data); readBit(6, &data);
readBit(5, &data); readBit(5, &data);
readBit(4, &data); readBit(4, &data);
readBit(3, &data); readBit(3, &data);
readBit(2, &data); readBit(2, &data);
readBit(1, &data); readBit(1, &data);
readBit(0, &data); readBit(0, &data);
// send ACK or NACK // send ACK or NACK
sdaWrite(last); sdaWrite(last);
sclDelay(4); sclDelay(4);
sclWrite(HIGH); sclWrite(HIGH);
sclDelay(6); sclDelay(6);
sclWrite(LOW); sclWrite(LOW);
sdaWrite(LOW); sdaWrite(LOW);
return data; return data;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void start() { void start()
if (!fastDigitalRead(sdaPin)) { {
// It's a repeat start. if (!fastDigitalRead(sdaPin)) {
sdaWrite(HIGH); // It's a repeat start.
sclDelay(8); sdaWrite(HIGH);
sclWrite(HIGH); sclDelay(8);
sclDelay(8); sclWrite(HIGH);
} sclDelay(8);
sdaWrite(LOW); }
sclDelay(8); sdaWrite(LOW);
sclWrite(LOW); sclDelay(8);
sclDelay(8); sclWrite(LOW);
} sclDelay(8);
//---------------------------------------------------------------------------- }
void stop(void) { //----------------------------------------------------------------------------
sdaWrite(LOW); void stop(void)
sclDelay(8); {
sclWrite(HIGH); sdaWrite(LOW);
sclDelay(8); sclDelay(8);
sdaWrite(HIGH); sclWrite(HIGH);
sclDelay(8); sclDelay(8);
} sdaWrite(HIGH);
//---------------------------------------------------------------------------- sclDelay(8);
bool write(uint8_t data) { }
// write byte //----------------------------------------------------------------------------
writeBit(7, data); bool write(uint8_t data)
writeBit(6, data); {
writeBit(5, data); // write byte
writeBit(4, data); writeBit(7, data);
writeBit(3, data); writeBit(6, data);
writeBit(2, data); writeBit(5, data);
writeBit(1, data); writeBit(4, data);
writeBit(0, data); writeBit(3, data);
writeBit(2, data);
writeBit(1, data);
writeBit(0, data);
// get ACK or NACK // get ACK or NACK
sdaWrite(HIGH); sdaWrite(HIGH);
sclWrite(HIGH); sclWrite(HIGH);
sclDelay(5); sclDelay(5);
bool rtn = fastDigitalRead(sdaPin); bool rtn = fastDigitalRead(sdaPin);
sclWrite(LOW); sclWrite(LOW);
sdaWrite(LOW); sdaWrite(LOW);
return rtn == 0; return rtn == 0;
} }
private: private:
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void sclWrite(bool value) {fastPinMode(sclPin, !value);} void sclWrite(bool value)
//---------------------------------------------------------------------------- {
inline __attribute__((always_inline)) fastPinMode(sclPin, !value);
void sdaWrite(bool value) {fastPinMode(sdaPin, !value);} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void readBit(uint8_t bit, uint8_t* data) { void sdaWrite(bool value)
sclWrite(HIGH); {
sclDelay(5); fastPinMode(sdaPin, !value);
if (fastDigitalRead(sdaPin)) *data |= 1 << bit; }
sclWrite(LOW); //----------------------------------------------------------------------------
if (bit) sclDelay(6); inline __attribute__((always_inline))
} void readBit(uint8_t bit, uint8_t* data)
//---------------------------------------------------------------------------- {
void sclDelay(uint8_t n) {_delay_loop_1(n);} sclWrite(HIGH);
//---------------------------------------------------------------------------- sclDelay(5);
inline __attribute__((always_inline)) if (fastDigitalRead(sdaPin)) {
void writeBit(uint8_t bit, uint8_t data) { *data |= 1 << bit;
uint8_t mask = 1 << bit; }
sdaWrite(data & mask); sclWrite(LOW);
sclWrite(HIGH); if (bit) {
sclDelay(5); sclDelay(6);
sclWrite(LOW); }
sclDelay(5); }
} //----------------------------------------------------------------------------
void sclDelay(uint8_t n)
{
_delay_loop_1(n);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void writeBit(uint8_t bit, uint8_t data)
{
uint8_t mask = 1 << bit;
sdaWrite(data & mask);
sclWrite(HIGH);
sclDelay(5);
sclWrite(LOW);
sclDelay(5);
}
}; };
#endif // SOFT_I2C_MASTER_H #endif // SOFT_I2C_MASTER_H
/** @} */ /** @} */
+120 -102
View File
@@ -18,7 +18,7 @@
* <http://www.gnu.org/licenses/>. * <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file * @file
* @brief Software SPI. * @brief Software SPI.
* *
* @defgroup softSPI Software SPI * @defgroup softSPI Software SPI
@@ -48,108 +48,126 @@ const bool SCK_MODE = true;
* @brief Fast software SPI. * @brief Fast software SPI.
*/ */
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin, uint8_t Mode = 0> template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin, uint8_t Mode = 0>
class SoftSPI { class SoftSPI
public: {
//---------------------------------------------------------------------------- public:
/** Initialize SoftSPI pins. */ //----------------------------------------------------------------------------
void begin() { /** Initialize SoftSPI pins. */
fastPinConfig(MisoPin, MISO_MODE, MISO_LEVEL); void begin()
fastPinConfig(MosiPin, MOSI_MODE, !MODE_CPHA(Mode)); {
fastPinConfig(SckPin, SCK_MODE, MODE_CPOL(Mode)); fastPinConfig(MisoPin, MISO_MODE, MISO_LEVEL);
} fastPinConfig(MosiPin, MOSI_MODE, !MODE_CPHA(Mode));
//---------------------------------------------------------------------------- fastPinConfig(SckPin, SCK_MODE, MODE_CPOL(Mode));
/** Soft SPI receive byte. }
* @return Data byte received. //----------------------------------------------------------------------------
*/ /** Soft SPI receive byte.
ALWAYS_INLINE uint8_t receive() { * @return Data byte received.
uint8_t data = 0; */
receiveBit(7, &data); ALWAYS_INLINE uint8_t receive()
receiveBit(6, &data); {
receiveBit(5, &data); uint8_t data = 0;
receiveBit(4, &data); receiveBit(7, &data);
receiveBit(3, &data); receiveBit(6, &data);
receiveBit(2, &data); receiveBit(5, &data);
receiveBit(1, &data); receiveBit(4, &data);
receiveBit(0, &data); receiveBit(3, &data);
return data; receiveBit(2, &data);
} receiveBit(1, &data);
//---------------------------------------------------------------------------- receiveBit(0, &data);
/** Soft SPI send byte. return data;
* @param[in] data Data byte to send. }
*/ //----------------------------------------------------------------------------
ALWAYS_INLINE void send(uint8_t data) { /** Soft SPI send byte.
sendBit(7, data); * @param[in] data Data byte to send.
sendBit(6, data); */
sendBit(5, data); ALWAYS_INLINE void send(uint8_t data)
sendBit(4, data); {
sendBit(3, data); sendBit(7, data);
sendBit(2, data); sendBit(6, data);
sendBit(1, data); sendBit(5, data);
sendBit(0, data); sendBit(4, data);
} sendBit(3, data);
//---------------------------------------------------------------------------- sendBit(2, data);
/** Soft SPI transfer byte. sendBit(1, data);
* @param[in] txData Data byte to send. sendBit(0, data);
* @return Data byte received. }
*/ //----------------------------------------------------------------------------
ALWAYS_INLINE uint8_t transfer(uint8_t txData) { /** Soft SPI transfer byte.
uint8_t rxData = 0; * @param[in] txData Data byte to send.
transferBit(7, &rxData, txData); * @return Data byte received.
transferBit(6, &rxData, txData); */
transferBit(5, &rxData, txData); ALWAYS_INLINE uint8_t transfer(uint8_t txData)
transferBit(4, &rxData, txData); {
transferBit(3, &rxData, txData); uint8_t rxData = 0;
transferBit(2, &rxData, txData); transferBit(7, &rxData, txData);
transferBit(1, &rxData, txData); transferBit(6, &rxData, txData);
transferBit(0, &rxData, txData); transferBit(5, &rxData, txData);
return rxData; transferBit(4, &rxData, txData);
} transferBit(3, &rxData, txData);
transferBit(2, &rxData, txData);
transferBit(1, &rxData, txData);
transferBit(0, &rxData, txData);
return rxData;
}
private: private:
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
ALWAYS_INLINE bool MODE_CPHA(uint8_t mode) {return (mode & 1) != 0;} ALWAYS_INLINE bool MODE_CPHA(uint8_t mode)
ALWAYS_INLINE bool MODE_CPOL(uint8_t mode) {return (mode & 2) != 0;} {
ALWAYS_INLINE void receiveBit(uint8_t bit, uint8_t* data) { return (mode & 1) != 0;
if (MODE_CPHA(Mode)) { }
fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); ALWAYS_INLINE bool MODE_CPOL(uint8_t mode)
} {
nop; return (mode & 2) != 0;
nop; }
fastDigitalWrite(SckPin, ALWAYS_INLINE void receiveBit(uint8_t bit, uint8_t* data)
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); {
if (fastDigitalRead(MisoPin)) *data |= 1 << bit; if (MODE_CPHA(Mode)) {
if (!MODE_CPHA(Mode)) { fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
fastDigitalWrite(SckPin, MODE_CPOL(Mode)); }
} nop;
} nop;
//---------------------------------------------------------------------------- fastDigitalWrite(SckPin,
ALWAYS_INLINE void sendBit(uint8_t bit, uint8_t data) { MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (MODE_CPHA(Mode)) { if (fastDigitalRead(MisoPin)) {
fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); *data |= 1 << bit;
} }
fastDigitalWrite(MosiPin, data & (1 << bit)); if (!MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, fastDigitalWrite(SckPin, MODE_CPOL(Mode));
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); }
nop; }
nop; //----------------------------------------------------------------------------
if (!MODE_CPHA(Mode)) { ALWAYS_INLINE void sendBit(uint8_t bit, uint8_t data)
fastDigitalWrite(SckPin, MODE_CPOL(Mode)); {
} if (MODE_CPHA(Mode)) {
} fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
//---------------------------------------------------------------------------- }
ALWAYS_INLINE void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData) { fastDigitalWrite(MosiPin, data & (1 << bit));
if (MODE_CPHA(Mode)) { fastDigitalWrite(SckPin,
fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
} nop;
fastDigitalWrite(MosiPin, txData & (1 << bit)); nop;
fastDigitalWrite(SckPin, if (!MODE_CPHA(Mode)) {
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); fastDigitalWrite(SckPin, MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) *rxData |= 1 << bit; }
if (!MODE_CPHA(Mode)) { }
fastDigitalWrite(SckPin, MODE_CPOL(Mode)); //----------------------------------------------------------------------------
} ALWAYS_INLINE void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData)
} {
//---------------------------------------------------------------------------- if (MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
}
fastDigitalWrite(MosiPin, txData & (1 << bit));
fastDigitalWrite(SckPin,
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) {
*rxData |= 1 << bit;
}
if (!MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, MODE_CPOL(Mode));
}
}
//----------------------------------------------------------------------------
}; };
#endif // SoftSPI_h #endif // SoftSPI_h
/** @} */ /** @} */
+106 -92
View File
@@ -9,118 +9,132 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<uint8_t ClkPin, uint8_t ConvPin, uint8_t DataPin> template<uint8_t ClkPin, uint8_t ConvPin, uint8_t DataPin>
/** ADS7818 class */ /** ADS7818 class */
class ADS7818 { class ADS7818
public: {
//---------------------------------------------------------------------------- public:
/** Set pin modes and initial levels. */ //----------------------------------------------------------------------------
void begin() { /** Set pin modes and initial levels. */
fastPinMode(ClkPin, 1); void begin()
fastDigitalWrite(ClkPin, 0); {
fastPinMode(ConvPin, 1); fastPinMode(ClkPin, 1);
fastDigitalWrite(ConvPin, 1); fastDigitalWrite(ClkPin, 0);
fastPinMode(DataPin, 0); fastPinMode(ConvPin, 1);
} fastDigitalWrite(ConvPin, 1);
//---------------------------------------------------------------------------- fastPinMode(DataPin, 0);
/** Read ADS7818 12-bit ADC }
* \return 16-bit value read. //----------------------------------------------------------------------------
*/ /** Read ADS7818 12-bit ADC
inline __attribute__((always_inline)) * \return 16-bit value read.
uint16_t read() { */
uint16_t v = 0; inline __attribute__((always_inline))
fastDigitalWrite(ConvPin, 0); uint16_t read()
fastDigitalWrite(ClkPin, 1); {
fastDigitalWrite(ClkPin, 0); uint16_t v = 0;
fastDigitalWrite(ConvPin, 1); fastDigitalWrite(ConvPin, 0);
fastDigitalWrite(ClkPin, 1); fastDigitalWrite(ClkPin, 1);
fastDigitalWrite(ClkPin, 0); fastDigitalWrite(ClkPin, 0);
fastDigitalWrite(ConvPin, 1);
fastDigitalWrite(ClkPin, 1);
fastDigitalWrite(ClkPin, 0);
readBitFast16(v, 11); readBitFast16(v, 11);
readBitFast16(v, 10); readBitFast16(v, 10);
readBitFast16(v, 9); readBitFast16(v, 9);
readBitFast16(v, 8); readBitFast16(v, 8);
readBitFast16(v, 7); readBitFast16(v, 7);
readBitFast16(v, 6); readBitFast16(v, 6);
readBitFast16(v, 5); readBitFast16(v, 5);
readBitFast16(v, 4); readBitFast16(v, 4);
readBitFast16(v, 3); readBitFast16(v, 3);
readBitFast16(v, 2); readBitFast16(v, 2);
readBitFast16(v, 1); readBitFast16(v, 1);
readBitFast16(v, 0); readBitFast16(v, 0);
return v; return v;
} }
private: private:
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void readBitFast16(uint16_t &v, uint8_t b) { void readBitFast16(uint16_t &v, uint8_t b)
fastDigitalWrite(ClkPin, 1); {
if (fastDigitalRead(DataPin)) v |= (1 << b); fastDigitalWrite(ClkPin, 1);
fastDigitalWrite(ClkPin, 0); if (fastDigitalRead(DataPin)) {
} v |= (1 << b);
}
fastDigitalWrite(ClkPin, 0);
}
}; };
#if 0 #if 0
/** initialize I/O ports */ /** initialize I/O ports */
STATIC_ALWAYS_INLINE void adcBegin() { STATIC_ALWAYS_INLINE void adcBegin()
fastPinMode(ADC_CLK_PIN, 1); {
fastDigitalWrite(ADC_CLK_PIN, 0); fastPinMode(ADC_CLK_PIN, 1);
fastPinMode(ADC_CONV_PIN, 1); fastDigitalWrite(ADC_CLK_PIN, 0);
fastDigitalWrite(ADC_CONV_PIN, 1); fastPinMode(ADC_CONV_PIN, 1);
fastPinMode(ADC_DATA_PIN, 0); fastDigitalWrite(ADC_CONV_PIN, 1);
fastPinMode(ADC_DATA_PIN, 0);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** clock for don't care bits /** clock for don't care bits
*\param[in] delayBefore if true, delay before raising clock*/ *\param[in] delayBefore if true, delay before raising clock*/
STATIC_ALWAYS_INLINE void fastDummy(bool first) { STATIC_ALWAYS_INLINE void fastDummy(bool first)
if (!first) nop; {
fastDigitalWrite(ADC_CLK_PIN, 1); if (!first) {
nop; nop;
fastDigitalWrite(ADC_CLK_PIN, 0); }
fastDigitalWrite(ADC_CLK_PIN, 1);
nop;
fastDigitalWrite(ADC_CLK_PIN, 0);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** read next bit fast as possible /** read next bit fast as possible
* \param[in] v word to receive bit * \param[in] v word to receive bit
* \param[in] b bit number to be set. v |= (1 << b) if next bit is high. * \param[in] b bit number to be set. v |= (1 << b) if next bit is high.
*/ */
STATIC_ALWAYS_INLINE void readBitFast16(uint16_t &v, uint8_t b) { STATIC_ALWAYS_INLINE void readBitFast16(uint16_t &v, uint8_t b)
fastDigitalWrite(ADC_CLK_PIN, 1); {
if (fastDigitalRead(ADC_DATA_PIN)) v |= (1 << b); fastDigitalWrite(ADC_CLK_PIN, 1);
fastDigitalWrite(ADC_CLK_PIN, 0); if (fastDigitalRead(ADC_DATA_PIN)) {
v |= (1 << b);
}
fastDigitalWrite(ADC_CLK_PIN, 0);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** Read AD7680 16-bit ADC in less than 8 microseconds /** Read AD7680 16-bit ADC in less than 8 microseconds
* cs is chip select pin * cs is chip select pin
*/ */
STATIC_ALWAYS_INLINE uint16_t adcRead(bool centered = false) { STATIC_ALWAYS_INLINE uint16_t adcRead(bool centered = false)
uint16_t v = 0; {
fastDigitalWrite(ADC_CONV_PIN, 0); uint16_t v = 0;
fastDigitalWrite(ADC_CLK_PIN, 1); fastDigitalWrite(ADC_CONV_PIN, 0);
fastDigitalWrite(ADC_CLK_PIN, 0); fastDigitalWrite(ADC_CLK_PIN, 1);
fastDigitalWrite(ADC_CONV_PIN, 1); fastDigitalWrite(ADC_CLK_PIN, 0);
fastDigitalWrite(ADC_CLK_PIN, 1); fastDigitalWrite(ADC_CONV_PIN, 1);
fastDigitalWrite(ADC_CLK_PIN, 0); fastDigitalWrite(ADC_CLK_PIN, 1);
// uint16_t v = 0; fastDigitalWrite(ADC_CLK_PIN, 0);
// uint16_t v = 0;
// fastDummy(1); // fastDummy(1);
// fastDummy(0); // fastDummy(0);
// fastDummy(0); // fastDummy(0);
// fastDummy(0); // fastDummy(0);
// readBitFast16(v, 15); // readBitFast16(v, 15);
// readBitFast16(v, 14); // readBitFast16(v, 14);
// readBitFast16(v, 13); // readBitFast16(v, 13);
// readBitFast16(v, 12); // readBitFast16(v, 12);
readBitFast16(v, 11); readBitFast16(v, 11);
readBitFast16(v, 10); readBitFast16(v, 10);
readBitFast16(v, 9); readBitFast16(v, 9);
readBitFast16(v, 8); readBitFast16(v, 8);
readBitFast16(v, 7); readBitFast16(v, 7);
readBitFast16(v, 6); readBitFast16(v, 6);
readBitFast16(v, 5); readBitFast16(v, 5);
readBitFast16(v, 4); readBitFast16(v, 4);
readBitFast16(v, 3); readBitFast16(v, 3);
readBitFast16(v, 2); readBitFast16(v, 2);
readBitFast16(v, 1); readBitFast16(v, 1);
readBitFast16(v, 0); readBitFast16(v, 0);
return centered ? v ^ 0X8000 : v; return centered ? v ^ 0X8000 : v;
} }
#endif // 0 #endif // 0
#endif // ADS7818_h #endif // ADS7818_h
+282 -239
View File
@@ -4,259 +4,302 @@
//============================================================================== //==============================================================================
template<uint8_t CsPin, uint8_t ClkPin, uint8_t DoutPin, uint8_t UsecDelay = 0> template<uint8_t CsPin, uint8_t ClkPin, uint8_t DoutPin, uint8_t UsecDelay = 0>
/** MCP3201 class */ /** MCP3201 class */
class MCP3201 { class MCP3201
public: {
//---------------------------------------------------------------------------- public:
/** Set pin modes and initial levels. */ //----------------------------------------------------------------------------
void begin() { /** Set pin modes and initial levels. */
fastPinMode(ClkPin, 1); void begin()
fastDigitalWrite(ClkPin, 0); {
fastPinMode(CsPin, 1); fastPinMode(ClkPin, 1);
fastDigitalWrite(CsPin, 1); fastDigitalWrite(ClkPin, 0);
fastPinMode(DoutPin, 0); fastPinMode(CsPin, 1);
} fastDigitalWrite(CsPin, 1);
//---------------------------------------------------------------------------- fastPinMode(DoutPin, 0);
/** Read MCP3201 12-bit ADC }
* \return 16-bit value read. //----------------------------------------------------------------------------
*/ /** Read MCP3201 12-bit ADC
inline __attribute__((always_inline)) * \return 16-bit value read.
uint16_t read() { */
fastDigitalWrite(CsPin, 0); inline __attribute__((always_inline))
// Start sample. uint16_t read()
mcpAdcDummy(3); {
// Extra sample time. fastDigitalWrite(CsPin, 0);
if (UsecDelay){ // Start sample.
delayMicroseconds(UsecDelay); mcpAdcDummy(3);
} // Extra sample time.
// End sample. if (UsecDelay) {
mcpAdcDummy(3); delayMicroseconds(UsecDelay);
// Null bit. }
mcpAdcDummy(3); // End sample.
uint16_t v = 0; mcpAdcDummy(3);
readBit(v, 11); // Null bit.
readBit(v, 10); mcpAdcDummy(3);
readBit(v, 9); uint16_t v = 0;
readBit(v, 8); readBit(v, 11);
readBit(v, 7); readBit(v, 10);
readBit(v, 6); readBit(v, 9);
readBit(v, 5); readBit(v, 8);
readBit(v, 4); readBit(v, 7);
readBit(v, 3); readBit(v, 6);
readBit(v, 2); readBit(v, 5);
readBit(v, 1); readBit(v, 4);
readBit(v, 0); readBit(v, 3);
fastDigitalWrite(CsPin, 1); readBit(v, 2);
return v; readBit(v, 1);
} readBit(v, 0);
private: fastDigitalWrite(CsPin, 1);
//---------------------------------------------------------------------------- return v;
/** delay n nops }
* \param[in] n nops to delay, must be a constant so compiler optimizes if(). private:
*/ //----------------------------------------------------------------------------
inline __attribute__((always_inline)) /** delay n nops
void delayCycles(uint8_t n) { * \param[in] n nops to delay, must be a constant so compiler optimizes if().
if (n & 1) asm volatile("nop\n\t"); */
if (n & 2) asm volatile("nop\n\t" "nop\n\t"); inline __attribute__((always_inline))
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); void delayCycles(uint8_t n)
} {
//---------------------------------------------------------------------------- if (n & 1) {
/** clock for don't care bits asm volatile("nop\n\t");
*\param[in] delayBefore if true, delay before raising clock*/ }
inline __attribute__((always_inline)) if (n & 2) {
void mcpAdcDummy(uint8_t delayBefore) { asm volatile("nop\n\t" "nop\n\t");
delayCycles(delayBefore); }
fastDigitalWrite(ClkPin, 1); if (n & 4) {
delayCycles(3); asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
fastDigitalWrite(ClkPin, 0); }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
inline __attribute__((always_inline)) /** clock for don't care bits
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 2) { *\param[in] delayBefore if true, delay before raising clock*/
delayCycles(3); inline __attribute__((always_inline))
fastDigitalWrite(ClkPin, 1); void mcpAdcDummy(uint8_t delayBefore)
delayCycles(delayRead); {
if (fastDigitalRead(DoutPin)) v |= (1 << b); delayCycles(delayBefore);
fastDigitalWrite(ClkPin, 0); fastDigitalWrite(ClkPin, 1);
} delayCycles(3);
fastDigitalWrite(ClkPin, 0);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 2)
{
delayCycles(3);
fastDigitalWrite(ClkPin, 1);
delayCycles(delayRead);
if (fastDigitalRead(DoutPin)) {
v |= (1 << b);
}
fastDigitalWrite(ClkPin, 0);
}
}; };
//============================================================================== //==============================================================================
template<uint8_t CsPin, uint8_t ClkPin, template<uint8_t CsPin, uint8_t ClkPin,
uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0> uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0>
/** MCP3202 class */ /** MCP3202 class */
class MCP3202 { class MCP3202
public: {
//---------------------------------------------------------------------------- public:
/** Set pin modes and initial levels. */ //----------------------------------------------------------------------------
void begin() { /** Set pin modes and initial levels. */
fastPinMode(ClkPin, 1); void begin()
fastDigitalWrite(ClkPin, 0); {
fastPinMode(CsPin, 1); fastPinMode(ClkPin, 1);
fastDigitalWrite(CsPin, 1); fastDigitalWrite(ClkPin, 0);
fastPinMode(DoutPin, 0); fastPinMode(CsPin, 1);
fastPinMode(DinPin, 1); fastDigitalWrite(CsPin, 1);
} fastPinMode(DoutPin, 0);
//---------------------------------------------------------------------------- fastPinMode(DinPin, 1);
/** Read MCP3202 12-bit ADC }
* \return 16-bit value read. //----------------------------------------------------------------------------
*/ /** Read MCP3202 12-bit ADC
inline __attribute__((always_inline)) * \return 16-bit value read.
uint16_t read(uint8_t config) { */
uint16_t v = 0; inline __attribute__((always_inline))
fastDigitalWrite(CsPin, 0); uint16_t read(uint8_t config)
// Start bit. {
writeBit(true); uint16_t v = 0;
// Mode bit. fastDigitalWrite(CsPin, 0);
writeBit(config & 2); // Start bit.
// Channel Selection and start sample. writeBit(true);
writeBit(config & 1); // Mode bit.
// Extra sample time. writeBit(config & 2);
if (UsecDelay){ // Channel Selection and start sample.
delayMicroseconds(UsecDelay); writeBit(config & 1);
} // Extra sample time.
// MSB first format and end sample. if (UsecDelay) {
writeBit(true); delayMicroseconds(UsecDelay);
// Null bit. }
writeBit(true); // MSB first format and end sample.
// Data bits. writeBit(true);
readBit(v, 11); // Null bit.
readBit(v, 10); writeBit(true);
readBit(v, 9); // Data bits.
readBit(v, 8); readBit(v, 11);
readBit(v, 7); readBit(v, 10);
readBit(v, 6); readBit(v, 9);
readBit(v, 5); readBit(v, 8);
readBit(v, 4); readBit(v, 7);
readBit(v, 3); readBit(v, 6);
readBit(v, 2); readBit(v, 5);
readBit(v, 1); readBit(v, 4);
readBit(v, 0); readBit(v, 3);
fastDigitalWrite(CsPin, 1); readBit(v, 2);
return v; readBit(v, 1);
} readBit(v, 0);
private: fastDigitalWrite(CsPin, 1);
//---------------------------------------------------------------------------- return v;
/** delay n nops }
* \param[in] n nops to delay, must be a constant so compiler optimizes if(). private:
*/ //----------------------------------------------------------------------------
inline __attribute__((always_inline)) /** delay n nops
void delayCycles(uint8_t n) { * \param[in] n nops to delay, must be a constant so compiler optimizes if().
if (n & 1) asm volatile("nop\n\t"); */
if (n & 2) asm volatile("nop\n\t" "nop\n\t"); inline __attribute__((always_inline))
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); void delayCycles(uint8_t n)
} {
//---------------------------------------------------------------------------- if (n & 1) {
/** clock for don't care bits asm volatile("nop\n\t");
*\param[in] delayBefore if true, delay before raising clock*/ }
inline __attribute__((always_inline)) if (n & 2) {
void writeBit(bool value) { asm volatile("nop\n\t" "nop\n\t");
fastDigitalWrite(DinPin, value); }
fastDigitalWrite(ClkPin, 1); if (n & 4) {
delayCycles(2); asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
fastDigitalWrite(ClkPin, 0); }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
inline __attribute__((always_inline)) /** clock for don't care bits
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 1) { *\param[in] delayBefore if true, delay before raising clock*/
delayCycles(2); inline __attribute__((always_inline))
fastDigitalWrite(ClkPin, 1); void writeBit(bool value)
delayCycles(delayRead); {
if (fastDigitalRead(DoutPin)) v |= (1 << b); fastDigitalWrite(DinPin, value);
fastDigitalWrite(ClkPin, 0); fastDigitalWrite(ClkPin, 1);
} delayCycles(2);
fastDigitalWrite(ClkPin, 0);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 1)
{
delayCycles(2);
fastDigitalWrite(ClkPin, 1);
delayCycles(delayRead);
if (fastDigitalRead(DoutPin)) {
v |= (1 << b);
}
fastDigitalWrite(ClkPin, 0);
}
}; };
//============================================================================== //==============================================================================
template<uint8_t CsPin, uint8_t ClkPin, template<uint8_t CsPin, uint8_t ClkPin,
uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0> uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0>
/** MCP3204 class */ /** MCP3204 class */
class MCP3204 { class MCP3204
public: {
//---------------------------------------------------------------------------- public:
/** Set pin modes and initial levels. */ //----------------------------------------------------------------------------
void begin() { /** Set pin modes and initial levels. */
fastPinMode(ClkPin, 1); void begin()
fastDigitalWrite(ClkPin, 0); {
fastPinMode(CsPin, 1); fastPinMode(ClkPin, 1);
fastDigitalWrite(CsPin, 1); fastDigitalWrite(ClkPin, 0);
fastPinMode(DoutPin, 0); fastPinMode(CsPin, 1);
fastPinMode(DinPin, 1); fastDigitalWrite(CsPin, 1);
} fastPinMode(DoutPin, 0);
//---------------------------------------------------------------------------- fastPinMode(DinPin, 1);
/** Read MCP3204 12-bit ADC }
* \return 16-bit value read. //----------------------------------------------------------------------------
*/ /** Read MCP3204 12-bit ADC
inline __attribute__((always_inline)) * \return 16-bit value read.
uint16_t read(uint8_t config) { */
uint16_t v = 0; inline __attribute__((always_inline))
fastDigitalWrite(CsPin, 0); uint16_t read(uint8_t config)
// Start bit. {
writeBit(true); uint16_t v = 0;
// Mode bit. fastDigitalWrite(CsPin, 0);
writeBit(config & 8); // Start bit.
// D2 channel bit. writeBit(true);
writeBit(config & 4); // Mode bit.
// D1 channel bit. writeBit(config & 8);
writeBit(config & 2); // D2 channel bit.
// D0 channel bit and start sample. writeBit(config & 4);
writeBit(config & 1); // D1 channel bit.
// Extra sample time. writeBit(config & 2);
if (UsecDelay){ // D0 channel bit and start sample.
delayMicroseconds(UsecDelay); writeBit(config & 1);
} // Extra sample time.
// End sample cycle. if (UsecDelay) {
writeBit(true); delayMicroseconds(UsecDelay);
// Null bit. }
writeBit(true); // End sample cycle.
// Data bits. writeBit(true);
readBit(v, 11); // Null bit.
readBit(v, 10); writeBit(true);
readBit(v, 9); // Data bits.
readBit(v, 8); readBit(v, 11);
readBit(v, 7); readBit(v, 10);
readBit(v, 6); readBit(v, 9);
readBit(v, 5); readBit(v, 8);
readBit(v, 4); readBit(v, 7);
readBit(v, 3); readBit(v, 6);
readBit(v, 2); readBit(v, 5);
readBit(v, 1); readBit(v, 4);
readBit(v, 0); readBit(v, 3);
fastDigitalWrite(CsPin, 1); readBit(v, 2);
return v; readBit(v, 1);
} readBit(v, 0);
private: fastDigitalWrite(CsPin, 1);
//---------------------------------------------------------------------------- return v;
/** delay n nops }
* \param[in] n nops to delay, must be a constant so compiler optimizes if(). private:
*/ //----------------------------------------------------------------------------
inline __attribute__((always_inline)) /** delay n nops
void delayCycles(uint8_t n) { * \param[in] n nops to delay, must be a constant so compiler optimizes if().
if (n & 1) asm volatile("nop\n\t"); */
if (n & 2) asm volatile("nop\n\t" "nop\n\t"); inline __attribute__((always_inline))
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); void delayCycles(uint8_t n)
} {
//---------------------------------------------------------------------------- if (n & 1) {
/** clock for don't care bits asm volatile("nop\n\t");
*\param[in] delayBefore if true, delay before raising clock*/ }
inline __attribute__((always_inline)) if (n & 2) {
void writeBit(bool value) { asm volatile("nop\n\t" "nop\n\t");
fastDigitalWrite(DinPin, value); }
fastDigitalWrite(ClkPin, 1); if (n & 4) {
delayCycles(2); asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
fastDigitalWrite(ClkPin, 0); }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
inline __attribute__((always_inline)) /** clock for don't care bits
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 1) { *\param[in] delayBefore if true, delay before raising clock*/
delayCycles(2); inline __attribute__((always_inline))
fastDigitalWrite(ClkPin, 1); void writeBit(bool value)
delayCycles(delayRead); {
if (fastDigitalRead(DoutPin)) v |= (1 << b); fastDigitalWrite(DinPin, value);
fastDigitalWrite(ClkPin, 0); fastDigitalWrite(ClkPin, 1);
} delayCycles(2);
fastDigitalWrite(ClkPin, 0);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 1)
{
delayCycles(2);
fastDigitalWrite(ClkPin, 1);
delayCycles(delayRead);
if (fastDigitalRead(DoutPin)) {
v |= (1 << b);
}
fastDigitalWrite(ClkPin, 0);
}
}; };
//============================================================================== //==============================================================================
// MCP3204 and MCP3208 use the same code. // MCP3204 and MCP3208 use the same code.
template<uint8_t CsPin, uint8_t ClkPin, template<uint8_t CsPin, uint8_t ClkPin,
uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0> uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0>
class MCP3208 : public MCP3204<CsPin, ClkPin, DoutPin, DinPin, UsecDelay> { class MCP3208 : public MCP3204<CsPin, ClkPin, DoutPin, DinPin, UsecDelay>
{
}; };
#endif // MCP320X_h #endif // MCP320X_h
+139 -113
View File
@@ -9,119 +9,145 @@ const uint8_t MCP355X_TO_MS = 100;
//============================================================================== //==============================================================================
template<uint8_t CsPin, uint8_t SckPin, uint8_t SdoPin> template<uint8_t CsPin, uint8_t SckPin, uint8_t SdoPin>
/** MCP355X class */ /** MCP355X class */
class MCP355X { class MCP355X
public: {
//---------------------------------------------------------------------------- public:
/** Set pin modes and initial levels. */ //----------------------------------------------------------------------------
void begin(bool singleMode = true) { /** Set pin modes and initial levels. */
m_singleMode = singleMode; void begin(bool singleMode = true)
fastPinMode(SdoPin, 0); {
fastPinMode(SckPin, 1); m_singleMode = singleMode;
fastDigitalWrite(SckPin, 1); fastPinMode(SdoPin, 0);
fastPinMode(CsPin, 1); fastPinMode(SckPin, 1);
fastDigitalWrite(CsPin, 1); fastDigitalWrite(SckPin, 1);
if (!m_singleMode) { fastPinMode(CsPin, 1);
// Wait for conversion to complete. fastDigitalWrite(CsPin, 1);
delay(100); if (!m_singleMode) {
// Start continuous conversion mode. // Wait for conversion to complete.
fastDigitalWrite(CsPin, 0); delay(100);
} // Start continuous conversion mode.
} fastDigitalWrite(CsPin, 0);
//---------------------------------------------------------------------------- }
/** Read MCP355X 22-bit ADC }
* \return 22-bit value. //----------------------------------------------------------------------------
*/ /** Read MCP355X 22-bit ADC
inline __attribute__((always_inline)) * \return 22-bit value.
int32_t read() { */
uint8_t t = 0; inline __attribute__((always_inline))
uint8_t v3 = 0; int32_t read()
if (m_singleMode) { {
// Start conversion. uint8_t t = 0;
fastDigitalWrite(CsPin, 0); uint8_t v3 = 0;
// Delay at least 10 usec to avoid RDY glitch on exit from Shutdown. if (m_singleMode) {
delay(1); // Start conversion.
// Toggle CsPin to indicate single conversion mode. fastDigitalWrite(CsPin, 0);
fastDigitalWrite(CsPin, 1); // Delay at least 10 usec to avoid RDY glitch on exit from Shutdown.
// Wait for conversion to complete. delay(1);
while (1) { // Toggle CsPin to indicate single conversion mode.
delay(1); fastDigitalWrite(CsPin, 1);
fastDigitalWrite(CsPin, 0); // Wait for conversion to complete.
// Delay while RDY settles. while (1) {
delayCycles(4); delay(1);
if (!fastDigitalRead(SdoPin)) break; fastDigitalWrite(CsPin, 0);
fastDigitalWrite(CsPin, 1); // Delay while RDY settles.
if (t++ > MCP355X_TO_MS) return MCP355X_ERR; delayCycles(4);
} if (!fastDigitalRead(SdoPin)) {
} else { break;
while (1) { }
if (!fastDigitalRead(SdoPin)) break; fastDigitalWrite(CsPin, 1);
delay(1); if (t++ > MCP355X_TO_MS) {
if (t++ > MCP355X_TO_MS) return MCP355X_ERR; return MCP355X_ERR;
} }
} }
uint8_t v2 = readByte(); } else {
uint8_t v1 = readByte(); while (1) {
uint8_t v0 = readByte(); if (!fastDigitalRead(SdoPin)) {
break;
// The 25th falling edge of SCK changes SDO/RDY from Data mode to RDY mode. }
uint8_t dummy = 0; delay(1);
readBit(dummy, 0); if (t++ > MCP355X_TO_MS) {
return MCP355X_ERR;
}
}
}
uint8_t v2 = readByte();
uint8_t v1 = readByte();
uint8_t v0 = readByte();
if (m_singleMode) { // The 25th falling edge of SCK changes SDO/RDY from Data mode to RDY mode.
fastDigitalWrite(CsPin, 1); uint8_t dummy = 0;
} readBit(dummy, 0);
if ((v2 & 0XE0) == 0X20) {
// Negative in range so extend sign bit. if (m_singleMode) {
v2 |= 0XC0; fastDigitalWrite(CsPin, 1);
v3 = 0XFF; }
} else if (v2 & 0X40) { if ((v2 & 0XE0) == 0X20) {
// Overflow high. Cause value to be >= MCP355X_OVH. // Negative in range so extend sign bit.
if (v2 & 0X20) v2 &= 0X3F; v2 |= 0XC0;
} else if (v2 & 0X80) { v3 = 0XFF;
// Overflow low. Cause value to be <= MCP355X_OVH. } else if (v2 & 0X40) {
if ((v2 & 0X20) == 0) v2 |= 0X40; // Overflow high. Cause value to be >= MCP355X_OVH.
v3 = 0XFF; if (v2 & 0X20) {
} v2 &= 0X3F;
uint16_t v_high = (v3 << 8) | v2; }
uint16_t v_low = (v1 << 8) | v0; } else if (v2 & 0X80) {
return ((uint32_t)v_high << 16) | v_low; // Overflow low. Cause value to be <= MCP355X_OVH.
} if ((v2 & 0X20) == 0) {
private: v2 |= 0X40;
//---------------------------------------------------------------------------- }
/** delay n nops v3 = 0XFF;
* \param[in] n nops to delay, must be a constant so compiler optimizes if(). }
*/ uint16_t v_high = (v3 << 8) | v2;
inline __attribute__((always_inline)) uint16_t v_low = (v1 << 8) | v0;
void delayCycles(uint8_t n) { return ((uint32_t)v_high << 16) | v_low;
if (n & 1) asm volatile("nop\n\t"); }
if (n & 2) asm volatile("nop\n\t" "nop\n\t"); private:
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); //----------------------------------------------------------------------------
} /** delay n nops
//---------------------------------------------------------------------------- * \param[in] n nops to delay, must be a constant so compiler optimizes if().
// Default delay yields about 2 MHz clock. */
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void readBit(uint8_t &v, uint8_t b, uint8_t delayRead = 0) { void delayCycles(uint8_t n)
fastDigitalWrite(SckPin, 0); {
delayCycles(2 + delayRead); if (n & 1) {
fastDigitalWrite(SckPin, 1); asm volatile("nop\n\t");
if (fastDigitalRead(SdoPin)) v |= (1 << b); }
delayCycles(delayRead); if (n & 2) {
} asm volatile("nop\n\t" "nop\n\t");
//---------------------------------------------------------------------------- }
inline __attribute__((always_inline)) if (n & 4) {
uint8_t readByte() { asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
uint8_t v = 0; }
readBit(v, 7); }
readBit(v, 6); //----------------------------------------------------------------------------
readBit(v, 5); // Default delay yields about 2 MHz clock.
readBit(v, 4); inline __attribute__((always_inline))
readBit(v, 3); void readBit(uint8_t &v, uint8_t b, uint8_t delayRead = 0)
readBit(v, 2); {
readBit(v, 1); fastDigitalWrite(SckPin, 0);
readBit(v, 0); delayCycles(2 + delayRead);
return v; fastDigitalWrite(SckPin, 1);
} if (fastDigitalRead(SdoPin)) {
//---------------------------------------------------------------------------- v |= (1 << b);
bool m_singleMode; }
delayCycles(delayRead);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
uint8_t readByte()
{
uint8_t v = 0;
readBit(v, 7);
readBit(v, 6);
readBit(v, 5);
readBit(v, 4);
readBit(v, 3);
readBit(v, 2);
readBit(v, 1);
readBit(v, 0);
return v;
}
//----------------------------------------------------------------------------
bool m_singleMode;
}; };
#endif // MCP355X_h #endif // MCP355X_h
@@ -4,9 +4,10 @@ DigitalPin<13> pin13(OUTPUT);
void setup() {} void setup() {}
void loop() { void loop()
// toggle is a two byte instruction that executes {
// in two cycles or 125 ns on a 16 MHz CPU // toggle is a two byte instruction that executes
pin13.toggle(); // in two cycles or 125 ns on a 16 MHz CPU
delay(250); pin13.toggle();
delay(250);
} }
@@ -4,15 +4,17 @@
// Class with compile time pin number. // Class with compile time pin number.
DigitalPin<13> pin13; DigitalPin<13> pin13;
void setup() { void setup()
// Set mode to OUTPUT and level LOW. {
pin13.config(OUTPUT, LOW); // Set mode to OUTPUT and level LOW.
pin13.config(OUTPUT, LOW);
} }
void loop() { void loop()
// toggle is a two byte instruction that executes {
// in two cycles or 125 ns on a 16 MHz CPU // toggle is a two byte instruction that executes
pin13.toggle(); // in two cycles or 125 ns on a 16 MHz CPU
delay(100); pin13.toggle();
pin13.toggle(); delay(100);
delay(400); pin13.toggle();
delay(400);
} }
@@ -6,6 +6,7 @@ DigitalPin<13> pin13(OUTPUT);
void setup() {} void setup() {}
void loop() { void loop()
pin13 = pin12; {
pin13 = pin12;
} }
@@ -10,30 +10,34 @@ DigitalPin<13> dataPin(OUTPUT, HIGH);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Time to send one bit is ten cycles or 625 ns for 16 MHz CPU. // Time to send one bit is ten cycles or 625 ns for 16 MHz CPU.
inline __attribute__((always_inline)) inline __attribute__((always_inline))
void sendBit(uint8_t bit, uint8_t data) { void sendBit(uint8_t bit, uint8_t data)
dataPin = data & (1 << bit); {
clockPin = 1; dataPin = data & (1 << bit);
// may want a nop here - clock pulse is 125 ns wide clockPin = 1;
clockPin = 0; // may want a nop here - clock pulse is 125 ns wide
clockPin = 0;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Time to send one byte is 5 usec. // Time to send one byte is 5 usec.
void shiftOut(uint8_t bits) { void shiftOut(uint8_t bits)
sendBit(7, bits); {
sendBit(6, bits); sendBit(7, bits);
sendBit(5, bits); sendBit(6, bits);
sendBit(4, bits); sendBit(5, bits);
sendBit(3, bits); sendBit(4, bits);
sendBit(2, bits); sendBit(3, bits);
sendBit(1, bits); sendBit(2, bits);
sendBit(0, bits); sendBit(1, bits);
sendBit(0, bits);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup()
// Not used. {
// Not used.
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() { void loop()
shiftOut(0X55); {
delay(2); shiftOut(0X55);
delay(2);
} }
@@ -6,19 +6,21 @@
PinIO readPin; PinIO readPin;
PinIO writePin; PinIO writePin;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void setup() { void setup()
// Assign pin 12 to readPin. {
readPin.begin(12); // Assign pin 12 to readPin.
readPin.begin(12);
// input mode with pull-ups disabled
readPin.config(INPUT, LOW);
// Assign pin 13 to writePin and set mode to output. // input mode with pull-ups disabled
writePin.begin(13); readPin.config(INPUT, LOW);
writePin.mode(OUTPUT);
// Assign pin 13 to writePin and set mode to output.
writePin.begin(13);
writePin.mode(OUTPUT);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void loop() { void loop()
// Copy the value read from readPin to writePin. {
writePin.write(readPin.read()); // Copy the value read from readPin to writePin.
writePin.write(readPin.read());
} }

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