diff --git a/.ci/arduino.groovy b/.ci/arduino.groovy index 1d372247f..ff0aa6728 100644 --- a/.ci/arduino.groovy +++ b/.ci/arduino.groovy @@ -1,11 +1,13 @@ #!groovy def buildArduino(config, String buildFlags, String sketch, String key) { - def root = '/opt/arduino-1.8.7/' + def root = '/opt/arduino-1.8.9/' + def build_path = 'build' + def build_path_cmd = ' -build-path '+build_path+' ' if (config.nightly_arduino_ide) { root = '/opt/arduino-nightly/' + // patch for nightly arduino-builder } - //def esp8266_tools = '/opt/arduino-nightly/hardware/esp8266com/esp8266/tools' def jenkins_root = '/var/lib/jenkins/' def builder = root+'arduino-builder' def standard_args = ' -warnings=all' //-verbose=true @@ -14,9 +16,11 @@ def buildArduino(config, String buildFlags, String sketch, String key) { def jenkins_packages = jenkins_root+'.arduino15/packages' def site_specifics = ' -hardware '+jenkins_packages+' -tools '+jenkins_packages def repo_specifics = ' -hardware hardware -libraries . ' - def build_cmd = builder+standard_args+builder_specifics+site_specifics+repo_specifics+buildFlags + def build_cmd = builder+standard_args+builder_specifics+site_specifics+repo_specifics+build_path_cmd+buildFlags sh """#!/bin/bash printf "\\e[1m\\e[32mBuilding \\e[34m${sketch} \\e[0musing \\e[1m\\e[36m${build_cmd}\\e[0m\\n" + if [ -d ${build_path} ]; then rm -r ${build_path}; fi + mkdir ${build_path} ${build_cmd} ${sketch} 2>> compiler_${key}.log""" } @@ -25,9 +29,9 @@ def parseWarnings(String key) { defaultEncoding: '', excludePattern: '''.*/EEPROM\\.h,.*/Dns\\.cpp,.*/socket\\.cpp,.*/util\\.h,.*/Servo\\.cpp, .*/Adafruit_NeoPixel\\.cpp,.*/UIPEthernet.*,.*/SoftwareSerial\\.cpp, - .*/pins_arduino\\.h,.*/Stream\\.cpp,.*/USBCore\\.cpp,.*/Wire\\.cpp, - .*/hardware/STM32F1.*,.*/hardware/esp8266.*,.*/hardware/espressif/esp32.*, - .*/libraries/SD/.*''', + .*/pins_arduino\\.h,.*/Stream\\.cpp,.*/USBCore\\.cpp,.*/libraries/Wire/.*, + .*/hardware/STM32F1.*,.*/hardware/esp8266.*,.*/hardware/esp32.*, + .*/libraries/SD/.*,.*/libraries/Ethernet/.*''', healthy: '', includePattern: '', messagesPattern: '', parserConfigurations: [[parserName: 'Arduino/AVR', pattern: 'compiler_'+key+'.log']], @@ -215,7 +219,7 @@ def buildSTM32F1(config, sketches, String key) { } def buildESP8266(config, sketches, String key) { - def fqbn = '-fqbn=esp8266:esp8266:generic:CpuFrequency=80,VTable=flash,ResetMethod=ck,CrystalFreq=26,FlashFreq=40,FlashMode=qio,FlashSize=512K0,led=2,LwIPVariant=v2mss536,Debug=Disabled,DebugLevel=None____,FlashErase=none,UploadSpeed=115200' + def fqbn = '-fqbn=esp8266:esp8266:generic:xtal=80,vt=flash,exception=disabled,ResetMethod=ck,CrystalFreq=26,FlashFreq=40,FlashMode=dout,eesz=512K,led=2,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=115200' config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (ESP8266 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') try { for (sketch = 0; sketch < sketches.size(); sketch++) { @@ -255,7 +259,7 @@ def buildESP8266(config, sketches, String key) { } def buildESP32(config, sketches, String key) { - def fqbn = '-fqbn espressif:esp32:esp32:PartitionScheme=default,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none -warnings=default' + def fqbn = '-fqbn esp32:esp32:esp32:PartitionScheme=default,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none -warnings=default' config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (ESP32 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') try { for (sketch = 0; sketch < sketches.size(); sketch++) { diff --git a/.ci/butler.sh b/.ci/butler.sh index bdf7db29e..951b5211b 100755 --- a/.ci/butler.sh +++ b/.ci/butler.sh @@ -4,12 +4,15 @@ cd .. result=0 -echo "No subjects are of invalid size
" > too_long_subjects.txt -echo "No subjects with leading lower case characters
" > leading_lowercases.txt -echo "No subjects with trailing periods
" > trailing_periods.txt -echo "No body lines are too wide
" > too_long_body_lines.txt -echo "No keywords are missing in keywords.txt
" > missing_keywords.txt -echo "No occurences of the deprecated boolean data type
" >> booleans.txt +echo "No subjects are of invalid size - great!
" > too_long_subjects.txt +echo "No subjects with leading lower case characters - great!
" > leading_lowercases.txt +echo "No subjects with trailing periods - great!
" > trailing_periods.txt +echo "No body lines are too wide - great!
" > too_long_body_lines.txt +echo "No keywords are missing in keywords.txt - great!
" > missing_keywords.txt +echo "No keywords in code that don't exist in keywords.txt - great!
" > missing_keywords_2.txt +echo "No keywords in code that don't have Doxygen comments and aren't blacklisted in keywords.txt - great!
" > missing_keywords_3.txt +echo "No lines in keywords.txt using spaces instead of TAB (the Arduino IDE doesn't support space) - great!
" > tab_spaces_keywords.txt +echo "No occurences of the deprecated boolean data type - great!
" >> booleans.txt too_long_subjects=`awk 'length > 72' subjects.txt` if [ -n "$too_long_subjects" ]; then @@ -49,6 +52,33 @@ if [ -n "$missing_keywords" ]; then result=1 fi +missing_keywords_2=$(SOURCE_FILES="core/ drivers/ hal/ examples/ examples_linux/ MyConfig.h MySensors.h"; for keyword in $(grep -whore 'MY_[A-Z][A-Z_0-9]*' $SOURCE_FILES | sort -u ); do grep -q $keyword keywords.txt || echo $keyword; done) +if [ -n "$missing_keywords_2" ]; then + echo "Keywords in code that don't exist in keywords.txt:" > missing_keywords_2.txt + echo "If keywords aren't in keywords.txt, they will not be highlighted in the Arduino IDE. Highlighting makes the code easier to follow and helps spot spelling mistakes." > missing_keywords_2.txt + echo "$missing_keywords_2" >> missing_keywords_2.txt + sed -i -e 's/$/
/' missing_keywords_2.txt + result=1 +fi + +missing_keywords_3=$(SOURCE_FILES="core/ drivers/ hal/ examples/ examples_linux/ MyConfig.h MySensors.h"; for keyword in $(grep -whore 'MY_[A-Z][A-Z_0-9]*' $SOURCE_FILES | sort -u ); do grep -q $keyword keywords.txt || echo $keyword; done) +if [ -n "$missing_keywords_3" ]; then + echo "Keywords in code that don't have Doxygen comments and aren't blacklisted in keywords.txt:" > missing_keywords_3.txt + echo "If keywords don't have Doxygen comments, they will not be available at https://www.mysensors.org/apidocs/index.html Add Doxygen comments to make it easier for users to find and understand how to use the new keywords." > missing_keywords_3.txt + echo "$missing_keywords_3" >> missing_keywords_3.txt + sed -i -e 's/$/
/' missing_keywords_3.txt + result=1 +fi + + +tab_spaces_keywords=$(grep -e '[[:space:]]KEYWORD' -e '[[:space:]]LITERAL1' keywords.txt | grep -v -e $'\tLITERAL1' -e $'\tKEYWORD') +if [ -n "$tab_spaces_keywords" ]; then + echo "Keywords that use space instead of TAB in keywords.txt:" > tab_spaces_keywords.txt + echo "$tab_spaces_keywords" >> tab_spaces_keywords.txt + sed -i -e 's/$/
/' tab_spaces_keywords.txt + result=1 +fi + # Evaluate if there exists booleans in the code tree (not counting this file) if git grep -q boolean -- `git ls-files | grep -v butler.sh`; then echo "You have added at least one occurence of the deprecated boolean data type. Please use bool instead.
" > booleans.txt @@ -57,7 +87,7 @@ fi printf "%s" "" > butler.html echo "Greetings! Here is my evaluation of your pull request:
" >> butler.html -awk 'FNR==1{print "
"}1' too_long_subjects.txt leading_lowercases.txt trailing_periods.txt too_long_body_lines.txt missing_keywords.txt booleans.txt >> butler.html +awk 'FNR==1{print "
"}1' too_long_subjects.txt leading_lowercases.txt trailing_periods.txt too_long_body_lines.txt missing_keywords.txt missing_keywords_2.txt missing_keywords_3.txt tab_spaces_keywords.txt booleans.txt >> butler.html echo "
" >> butler.html if [ $result -ne 0 ]; then echo "I am afraid there are some issues with your commit messages and/or use of keywords.
" >> butler.html diff --git a/.ci/doxygen.groovy b/.ci/doxygen.groovy index 9e68b3817..08913f465 100644 --- a/.ci/doxygen.groovy +++ b/.ci/doxygen.groovy @@ -6,7 +6,7 @@ def call(config) { Documentation/doxygen.sh""" warnings canComputeNew: false, canResolveRelativePaths: false, defaultEncoding: '', - excludePattern: '''.*/sha204_library\\.h,.*/drivers/Linux/.*,.*/drivers/TinyGSM/.*,.*/cores/esp8266/.*,hardware/.*''', + excludePattern: '''.*/hal/architecture/Linux/drivers/.*,.*/hal/architecture/AVR/drivers/.*,.*/drivers/TinyGSM/.*''', failedTotalAll: '', healthy: '', includePattern: '', messagesPattern: '', parserConfigurations: [[parserName: 'Doxygen', pattern: config.repository_root+'doxygen.log']], unHealthy: '', unstableTotalAll: '0' diff --git a/.ci/static_analysis.groovy b/.ci/static_analysis.groovy index 642176e1a..af8a2ce11 100644 --- a/.ci/static_analysis.groovy +++ b/.ci/static_analysis.groovy @@ -5,7 +5,7 @@ def cppCheck(config) { sh """#!/bin/bash +x cd ${config.repository_root} echo "Doing cppcheck for AVR..." - find . -type f \\( -iname \\*.c -o -iname \\*.cpp -o -iname \\*.ino \\) | cppcheck -j 4 --force --file-list=- --enable=style,information --platform=.mystools/cppcheck/config/avr.xml --suppressions-list=.mystools/cppcheck/config/suppressions.cfg --includes-file=.mystools/cppcheck/config/includes.cfg --language=c++ --inline-suppr --xml --xml-version=2 2> cppcheck-avr.xml + find . -type f \\( -iname \\*.c -o -iname \\*.cpp -o -iname \\*.ino \\) | cppcheck -j 4 --force --file-list=- --enable=style,portability,performance --platform=.mystools/cppcheck/config/avr.xml --suppressions-list=.mystools/cppcheck/config/suppressions.cfg --includes-file=.mystools/cppcheck/config/includes.cfg --language=c++ --inline-suppr --xml --xml-version=2 2> cppcheck-avr.xml cppcheck-htmlreport --file="cppcheck-avr.xml" --title="cppcheck-avr" --report-dir=cppcheck-avr_cppcheck_reports --source-dir=.""" publishHTML([allowMissing: false, alwaysLinkToLastBuild: false, keepAll: true, diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..8fbde8c5a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://www.mysensors.org/hall-of-fame diff --git a/.gitignore b/.gitignore index 0af2be61c..bec8ade2e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ bin .idea doxygen.log TAGS -tags \ No newline at end of file +tags +.DS_Store diff --git a/.mystools/README.md b/.mystools/README.md index 19a674251..4c8c034a1 100644 --- a/.mystools/README.md +++ b/.mystools/README.md @@ -4,10 +4,10 @@ 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, +across modalities (e.g. local development, continuous integration, editor linters, etc.) -1. All common tools are hosted and managed in +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, @@ -15,7 +15,7 @@ editor linters, etc.) configuration files and a run script 3. A single bootstrap script configures a development environment -4. A lightweight runtime provides a common set of +4. A lightweight runtime provides a common set of convenience functions and variables to all scripts 5. Support for all MySensors development environments @@ -92,7 +92,7 @@ One or more required tools not found. Install required tools and re-run .mystoo 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 +Astyle 3.1 or later and Cppcheck 1.88 or later. Once you have installed AStyle and Cppcheck, re-run bootstrap-dev.sh to finish configuring your development environment. @@ -106,7 +106,18 @@ configuring your development environment. #### Linux Trusty or earlier -##### Note: The apt versions are too old on Trusty so follow the [Linux - Build and Install from Source](#buildFromSource) instructions +Note: The apt versions are too old on Trusty so follow the [Linux - Build and Install from Source](#buildFromSource) instructions + +##### Windows - WSL +Tested with Ubuntu 18-04 LTS and 19.04 +``` +apt-get install astyle +git clone https://github.com/danmar/cppcheck.git +cd cppcheck/ +git checkout 1.89 # or later version if available +mkdir build && cd build && cmake .. && cmake --build . +sudo make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" install +``` ##### Windows - GitHub Git Shell @@ -142,13 +153,9 @@ iwr 'http://github.com/danmar/cppcheck/releases/download/1.76.1/cppcheck-1.76.1- ### 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 +Run Either Cygwin Setup-x86-64.exe or Setup-x86.exe depending upon your OS. Select and install astyle and cppcheck ``` ##### Linux - Build and Install from Source @@ -163,15 +170,12 @@ curl -L 'https://sourceforge.net/projects/astyle/files/astyle/astyle%202.05.1/as 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 - +``` +git clone https://github.com/danmar/cppcheck.git +cd cppcheck/ +git checkout 1.89 # or later version if available) +mkdir build && cd build && cmake .. && cmake --build . +sudo make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" install ``` ### Implementation Details diff --git a/.mystools/bootstrap-dev.sh b/.mystools/bootstrap-dev.sh index 33c689dd7..95ff6c369 100755 --- a/.mystools/bootstrap-dev.sh +++ b/.mystools/bootstrap-dev.sh @@ -18,11 +18,12 @@ 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') + local version=$(${1} --version 2>&1 | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') if [ $(ver ${version}) -lt $(ver ${2}) ]; then warn "Found ${1} ${version} however, version ${2} or greater is required..." return 1 + else + log "Found ${1} ${version}" fi else warn "${1} not installed or not in current path." @@ -73,8 +74,8 @@ check_git_remote "upstream" "${mysrepo}" || { #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 "astyle" "3.1" || err "Install AStyle 3.1 or greater and re-run ${0}" +check_tool_prerequisite "cppcheck" "1.88" || err "Install Cppcheck 1.88 or greater and re-run ${0}" check_tool_prerequisite "git" "2.0" || err "Install git 2.0 or greater and re-run ${0}" #4 diff --git a/.mystools/cppcheck/config/includes.cfg b/.mystools/cppcheck/config/includes.cfg index 76f432b2b..e69de29bb 100644 --- a/.mystools/cppcheck/config/includes.cfg +++ b/.mystools/cppcheck/config/includes.cfg @@ -1,5 +0,0 @@ -. -drivers/Linux -drivers/AES -drivers/BCM -core diff --git a/.mystools/cppcheck/config/suppressions.cfg b/.mystools/cppcheck/config/suppressions.cfg index 18171c423..a2450122c 100644 --- a/.mystools/cppcheck/config/suppressions.cfg +++ b/.mystools/cppcheck/config/suppressions.cfg @@ -1,4 +1,6 @@ -ConfigurationNotChecked -unmatchedSuppression -// This suppression is because the problem is in an in-lined macro so in-line-suppressions does not appear to take effect -unreadVariable:*/MyHwNRF5.cpp +// inline suppression doesn't work +*:hal/architecture/NRF5/drivers/Flash.cpp +*:hal/transport/RFM69/driver/old/RFM69_old.cpp +// 3rd party +*:hal/architecture/Linux/* +*:drivers/* diff --git a/.mystools/cppcheck/config/unix32.xml b/.mystools/cppcheck/config/unix32.xml deleted file mode 100644 index bc4ca0e89..000000000 --- a/.mystools/cppcheck/config/unix32.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - 8 - unsigned - - 2 - 4 - 4 - 8 - 4 - 8 - 12 - 4 - 4 - 2 - - diff --git a/.mystools/cppcheck/options.sh b/.mystools/cppcheck/options.sh index a28e62d4a..43b79ca08 100755 --- a/.mystools/cppcheck/options.sh +++ b/.mystools/cppcheck/options.sh @@ -1,15 +1,15 @@ #!/bin/bash -OPTIONS="--quiet \ - --error-exitcode=1 \ - --force \ - --enable=style,information \ - -DCPPCHECK \ - --language=c++ \ - --library=${LIBRARY:-avr} \ - --platform="${TOOLCONFIG}"/${PLATFORM:-avr.xml} \ +OPTIONS="--quiet \ + --error-exitcode=1 \ + --force \ + --enable=style,portability,performance \ + -DCPPCHECK \ + --language=c++ \ + --library=${LIBRARY:-avr} \ + --platform="${TOOLCONFIG}"/${PLATFORM:-avr.xml} \ --includes-file="${TOOLCONFIG}"/includes.cfg \ - --inline-suppr \ + --inline-suppr \ --suppressions-list="${TOOLCONFIG}"/suppressions.cfg" echo $OPTIONS diff --git a/Documentation/mainpage.dox b/Documentation/mainpage.dox index 9cc8d2973..b3cc614b0 100644 --- a/Documentation/mainpage.dox +++ b/Documentation/mainpage.dox @@ -5,7 +5,7 @@ Are you a sketch developer/user, see @ref publics Are you a core/library developer, see @ref internals -@copyright (C) 2013-2018 Sensnology AB +@copyright (C) 2013-2019 Sensnology AB Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors */ diff --git a/Documentation/plantuml.jar b/Documentation/plantuml.jar index 9528a42ec..cd1c25608 100644 Binary files a/Documentation/plantuml.jar and b/Documentation/plantuml.jar differ diff --git a/Documentation/styles/html/footerFile.html b/Documentation/styles/html/footerFile.html index 6413607af..4d501e845 100644 --- a/Documentation/styles/html/footerFile.html +++ b/Documentation/styles/html/footerFile.html @@ -12,7 +12,7 @@ diff --git a/Doxyfile b/Doxyfile index 513899d58..052a4ca25 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.6 +# Doxyfile 1.8.15 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -46,10 +46,10 @@ PROJECT_NUMBER = $(PROJECTNUMBER) PROJECT_BRIEF = -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = Documentation/img/logo.png @@ -60,7 +60,7 @@ PROJECT_LOGO = Documentation/img/logo.png OUTPUT_DIRECTORY = Documentation -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where @@ -70,6 +70,14 @@ OUTPUT_DIRECTORY = Documentation CREATE_SUBDIRS = NO +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. @@ -85,14 +93,22 @@ CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -127,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -197,9 +213,9 @@ MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -218,10 +234,15 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) -ALIASES = "startuml{1}=\image html \1\n\image latex \1\n\if DontIgnorePlantUMLCode" -ALIASES += "enduml=\endif" +ALIASES = "startuml{1}=\image html \1\n\image latex \1\n\if DontIgnorePlantUMLCode" \ + "enduml=\endif" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" @@ -257,16 +278,28 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. @@ -275,7 +308,7 @@ EXTENSION_MAPPING = ino=C++ # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -283,10 +316,19 @@ EXTENSION_MAPPING = ino=C++ MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES @@ -308,7 +350,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -326,13 +368,20 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -391,7 +440,7 @@ LOOKUP_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -401,35 +450,35 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -454,21 +503,21 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be +# (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -482,7 +531,7 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also +# names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. @@ -491,12 +540,19 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -524,14 +580,14 @@ INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. @@ -576,27 +632,25 @@ SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -621,8 +675,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -667,11 +721,10 @@ LAYOUT_FILE = Documentation/styles/DoxygenLayout.xml # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. +# search path. See also \cite for info how to create references. CITE_BIB_FILES = @@ -687,7 +740,7 @@ CITE_BIB_FILES = QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -695,7 +748,7 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. @@ -712,12 +765,19 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -741,15 +801,16 @@ WARN_LOGFILE = doxygen.log # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = . Documentation/mainpage.dox +INPUT = . \ + Documentation/mainpage.dox # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -757,14 +818,20 @@ INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. -FILE_PATTERNS = *.h *.ino +FILE_PATTERNS = *.h \ + *.ino # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -812,7 +879,8 @@ EXCLUDE_SYMBOLS = # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = examples drivers/AES +EXAMPLE_PATH = examples \ + hal/crypto/generic/drivers/AES # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and @@ -848,6 +916,10 @@ IMAGE_PATH = Documentation/img # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -857,11 +929,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -893,7 +969,7 @@ USE_MDFILE_AS_MAINPAGE = # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = NO +SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -909,7 +985,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -921,7 +997,7 @@ REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -941,12 +1017,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -968,6 +1044,35 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -998,7 +1103,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1058,18 +1163,20 @@ HTML_FOOTER = Documentation/styles/html/footerFile.html # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = Documentation/styles/html/styleSheetFile.css -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = Documentation/styles/html/styleSheetFile.css +HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1082,9 +1189,9 @@ HTML_EXTRA_STYLESHEET = Documentation/styles/html/styleSheetFile.css HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to +# will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1113,12 +1220,24 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1142,13 +1261,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1187,7 +1306,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1210,28 +1329,29 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1262,7 +1382,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1270,7 +1390,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1279,7 +1399,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1287,7 +1407,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1295,7 +1415,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1344,7 +1464,7 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has @@ -1372,7 +1492,7 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1388,7 +1508,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1400,8 +1520,8 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1427,8 +1547,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1471,11 +1591,11 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. There -# are two flavours of web server based searching depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. See -# the section "External Indexing and Searching" for details. +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1487,9 +1607,9 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1500,9 +1620,9 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1538,7 +1658,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1554,22 +1674,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: \makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = \makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1587,9 +1720,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1603,23 +1739,36 @@ EXTRA_PACKAGES = # # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will -# replace them by respectively the title of the page, the current date and time, -# only the current date, the version number of doxygen, the project name (see -# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. # # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1637,8 +1786,8 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a # higher quality PDF documentation. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1673,17 +1822,33 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1698,7 +1863,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1718,9 +1883,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1729,17 +1894,27 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1763,6 +1938,13 @@ MAN_OUTPUT = man MAN_EXTENSION = .3 +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without @@ -1776,7 +1958,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1790,7 +1972,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1799,11 +1981,18 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1817,14 +2006,23 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1833,7 +2031,7 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1841,7 +2039,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1849,9 +2047,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1871,14 +2069,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. @@ -1894,7 +2092,7 @@ MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1936,9 +2134,9 @@ PREDEFINED = "DOXYGEN=1" EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an -# all uppercase name, and do not end with a semicolon. Such function macros are -# typically used for boiler-plate code, and will confuse the parser if not +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1958,7 +2156,7 @@ SKIP_FUNCTION_MACROS = YES # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. -# Note: Each tag file must have an unique name (where the name does NOT include +# Note: Each tag file must have a unique name (where the name does NOT include # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. @@ -1970,20 +2168,21 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. @@ -2000,7 +2199,7 @@ PERL_PATH = /usr/bin/perl # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # NO turns the diagrams off. Note that this option also works with HAVE_DOT # disabled, but it is recommended to install and use dot, since it yields more @@ -2025,7 +2224,7 @@ MSCGEN_PATH = DIA_PATH = -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2050,7 +2249,7 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font n the dot files that doxygen +# When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make # sure dot is able to find the font, which can be done by putting it in a # standard location or by setting the DOTFONTPATH environment variable or by @@ -2098,7 +2297,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2150,7 +2349,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2161,7 +2361,8 @@ CALL_GRAPH = NO # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2184,11 +2385,15 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2231,6 +2436,24 @@ MSCFILE_DIRS = DIAFILE_DIRS = +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = $(PLANTUML_JAR_PATH) + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized @@ -2241,7 +2464,7 @@ DIAFILE_DIRS = # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_GRAPH_MAX_NODES = 50 +DOT_GRAPH_MAX_NODES = 500 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the @@ -2267,7 +2490,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2284,12 +2507,9 @@ DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot # files that are used to generate the various graphs. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES - -# PlantUML executable -PLANTUML_JAR_PATH = $(PLANTUML_JAR_PATH) diff --git a/Logparser/logparser.css b/Logparser/logparser.css new file mode 100644 index 000000000..ff508f060 --- /dev/null +++ b/Logparser/logparser.css @@ -0,0 +1,60 @@ +@charset "UTF-8";/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:Glyphicons Halflings;src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:Glyphicons Halflings;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:22px;margin-bottom:22px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:22px;margin-bottom:11px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:11px;margin-bottom:11px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:41px}h2,.h2{font-size:34px}h3,.h3{font-size:28px}h4,.h4{font-size:20px}h5,.h5{font-size:16px}h6,.h6{font-size:14px}p{margin:0 0 11px}.lead{margin-bottom:22px;font-size:18px;font-weight:300;line-height:1.4}@media(min-width:768px){.lead{font-size:24px}}small,.small{font-size:87%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:10px;margin:44px 0 22px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:11px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:22px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media(min-width:100px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:11px 22px;margin:0 0 22px;font-size:20px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:22px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,Courier New,monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10.5px;margin:0 0 11px;font-size:15px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media(min-width:768px){.container{width:750px}}@media(min-width:992px){.container{width:970px}}@media(min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media(min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media(min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media(min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:22px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:16.5px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:22px;font-size:24px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:16px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:36px;padding:6px 12px;font-size:16px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:36px;line-height:1.42857143 \0}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:33px;line-height:1.5 \0}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:49px;line-height:1.33 \0}_:-ms-fullscreen,:root input[type=date],:root input[type=time],:root input[type=datetime-local],:root input[type=month]{line-height:1.42857143}_:-ms-fullscreen.input-sm,:root input[type=date].input-sm,:root input[type=time].input-sm,:root input[type=datetime-local].input-sm,:root input[type=month].input-sm{line-height:1.5}_:-ms-fullscreen.input-lg,:root input[type=date].input-lg,:root input[type=time].input-lg,:root input[type=datetime-local].input-lg,:root input[type=month].input-lg{line-height:1.33}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:22px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm,.form-group-sm .form-control{height:33px;padding:5px 10px;font-size:14px;line-height:1.5;border-radius:3px}select.input-sm,select.form-group-sm .form-control{height:33px;line-height:33px}textarea.input-sm,textarea.form-group-sm .form-control,select[multiple].input-sm,select[multiple].form-group-sm .form-control{height:auto}.input-lg,.form-group-lg .form-control{height:49px;padding:10px 16px;font-size:20px;line-height:1.33;border-radius:6px}select.input-lg,select.form-group-lg .form-control{height:49px;line-height:49px}textarea.input-lg,textarea.form-group-lg .form-control,select[multiple].input-lg,select[multiple].form-group-lg .form-control{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:45px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:36px;height:36px;line-height:36px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:49px;height:49px;line-height:49px}.input-sm+.form-control-feedback{width:33px;height:33px;line-height:33px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:27px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media(min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media(min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media(min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media(min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:16px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:400;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:20px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:14px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:14px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none;visibility:hidden}.collapse.in{display:block;visibility:visible}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:16px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:10px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#337ab7}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:14px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media(min-width:100px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:49px;padding:10px 16px;font-size:20px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:49px;line-height:49px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:33px;padding:5px 10px;font-size:14px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:33px;line-height:33px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:16px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:14px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:20px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:10px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media(min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media(min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media(min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media(min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none;visibility:hidden}.tab-content>.active{display:block;visibility:visible}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:60px;margin-bottom:22px;border:1px solid transparent}@media(min-width:100px){.navbar{border-radius:4px}}@media(min-width:100px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media(min-width:100px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;visibility:visible!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media(max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media(min-width:100px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media(min-width:100px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media(min-width:100px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:19px 15px;font-size:20px;line-height:22px;height:60px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media(min-width:100px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:13px;margin-bottom:13px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media(min-width:100px){.navbar-toggle{display:none}}.navbar-nav{margin:9.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:22px}@media(max-width:99px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:22px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media(min-width:100px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:19px;padding-bottom:19px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:12px;margin-bottom:12px}@media(min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media(max-width:99px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media(min-width:100px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:12px;margin-bottom:12px}.navbar-btn.btn-sm{margin-top:13.5px;margin-bottom:13.5px}.navbar-btn.btn-xs{margin-top:19px;margin-bottom:19px}.navbar-text{margin-top:19px;margin-bottom:19px}@media(min-width:100px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media(min-width:100px){.navbar-left{float:left}.navbar-right{float:right;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media(max-width:99px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media(max-width:99px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:22px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:22px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#337ab7;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:20px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:14px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:22px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:14px;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:24px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:72px}}.thumbnail{display:block;padding:4px;margin-bottom:22px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:22px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:22px;margin-bottom:22px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:14px;line-height:22px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;color:#555;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:22px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:18px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:22px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:24px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media(min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media(min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;visibility:visible;font-size:14px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-size:16px;font-weight:400;line-height:1.42857143;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:16px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:transform .6s ease-in-out;backface-visibility:hidden;perspective:1000}.carousel-inner>.item.next,.carousel-inner>.item.active.right{transform:translate3d(100%,0,0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{transform:translate3d(-100%,0,0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{transform:translate3d(0,0,0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:transparent}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media(max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media(max-width:767px){.visible-xs-block{display:block!important}}@media(max-width:767px){.visible-xs-inline{display:inline!important}}@media(max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media(min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media(min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media(min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media(min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media(min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media(min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media(min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media(min-width:1200px){.visible-lg-block{display:block!important}}@media(min-width:1200px){.visible-lg-inline{display:inline!important}}@media(min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media(max-width:767px){.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}@keyframes popIn{0%{transform:scale(1,1)}25%{transform:scale(1.2,1)}50%{transform:scale(1.4,1)}100%{transform:scale(1,1)}}@keyframes popOut{0%{transform:scale(1,1)}25%{transform:scale(1.2,1)}50%{transform:scale(1.4,1)}100%{transform:scale(1,1)}}@keyframes splashIn{0%{transform:scale(1);opacity:1}25%{transform:scale(1.1);opacity:.8}50%{transform:scale(1.1);opacity:.9}100%{transform:scale(1);opacity:1}}@keyframes splashOut{0%{transform:scale(1);opacity:1}25%{transform:scale(1);opacity:.8}50%{transform:scale(1);opacity:.9}100%{transform:scale(.5);opacity:1}}.checkbox-toggle{position:relative}.checkbox-toggle input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-toggle input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-toggle input+span{cursor:pointer;user-select:none}.checkbox-toggle input+span:before{position:absolute;left:0;display:inline-block}.checkbox-toggle input+span>h4{display:inline}.form-horizontal [class^=checkbox] input+span:after{top:7px}.checkbox-slider{position:relative}.checkbox-slider input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider input+span{cursor:pointer;user-select:none}.checkbox-slider input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider input+span>h4{display:inline}.checkbox-slider input+span{padding-left:40px}.checkbox-slider input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider input:checked+span:after{margin-left:20px;content:""}.checkbox-slider input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--default{position:relative}.checkbox-slider--default input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider--default input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider--default input+span{cursor:pointer;user-select:none}.checkbox-slider--default input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider--default input+span>h4{display:inline}.checkbox-slider--default input+span{padding-left:40px}.checkbox-slider--default input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider--default input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider--default input:checked+span:after{margin-left:20px;content:""}.checkbox-slider--default input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--default input+span:after{background:#FFF;border:solid transparent 1px;background-clip:content-box}.checkbox-slider--default input:checked+span:after{background:#5cb85c;border:solid transparent 1px;background-clip:content-box}.checkbox-slider--a-rounded{position:relative}.checkbox-slider--a-rounded input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider--a-rounded input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider--a-rounded input+span{cursor:pointer;user-select:none}.checkbox-slider--a-rounded input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider--a-rounded input+span>h4{display:inline}.checkbox-slider--a-rounded input+span{padding-left:40px}.checkbox-slider--a-rounded input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider--a-rounded input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider--a-rounded input:checked+span:after{margin-left:20px;content:""}.checkbox-slider--a-rounded input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--a-rounded input+span:after{background:#FFF;border:solid transparent 1px;background-clip:content-box}.checkbox-slider--a-rounded input:checked+span:after{background:#5cb85c;border:solid transparent 1px;background-clip:content-box}.checkbox-slider--a-rounded input+span:after,.checkbox-slider--a-rounded input+span:before{border-radius:4px}.checkbox-slider--a-rounded input+span:after,.checkbox-slider--a-rounded input:checked+span:after{border:solid transparent 2px;background-clip:content-box}.checkbox-slider--a-rounded.checkbox-slider-sm input+span:before,.checkbox-slider--a-rounded.checkbox-slider-sm input+span:after{border-radius:3px}.checkbox-slider--a-rounded.checkbox-slider-md input+span:before,.checkbox-slider--a-rounded.checkbox-slider-md input+span:after{border-radius:4px}.checkbox-slider--a-rounded.checkbox-slider-lg input+span:before,.checkbox-slider--a-rounded.checkbox-slider-lg input+span:after{border-radius:6px}.checkbox-slider--a{position:relative}.checkbox-slider--a input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider--a input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider--a input+span{cursor:pointer;user-select:none}.checkbox-slider--a input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider--a input+span>h4{display:inline}.checkbox-slider--a input+span{padding-left:40px}.checkbox-slider--a input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider--a input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider--a input:checked+span:after{margin-left:20px;content:""}.checkbox-slider--a input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--a input+span{padding-left:60px}.checkbox-slider--a input+span:before{content:"";width:60px}.checkbox-slider--a input+span:after{width:40px;font-size:10px;color:#000;content:"Off";border:solid transparent 1px;background-clip:content-box}.checkbox-slider--a input:checked+span:after{content:"On";color:#fff;background:#5cb85c;border:solid transparent 1px;background-clip:content-box}.checkbox-slider--a.checkbox-slider-sm input+span{padding-left:30px}.checkbox-slider--a.checkbox-slider-sm input+span:before{width:30px}.checkbox-slider--a.checkbox-slider-sm input+span:after{width:20px;font-size:5px}.checkbox-slider--a.checkbox-slider-sm input:checked+span:after{margin-left:10px}.checkbox-slider--a.checkbox-slider-md input+span{padding-left:90px}.checkbox-slider--a.checkbox-slider-md input+span:before{width:90px}.checkbox-slider--a.checkbox-slider-md input+span:after{width:60px;font-size:15px}.checkbox-slider--a.checkbox-slider-md input:checked+span:after{margin-left:30px}.checkbox-slider--a.checkbox-slider-lg input+span{padding-left:120px}.checkbox-slider--a.checkbox-slider-lg input+span:before{width:120px}.checkbox-slider--a.checkbox-slider-lg input+span:after{width:80px;font-size:20px}.checkbox-slider--a.checkbox-slider-lg input:checked+span:after{margin-left:40px}.checkbox-slider--b{position:relative}.checkbox-slider--b input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider--b input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider--b input+span{cursor:pointer;user-select:none}.checkbox-slider--b input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider--b input+span>h4{display:inline}.checkbox-slider--b input+span{padding-left:40px}.checkbox-slider--b input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider--b input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider--b input:checked+span:after{margin-left:20px;content:""}.checkbox-slider--b input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--b input+span{padding-left:40px}.checkbox-slider--b input+span:before{border-radius:20px;width:40px}.checkbox-slider--b input+span:after{background:#FFF;content:"";width:20px;border:solid transparent 2px;background-clip:padding-box;border-radius:20px}.checkbox-slider--b input:not(:checked)+span:after{animation:popOut ease-in .3s normal}.checkbox-slider--b input:checked+span:after{content:"";margin-left:20px;border:solid transparent 2px;background-clip:padding-box;animation:popIn ease-in .3s normal}.checkbox-slider--b input:checked+span:before{background:#5cb85c}.checkbox-slider--b.checkbox-slider-md input+span:before{border-radius:30px}.checkbox-slider--b.checkbox-slider-md input+span:after{border-radius:30px}.checkbox-slider--b.checkbox-slider-lg input+span:before{border-radius:40px}.checkbox-slider--b.checkbox-slider-lg input+span:after{border-radius:40px}.checkbox-slider--b-flat{position:relative}.checkbox-slider--b-flat input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider--b-flat input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider--b-flat input+span{cursor:pointer;user-select:none}.checkbox-slider--b-flat input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider--b-flat input+span>h4{display:inline}.checkbox-slider--b-flat input+span{padding-left:40px}.checkbox-slider--b-flat input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider--b-flat input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider--b-flat input:checked+span:after{margin-left:20px;content:""}.checkbox-slider--b-flat input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--b-flat input+span{padding-left:40px}.checkbox-slider--b-flat input+span:before{border-radius:20px;width:40px}.checkbox-slider--b-flat input+span:after{background:#FFF;content:"";width:20px;border:solid transparent 2px;background-clip:padding-box;border-radius:20px}.checkbox-slider--b-flat input:not(:checked)+span:after{animation:popOut ease-in .3s normal}.checkbox-slider--b-flat input:checked+span:after{content:"";margin-left:20px;border:solid transparent 2px;background-clip:padding-box;animation:popIn ease-in .3s normal}.checkbox-slider--b-flat input:checked+span:before{background:#5cb85c}.checkbox-slider--b-flat.checkbox-slider-md input+span:before{border-radius:30px}.checkbox-slider--b-flat.checkbox-slider-md input+span:after{border-radius:30px}.checkbox-slider--b-flat.checkbox-slider-lg input+span:before{border-radius:40px}.checkbox-slider--b-flat.checkbox-slider-lg input+span:after{border-radius:40px}.checkbox-slider--b-flat input+span:before{box-shadow:none}.checkbox-slider--c{position:relative}.checkbox-slider--c input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider--c input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider--c input+span{cursor:pointer;user-select:none}.checkbox-slider--c input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider--c input+span>h4{display:inline}.checkbox-slider--c input+span{padding-left:40px}.checkbox-slider--c input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider--c input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider--c input:checked+span:after{margin-left:20px;content:""}.checkbox-slider--c input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--c input+span{padding-left:40px}.checkbox-slider--c input+span:before{height:2px!important;top:10px;box-shadow:none;width:40px;background:#555}.checkbox-slider--c input+span:after{box-shadow:none;width:20px;border:solid #555 2px;border-radius:20px}.checkbox-slider--c input:checked+span:after{background:#5cb85c;margin-left:20px;border:solid #5cb85c 2px;animation:splashIn ease-in .3s normal}.checkbox-slider--c input:checked+span:before{background:#5cb85c}.checkbox-slider--c.checkbox-slider-sm input+span:before{top:4px}.checkbox-slider--c.checkbox-slider-md input+span:before{top:14px}.checkbox-slider--c.checkbox-slider-md input+span:after{width:30px;border-radius:30px}.checkbox-slider--c.checkbox-slider-lg input+span:before{top:19px}.checkbox-slider--c.checkbox-slider-lg input+span:after{width:40px;border-radius:40px}.form-horizontal [class*=checkbox-slider--c].checkbox-slider-sm input+span:before{top:10px}.form-horizontal [class*=checkbox-slider--c].checkbox-slider-md input+span:before{top:20px}.form-horizontal [class*=checkbox-slider--c].checkbox-slider-lg input+span:before{top:25px}.checkbox-slider--c-weight{position:relative}.checkbox-slider--c-weight input[type=checkbox]{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:0;height:0;margin:0;cursor:pointer;opacity:0}.checkbox-slider--c-weight input[type=checkbox]:focus+*:before{outline:solid #66afe9 2px}.checkbox-slider--c-weight input+span{cursor:pointer;user-select:none}.checkbox-slider--c-weight input+span:before{position:absolute;left:0;display:inline-block}.checkbox-slider--c-weight input+span>h4{display:inline}.checkbox-slider--c-weight input+span{padding-left:40px}.checkbox-slider--c-weight input+span:before{content:"";height:20px;width:40px;background:rgba(100,100,100,.2);box-shadow:inset 0 0 5px rgba(0,0,0,.8);transition:background .2s ease-out}.checkbox-slider--c-weight input+span:after{width:20px;height:20px;position:absolute;left:0;top:0;display:block;background:#FFF;transition:margin-left .1s ease-in-out;text-align:center;font-weight:700;content:""}.checkbox-slider--c-weight input:checked+span:after{margin-left:20px;content:""}.checkbox-slider--c-weight input:checked+span:before{transition:background .2s ease-in}.checkbox-slider--c-weight input+span{padding-left:40px}.checkbox-slider--c-weight input+span:before{height:2px!important;top:10px;box-shadow:none;width:40px;background:#555}.checkbox-slider--c-weight input+span:after{box-shadow:none;width:20px;border:solid #555 2px;border-radius:20px}.checkbox-slider--c-weight input:checked+span:after{background:#5cb85c;margin-left:20px;border:solid #5cb85c 2px;animation:splashIn ease-in .3s normal}.checkbox-slider--c-weight input:checked+span:before{background:#5cb85c}.checkbox-slider--c-weight.checkbox-slider-sm input+span:before{top:4px}.checkbox-slider--c-weight.checkbox-slider-md input+span:before{top:14px}.checkbox-slider--c-weight.checkbox-slider-md input+span:after{width:30px;border-radius:30px}.checkbox-slider--c-weight.checkbox-slider-lg input+span:before{top:19px}.checkbox-slider--c-weight.checkbox-slider-lg input+span:after{width:40px;border-radius:40px}.checkbox-slider--c-weight input+span:before{height:1px!important}.checkbox-slider--c-weight input:checked+span:before{height:2px!important}.checkbox-slider--c-weight input:not(:checked)+span:after{transform:scale(.7);left:-6px}.checkbox-slider--default input:disabled+span:after{background:#777}.checkbox-slider--default input:disabled+span:before{box-shadow:0 0 0 black}.checkbox-slider--default input:disabled+span{color:#777}.checkbox-slider--a input:disabled+span:after{background:#777;color:#FFF}.checkbox-slider--a input:disabled+span:before{box-shadow:0 0 0 black}.checkbox-slider--a input:disabled+span{color:#777}.checkbox-slider--b input:disabled+span:after{border:solid transparent 2px;border-radius:40px}.checkbox-slider--b input:disabled+span:before{box-shadow:0 0 0 black}.checkbox-slider--b input:disabled+span{color:#777}.checkbox-slider--c input:disabled:checked+span:after{background:#777}.checkbox-slider--c input:disabled+span:after{border-color:#777}.checkbox-slider--c input:disabled+span:before{background:#777}.checkbox-slider--c input:disabled+span{color:#777}input:checked+.indicator-success{color:#5cb85c}input:checked+.indicator-info{color:#5bc0de}input:checked+.indicator-warning{color:#f0ad4e}input:checked+.indicator-danger{color:#d9534f}.checkbox-slider-sm{line-height:10px}.checkbox-slider-sm input+span{padding-left:20px}.checkbox-slider-sm input+span:before{width:20px}.checkbox-slider-sm input+span:after,.checkbox-slider-sm input+span:before{height:10px;line-height:10px}.checkbox-slider-sm input+span:after{width:10px;vertical-align:middle}.checkbox-slider-sm input:checked+span:after{margin-left:10px}.checkbox-slider-md{line-height:30px}.checkbox-slider-md input+span{padding-left:60px}.checkbox-slider-md input+span:before{width:60px}.checkbox-slider-md input+span:after,.checkbox-slider-md input+span:before{height:30px;line-height:30px}.checkbox-slider-md input+span:after{width:30px;vertical-align:middle}.checkbox-slider-md input:checked+span:after{margin-left:30px}.checkbox-slider-lg{line-height:40px}.checkbox-slider-lg input+span{padding-left:80px}.checkbox-slider-lg input+span:before{width:80px}.checkbox-slider-lg input+span:after,.checkbox-slider-lg input+span:before{height:40px;line-height:40px}.checkbox-slider-lg input+span:after{width:40px;vertical-align:middle}.checkbox-slider-lg input:checked+span:after{margin-left:40px}.checkbox-slider-info.checkbox-slider--default input:checked+span:after,.checkbox-slider-info.checkbox-slider--a input:checked+span:after,.checkbox-slider-info.checkbox-slider--a-rounded input:checked+span:after,.checkbox-slider-info.checkbox-slider--c input:checked+span:after,.checkbox-slider-info.checkbox-slider--c-weight input:checked+span:after{background:#5bc0de;background-clip:content-box}.checkbox-slider-info.checkbox-slider--c input:checked+span:after,.checkbox-slider-info.checkbox-slider--c-weight input:checked+span:after{border-color:#5bc0de}.checkbox-slider-info.checkbox-slider--b input:checked+span:before,.checkbox-slider-info.checkbox-slider--b-flat input:checked+span:before,.checkbox-slider-info.checkbox-slider--c input:checked+span:before,.checkbox-slider-info.checkbox-slider--c-weight input:checked+span:before{background:#5bc0de}.checkbox-slider-warning.checkbox-slider--default input:checked+span:after,.checkbox-slider-warning.checkbox-slider--a input:checked+span:after,.checkbox-slider-warning.checkbox-slider--a-rounded input:checked+span:after,.checkbox-slider-warning.checkbox-slider--c input:checked+span:after,.checkbox-slider-warning.checkbox-slider--c-weight input:checked+span:after{background:#f0ad4e;background-clip:content-box}.checkbox-slider-warning.checkbox-slider--c input:checked+span:after,.checkbox-slider-warning.checkbox-slider--c-weight input:checked+span:after{border-color:#f0ad4e}.checkbox-slider-warning.checkbox-slider--b input:checked+span:before,.checkbox-slider-warning.checkbox-slider--b-flat input:checked+span:before,.checkbox-slider-warning.checkbox-slider--c input:checked+span:before,.checkbox-slider-warning.checkbox-slider--c-weight input:checked+span:before{background:#f0ad4e}.checkbox-slider-danger.checkbox-slider--default input:checked+span:after,.checkbox-slider-danger.checkbox-slider--a input:checked+span:after,.checkbox-slider-danger.checkbox-slider--a-rounded input:checked+span:after,.checkbox-slider-danger.checkbox-slider--c input:checked+span:after,.checkbox-slider-danger.checkbox-slider--c-weight input:checked+span:after{background:#d9534f;background-clip:content-box}.checkbox-slider-danger.checkbox-slider--c input:checked+span:after,.checkbox-slider-danger.checkbox-slider--c-weight input:checked+span:after{border-color:#d9534f}.checkbox-slider-danger.checkbox-slider--b input:checked+span:before,.checkbox-slider-danger.checkbox-slider--b-flat input:checked+span:before,.checkbox-slider-danger.checkbox-slider--c input:checked+span:before,.checkbox-slider-danger.checkbox-slider--c-weight input:checked+span:before{background:#d9534f}.btn-social{position:relative;padding-left:46px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:34px;line-height:36px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,.2)}.btn-social.btn-lg{padding-left:64px}.btn-social.btn-lg :first-child{line-height:48px;width:48px;font-size:1.8em}.btn-social.btn-sm{padding-left:41px}.btn-social.btn-sm :first-child{line-height:31px;width:31px;font-size:1.4em}.btn-social.btn-xs{padding-left:32px}.btn-social.btn-xs :first-child{line-height:22px;width:22px;font-size:1.2em}.btn-social-icon{position:relative;padding-left:46px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:36px;width:36px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:34px;line-height:36px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,.2)}.btn-social-icon.btn-lg{padding-left:64px}.btn-social-icon.btn-lg :first-child{line-height:48px;width:48px;font-size:1.8em}.btn-social-icon.btn-sm{padding-left:41px}.btn-social-icon.btn-sm :first-child{line-height:31px;width:31px;font-size:1.4em}.btn-social-icon.btn-xs{padding-left:32px}.btn-social-icon.btn-xs :first-child{line-height:22px;width:22px;font-size:1.2em}.btn-social-icon :first-child{border:none;text-align:center;width:100%!important}.btn-social-icon.btn-lg{height:48px;width:48px;padding-left:0;padding-right:0}.btn-social-icon.btn-sm{height:33px;width:33px;padding-left:0;padding-right:0}.btn-social-icon.btn-xs{height:24px;width:24px;padding-left:0;padding-right:0}.btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,.2)}.btn-adn:hover,.btn-adn:focus,.btn-adn.focus,.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,.2)}.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none}.btn-adn.disabled,.btn-adn[disabled],fieldset[disabled] .btn-adn,.btn-adn.disabled:hover,.btn-adn[disabled]:hover,fieldset[disabled] .btn-adn:hover,.btn-adn.disabled:focus,.btn-adn[disabled]:focus,fieldset[disabled] .btn-adn:focus,.btn-adn.disabled.focus,.btn-adn[disabled].focus,fieldset[disabled] .btn-adn.focus,.btn-adn.disabled:active,.btn-adn[disabled]:active,fieldset[disabled] .btn-adn:active,.btn-adn.disabled.active,.btn-adn[disabled].active,fieldset[disabled] .btn-adn.active{background-color:#d87a68;border-color:rgba(0,0,0,.2)}.btn-adn .badge{color:#d87a68;background-color:#fff}.btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,.2)}.btn-bitbucket:hover,.btn-bitbucket:focus,.btn-bitbucket.focus,.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,.2)}.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none}.btn-bitbucket.disabled,.btn-bitbucket[disabled],fieldset[disabled] .btn-bitbucket,.btn-bitbucket.disabled:hover,.btn-bitbucket[disabled]:hover,fieldset[disabled] .btn-bitbucket:hover,.btn-bitbucket.disabled:focus,.btn-bitbucket[disabled]:focus,fieldset[disabled] .btn-bitbucket:focus,.btn-bitbucket.disabled.focus,.btn-bitbucket[disabled].focus,fieldset[disabled] .btn-bitbucket.focus,.btn-bitbucket.disabled:active,.btn-bitbucket[disabled]:active,fieldset[disabled] .btn-bitbucket:active,.btn-bitbucket.disabled.active,.btn-bitbucket[disabled].active,fieldset[disabled] .btn-bitbucket.active{background-color:#205081;border-color:rgba(0,0,0,.2)}.btn-bitbucket .badge{color:#205081;background-color:#fff}.btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,.2)}.btn-dropbox:hover,.btn-dropbox:focus,.btn-dropbox.focus,.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,.2)}.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none}.btn-dropbox.disabled,.btn-dropbox[disabled],fieldset[disabled] .btn-dropbox,.btn-dropbox.disabled:hover,.btn-dropbox[disabled]:hover,fieldset[disabled] .btn-dropbox:hover,.btn-dropbox.disabled:focus,.btn-dropbox[disabled]:focus,fieldset[disabled] .btn-dropbox:focus,.btn-dropbox.disabled.focus,.btn-dropbox[disabled].focus,fieldset[disabled] .btn-dropbox.focus,.btn-dropbox.disabled:active,.btn-dropbox[disabled]:active,fieldset[disabled] .btn-dropbox:active,.btn-dropbox.disabled.active,.btn-dropbox[disabled].active,fieldset[disabled] .btn-dropbox.active{background-color:#1087dd;border-color:rgba(0,0,0,.2)}.btn-dropbox .badge{color:#1087dd;background-color:#fff}.btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,.2)}.btn-facebook:hover,.btn-facebook:focus,.btn-facebook.focus,.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,.2)}.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none}.btn-facebook.disabled,.btn-facebook[disabled],fieldset[disabled] .btn-facebook,.btn-facebook.disabled:hover,.btn-facebook[disabled]:hover,fieldset[disabled] .btn-facebook:hover,.btn-facebook.disabled:focus,.btn-facebook[disabled]:focus,fieldset[disabled] .btn-facebook:focus,.btn-facebook.disabled.focus,.btn-facebook[disabled].focus,fieldset[disabled] .btn-facebook.focus,.btn-facebook.disabled:active,.btn-facebook[disabled]:active,fieldset[disabled] .btn-facebook:active,.btn-facebook.disabled.active,.btn-facebook[disabled].active,fieldset[disabled] .btn-facebook.active{background-color:#3b5998;border-color:rgba(0,0,0,.2)}.btn-facebook .badge{color:#3b5998;background-color:#fff}.btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,.2)}.btn-flickr:hover,.btn-flickr:focus,.btn-flickr.focus,.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,.2)}.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none}.btn-flickr.disabled,.btn-flickr[disabled],fieldset[disabled] .btn-flickr,.btn-flickr.disabled:hover,.btn-flickr[disabled]:hover,fieldset[disabled] .btn-flickr:hover,.btn-flickr.disabled:focus,.btn-flickr[disabled]:focus,fieldset[disabled] .btn-flickr:focus,.btn-flickr.disabled.focus,.btn-flickr[disabled].focus,fieldset[disabled] .btn-flickr.focus,.btn-flickr.disabled:active,.btn-flickr[disabled]:active,fieldset[disabled] .btn-flickr:active,.btn-flickr.disabled.active,.btn-flickr[disabled].active,fieldset[disabled] .btn-flickr.active{background-color:#ff0084;border-color:rgba(0,0,0,.2)}.btn-flickr .badge{color:#ff0084;background-color:#fff}.btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,.2)}.btn-foursquare:hover,.btn-foursquare:focus,.btn-foursquare.focus,.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,.2)}.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none}.btn-foursquare.disabled,.btn-foursquare[disabled],fieldset[disabled] .btn-foursquare,.btn-foursquare.disabled:hover,.btn-foursquare[disabled]:hover,fieldset[disabled] .btn-foursquare:hover,.btn-foursquare.disabled:focus,.btn-foursquare[disabled]:focus,fieldset[disabled] .btn-foursquare:focus,.btn-foursquare.disabled.focus,.btn-foursquare[disabled].focus,fieldset[disabled] .btn-foursquare.focus,.btn-foursquare.disabled:active,.btn-foursquare[disabled]:active,fieldset[disabled] .btn-foursquare:active,.btn-foursquare.disabled.active,.btn-foursquare[disabled].active,fieldset[disabled] .btn-foursquare.active{background-color:#f94877;border-color:rgba(0,0,0,.2)}.btn-foursquare .badge{color:#f94877;background-color:#fff}.btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,.2)}.btn-github:hover,.btn-github:focus,.btn-github.focus,.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,.2)}.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none}.btn-github.disabled,.btn-github[disabled],fieldset[disabled] .btn-github,.btn-github.disabled:hover,.btn-github[disabled]:hover,fieldset[disabled] .btn-github:hover,.btn-github.disabled:focus,.btn-github[disabled]:focus,fieldset[disabled] .btn-github:focus,.btn-github.disabled.focus,.btn-github[disabled].focus,fieldset[disabled] .btn-github.focus,.btn-github.disabled:active,.btn-github[disabled]:active,fieldset[disabled] .btn-github:active,.btn-github.disabled.active,.btn-github[disabled].active,fieldset[disabled] .btn-github.active{background-color:#444;border-color:rgba(0,0,0,.2)}.btn-github .badge{color:#444;background-color:#fff}.btn-google-plus{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,.2)}.btn-google-plus:hover,.btn-google-plus:focus,.btn-google-plus.focus,.btn-google-plus:active,.btn-google-plus.active,.open>.dropdown-toggle.btn-google-plus{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,.2)}.btn-google-plus:active,.btn-google-plus.active,.open>.dropdown-toggle.btn-google-plus{background-image:none}.btn-google-plus.disabled,.btn-google-plus[disabled],fieldset[disabled] .btn-google-plus,.btn-google-plus.disabled:hover,.btn-google-plus[disabled]:hover,fieldset[disabled] .btn-google-plus:hover,.btn-google-plus.disabled:focus,.btn-google-plus[disabled]:focus,fieldset[disabled] .btn-google-plus:focus,.btn-google-plus.disabled.focus,.btn-google-plus[disabled].focus,fieldset[disabled] .btn-google-plus.focus,.btn-google-plus.disabled:active,.btn-google-plus[disabled]:active,fieldset[disabled] .btn-google-plus:active,.btn-google-plus.disabled.active,.btn-google-plus[disabled].active,fieldset[disabled] .btn-google-plus.active{background-color:#dd4b39;border-color:rgba(0,0,0,.2)}.btn-google-plus .badge{color:#dd4b39;background-color:#fff}.btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,.2)}.btn-instagram:hover,.btn-instagram:focus,.btn-instagram.focus,.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,.2)}.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none}.btn-instagram.disabled,.btn-instagram[disabled],fieldset[disabled] .btn-instagram,.btn-instagram.disabled:hover,.btn-instagram[disabled]:hover,fieldset[disabled] .btn-instagram:hover,.btn-instagram.disabled:focus,.btn-instagram[disabled]:focus,fieldset[disabled] .btn-instagram:focus,.btn-instagram.disabled.focus,.btn-instagram[disabled].focus,fieldset[disabled] .btn-instagram.focus,.btn-instagram.disabled:active,.btn-instagram[disabled]:active,fieldset[disabled] .btn-instagram:active,.btn-instagram.disabled.active,.btn-instagram[disabled].active,fieldset[disabled] .btn-instagram.active{background-color:#3f729b;border-color:rgba(0,0,0,.2)}.btn-instagram .badge{color:#3f729b;background-color:#fff}.btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,.2)}.btn-linkedin:hover,.btn-linkedin:focus,.btn-linkedin.focus,.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,.2)}.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none}.btn-linkedin.disabled,.btn-linkedin[disabled],fieldset[disabled] .btn-linkedin,.btn-linkedin.disabled:hover,.btn-linkedin[disabled]:hover,fieldset[disabled] .btn-linkedin:hover,.btn-linkedin.disabled:focus,.btn-linkedin[disabled]:focus,fieldset[disabled] .btn-linkedin:focus,.btn-linkedin.disabled.focus,.btn-linkedin[disabled].focus,fieldset[disabled] .btn-linkedin.focus,.btn-linkedin.disabled:active,.btn-linkedin[disabled]:active,fieldset[disabled] .btn-linkedin:active,.btn-linkedin.disabled.active,.btn-linkedin[disabled].active,fieldset[disabled] .btn-linkedin.active{background-color:#007bb6;border-color:rgba(0,0,0,.2)}.btn-linkedin .badge{color:#007bb6;background-color:#fff}.btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,.2)}.btn-microsoft:hover,.btn-microsoft:focus,.btn-microsoft.focus,.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,.2)}.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none}.btn-microsoft.disabled,.btn-microsoft[disabled],fieldset[disabled] .btn-microsoft,.btn-microsoft.disabled:hover,.btn-microsoft[disabled]:hover,fieldset[disabled] .btn-microsoft:hover,.btn-microsoft.disabled:focus,.btn-microsoft[disabled]:focus,fieldset[disabled] .btn-microsoft:focus,.btn-microsoft.disabled.focus,.btn-microsoft[disabled].focus,fieldset[disabled] .btn-microsoft.focus,.btn-microsoft.disabled:active,.btn-microsoft[disabled]:active,fieldset[disabled] .btn-microsoft:active,.btn-microsoft.disabled.active,.btn-microsoft[disabled].active,fieldset[disabled] .btn-microsoft.active{background-color:#2672ec;border-color:rgba(0,0,0,.2)}.btn-microsoft .badge{color:#2672ec;background-color:#fff}.btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,.2)}.btn-openid:hover,.btn-openid:focus,.btn-openid.focus,.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,.2)}.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none}.btn-openid.disabled,.btn-openid[disabled],fieldset[disabled] .btn-openid,.btn-openid.disabled:hover,.btn-openid[disabled]:hover,fieldset[disabled] .btn-openid:hover,.btn-openid.disabled:focus,.btn-openid[disabled]:focus,fieldset[disabled] .btn-openid:focus,.btn-openid.disabled.focus,.btn-openid[disabled].focus,fieldset[disabled] .btn-openid.focus,.btn-openid.disabled:active,.btn-openid[disabled]:active,fieldset[disabled] .btn-openid:active,.btn-openid.disabled.active,.btn-openid[disabled].active,fieldset[disabled] .btn-openid.active{background-color:#f7931e;border-color:rgba(0,0,0,.2)}.btn-openid .badge{color:#f7931e;background-color:#fff}.btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,.2)}.btn-pinterest:hover,.btn-pinterest:focus,.btn-pinterest.focus,.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,.2)}.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none}.btn-pinterest.disabled,.btn-pinterest[disabled],fieldset[disabled] .btn-pinterest,.btn-pinterest.disabled:hover,.btn-pinterest[disabled]:hover,fieldset[disabled] .btn-pinterest:hover,.btn-pinterest.disabled:focus,.btn-pinterest[disabled]:focus,fieldset[disabled] .btn-pinterest:focus,.btn-pinterest.disabled.focus,.btn-pinterest[disabled].focus,fieldset[disabled] .btn-pinterest.focus,.btn-pinterest.disabled:active,.btn-pinterest[disabled]:active,fieldset[disabled] .btn-pinterest:active,.btn-pinterest.disabled.active,.btn-pinterest[disabled].active,fieldset[disabled] .btn-pinterest.active{background-color:#cb2027;border-color:rgba(0,0,0,.2)}.btn-pinterest .badge{color:#cb2027;background-color:#fff}.btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,.2)}.btn-reddit:hover,.btn-reddit:focus,.btn-reddit.focus,.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,.2)}.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none}.btn-reddit.disabled,.btn-reddit[disabled],fieldset[disabled] .btn-reddit,.btn-reddit.disabled:hover,.btn-reddit[disabled]:hover,fieldset[disabled] .btn-reddit:hover,.btn-reddit.disabled:focus,.btn-reddit[disabled]:focus,fieldset[disabled] .btn-reddit:focus,.btn-reddit.disabled.focus,.btn-reddit[disabled].focus,fieldset[disabled] .btn-reddit.focus,.btn-reddit.disabled:active,.btn-reddit[disabled]:active,fieldset[disabled] .btn-reddit:active,.btn-reddit.disabled.active,.btn-reddit[disabled].active,fieldset[disabled] .btn-reddit.active{background-color:#eff7ff;border-color:rgba(0,0,0,.2)}.btn-reddit .badge{color:#eff7ff;background-color:#000}.btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,.2)}.btn-soundcloud:hover,.btn-soundcloud:focus,.btn-soundcloud.focus,.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,.2)}.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none}.btn-soundcloud.disabled,.btn-soundcloud[disabled],fieldset[disabled] .btn-soundcloud,.btn-soundcloud.disabled:hover,.btn-soundcloud[disabled]:hover,fieldset[disabled] .btn-soundcloud:hover,.btn-soundcloud.disabled:focus,.btn-soundcloud[disabled]:focus,fieldset[disabled] .btn-soundcloud:focus,.btn-soundcloud.disabled.focus,.btn-soundcloud[disabled].focus,fieldset[disabled] .btn-soundcloud.focus,.btn-soundcloud.disabled:active,.btn-soundcloud[disabled]:active,fieldset[disabled] .btn-soundcloud:active,.btn-soundcloud.disabled.active,.btn-soundcloud[disabled].active,fieldset[disabled] .btn-soundcloud.active{background-color:#f50;border-color:rgba(0,0,0,.2)}.btn-soundcloud .badge{color:#f50;background-color:#fff}.btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,.2)}.btn-tumblr:hover,.btn-tumblr:focus,.btn-tumblr.focus,.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,.2)}.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none}.btn-tumblr.disabled,.btn-tumblr[disabled],fieldset[disabled] .btn-tumblr,.btn-tumblr.disabled:hover,.btn-tumblr[disabled]:hover,fieldset[disabled] .btn-tumblr:hover,.btn-tumblr.disabled:focus,.btn-tumblr[disabled]:focus,fieldset[disabled] .btn-tumblr:focus,.btn-tumblr.disabled.focus,.btn-tumblr[disabled].focus,fieldset[disabled] .btn-tumblr.focus,.btn-tumblr.disabled:active,.btn-tumblr[disabled]:active,fieldset[disabled] .btn-tumblr:active,.btn-tumblr.disabled.active,.btn-tumblr[disabled].active,fieldset[disabled] .btn-tumblr.active{background-color:#2c4762;border-color:rgba(0,0,0,.2)}.btn-tumblr .badge{color:#2c4762;background-color:#fff}.btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,.2)}.btn-twitter:hover,.btn-twitter:focus,.btn-twitter.focus,.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,.2)}.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none}.btn-twitter.disabled,.btn-twitter[disabled],fieldset[disabled] .btn-twitter,.btn-twitter.disabled:hover,.btn-twitter[disabled]:hover,fieldset[disabled] .btn-twitter:hover,.btn-twitter.disabled:focus,.btn-twitter[disabled]:focus,fieldset[disabled] .btn-twitter:focus,.btn-twitter.disabled.focus,.btn-twitter[disabled].focus,fieldset[disabled] .btn-twitter.focus,.btn-twitter.disabled:active,.btn-twitter[disabled]:active,fieldset[disabled] .btn-twitter:active,.btn-twitter.disabled.active,.btn-twitter[disabled].active,fieldset[disabled] .btn-twitter.active{background-color:#55acee;border-color:rgba(0,0,0,.2)}.btn-twitter .badge{color:#55acee;background-color:#fff}.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,.2)}.btn-vimeo:hover,.btn-vimeo:focus,.btn-vimeo.focus,.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,.2)}.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none}.btn-vimeo.disabled,.btn-vimeo[disabled],fieldset[disabled] .btn-vimeo,.btn-vimeo.disabled:hover,.btn-vimeo[disabled]:hover,fieldset[disabled] .btn-vimeo:hover,.btn-vimeo.disabled:focus,.btn-vimeo[disabled]:focus,fieldset[disabled] .btn-vimeo:focus,.btn-vimeo.disabled.focus,.btn-vimeo[disabled].focus,fieldset[disabled] .btn-vimeo.focus,.btn-vimeo.disabled:active,.btn-vimeo[disabled]:active,fieldset[disabled] .btn-vimeo:active,.btn-vimeo.disabled.active,.btn-vimeo[disabled].active,fieldset[disabled] .btn-vimeo.active{background-color:#1ab7ea;border-color:rgba(0,0,0,.2)}.btn-vimeo .badge{color:#1ab7ea;background-color:#fff}.btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,.2)}.btn-vk:hover,.btn-vk:focus,.btn-vk.focus,.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,.2)}.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none}.btn-vk.disabled,.btn-vk[disabled],fieldset[disabled] .btn-vk,.btn-vk.disabled:hover,.btn-vk[disabled]:hover,fieldset[disabled] .btn-vk:hover,.btn-vk.disabled:focus,.btn-vk[disabled]:focus,fieldset[disabled] .btn-vk:focus,.btn-vk.disabled.focus,.btn-vk[disabled].focus,fieldset[disabled] .btn-vk.focus,.btn-vk.disabled:active,.btn-vk[disabled]:active,fieldset[disabled] .btn-vk:active,.btn-vk.disabled.active,.btn-vk[disabled].active,fieldset[disabled] .btn-vk.active{background-color:#587ea3;border-color:rgba(0,0,0,.2)}.btn-vk .badge{color:#587ea3;background-color:#fff}.btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,.2)}.btn-yahoo:hover,.btn-yahoo:focus,.btn-yahoo.focus,.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,.2)}.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none}.btn-yahoo.disabled,.btn-yahoo[disabled],fieldset[disabled] .btn-yahoo,.btn-yahoo.disabled:hover,.btn-yahoo[disabled]:hover,fieldset[disabled] .btn-yahoo:hover,.btn-yahoo.disabled:focus,.btn-yahoo[disabled]:focus,fieldset[disabled] .btn-yahoo:focus,.btn-yahoo.disabled.focus,.btn-yahoo[disabled].focus,fieldset[disabled] .btn-yahoo.focus,.btn-yahoo.disabled:active,.btn-yahoo[disabled]:active,fieldset[disabled] .btn-yahoo:active,.btn-yahoo.disabled.active,.btn-yahoo[disabled].active,fieldset[disabled] .btn-yahoo.active{background-color:#720e9e;border-color:rgba(0,0,0,.2)}.btn-yahoo .badge{color:#720e9e;background-color:#fff}/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(../fonts/fontawesome-webfont.eot?v=4.5.0);src:url(../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0) format('embedded-opentype'),url(../fonts/fontawesome-webfont.woff2?v=4.5.0) format('woff2'),url(../fonts/fontawesome-webfont.woff?v=4.5.0) format('woff'),url(../fonts/fontawesome-webfont.ttf?v=4.5.0) format('truetype'),url(../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular) format('svg')}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}/*! +Ionicons, v1.5.2 +Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ +https://twitter.com/benjsperry https://twitter.com/ionicframework +MIT License: https://github.com/driftyco/ionicons +*/@font-face{font-family:Ionicons;src:url(../../../fonts/ionicons.eot?v=1.5.2);src:url(../../../fonts/ionicons.eot?v=1.5.2#iefix) format("embedded-opentype"),url(../../../fonts/ionicons.ttf?v=1.5.2) format("truetype"),url(../../../fonts/ionicons.woff?v=1.5.2) format("woff"),url(../../../fonts/ionicons.svg?v=1.5.2#Ionicons) format("svg")}.ion,.ion-loading-a,.ion-loading-b,.ion-loading-c,.ion-loading-d,.ion-looping,.ion-refreshing,.ion-ios7-reloading,.ionicons,.ion-alert:before,.ion-alert-circled:before,.ion-android-add:before,.ion-android-add-contact:before,.ion-android-alarm:before,.ion-android-archive:before,.ion-android-arrow-back:before,.ion-android-arrow-down-left:before,.ion-android-arrow-down-right:before,.ion-android-arrow-forward:before,.ion-android-arrow-up-left:before,.ion-android-arrow-up-right:before,.ion-android-battery:before,.ion-android-book:before,.ion-android-calendar:before,.ion-android-call:before,.ion-android-camera:before,.ion-android-chat:before,.ion-android-checkmark:before,.ion-android-clock:before,.ion-android-close:before,.ion-android-contact:before,.ion-android-contacts:before,.ion-android-data:before,.ion-android-developer:before,.ion-android-display:before,.ion-android-download:before,.ion-android-drawer:before,.ion-android-dropdown:before,.ion-android-earth:before,.ion-android-folder:before,.ion-android-forums:before,.ion-android-friends:before,.ion-android-hand:before,.ion-android-image:before,.ion-android-inbox:before,.ion-android-information:before,.ion-android-keypad:before,.ion-android-lightbulb:before,.ion-android-locate:before,.ion-android-location:before,.ion-android-mail:before,.ion-android-microphone:before,.ion-android-mixer:before,.ion-android-more:before,.ion-android-note:before,.ion-android-playstore:before,.ion-android-printer:before,.ion-android-promotion:before,.ion-android-reminder:before,.ion-android-remove:before,.ion-android-search:before,.ion-android-send:before,.ion-android-settings:before,.ion-android-share:before,.ion-android-social:before,.ion-android-social-user:before,.ion-android-sort:before,.ion-android-stair-drawer:before,.ion-android-star:before,.ion-android-stopwatch:before,.ion-android-storage:before,.ion-android-system-back:before,.ion-android-system-home:before,.ion-android-system-windows:before,.ion-android-timer:before,.ion-android-trash:before,.ion-android-user-menu:before,.ion-android-volume:before,.ion-android-wifi:before,.ion-aperture:before,.ion-archive:before,.ion-arrow-down-a:before,.ion-arrow-down-b:before,.ion-arrow-down-c:before,.ion-arrow-expand:before,.ion-arrow-graph-down-left:before,.ion-arrow-graph-down-right:before,.ion-arrow-graph-up-left:before,.ion-arrow-graph-up-right:before,.ion-arrow-left-a:before,.ion-arrow-left-b:before,.ion-arrow-left-c:before,.ion-arrow-move:before,.ion-arrow-resize:before,.ion-arrow-return-left:before,.ion-arrow-return-right:before,.ion-arrow-right-a:before,.ion-arrow-right-b:before,.ion-arrow-right-c:before,.ion-arrow-shrink:before,.ion-arrow-swap:before,.ion-arrow-up-a:before,.ion-arrow-up-b:before,.ion-arrow-up-c:before,.ion-asterisk:before,.ion-at:before,.ion-bag:before,.ion-battery-charging:before,.ion-battery-empty:before,.ion-battery-full:before,.ion-battery-half:before,.ion-battery-low:before,.ion-beaker:before,.ion-beer:before,.ion-bluetooth:before,.ion-bonfire:before,.ion-bookmark:before,.ion-briefcase:before,.ion-bug:before,.ion-calculator:before,.ion-calendar:before,.ion-camera:before,.ion-card:before,.ion-cash:before,.ion-chatbox:before,.ion-chatbox-working:before,.ion-chatboxes:before,.ion-chatbubble:before,.ion-chatbubble-working:before,.ion-chatbubbles:before,.ion-checkmark:before,.ion-checkmark-circled:before,.ion-checkmark-round:before,.ion-chevron-down:before,.ion-chevron-left:before,.ion-chevron-right:before,.ion-chevron-up:before,.ion-clipboard:before,.ion-clock:before,.ion-close:before,.ion-close-circled:before,.ion-close-round:before,.ion-closed-captioning:before,.ion-cloud:before,.ion-code:before,.ion-code-download:before,.ion-code-working:before,.ion-coffee:before,.ion-compass:before,.ion-compose:before,.ion-connection-bars:before,.ion-contrast:before,.ion-cube:before,.ion-disc:before,.ion-document:before,.ion-document-text:before,.ion-drag:before,.ion-earth:before,.ion-edit:before,.ion-egg:before,.ion-eject:before,.ion-email:before,.ion-eye:before,.ion-eye-disabled:before,.ion-female:before,.ion-filing:before,.ion-film-marker:before,.ion-fireball:before,.ion-flag:before,.ion-flame:before,.ion-flash:before,.ion-flash-off:before,.ion-flask:before,.ion-folder:before,.ion-fork:before,.ion-fork-repo:before,.ion-forward:before,.ion-funnel:before,.ion-game-controller-a:before,.ion-game-controller-b:before,.ion-gear-a:before,.ion-gear-b:before,.ion-grid:before,.ion-hammer:before,.ion-happy:before,.ion-headphone:before,.ion-heart:before,.ion-heart-broken:before,.ion-help:before,.ion-help-buoy:before,.ion-help-circled:before,.ion-home:before,.ion-icecream:before,.ion-icon-social-google-plus:before,.ion-icon-social-google-plus-outline:before,.ion-image:before,.ion-images:before,.ion-information:before,.ion-information-circled:before,.ion-ionic:before,.ion-ios7-alarm:before,.ion-ios7-alarm-outline:before,.ion-ios7-albums:before,.ion-ios7-albums-outline:before,.ion-ios7-americanfootball:before,.ion-ios7-americanfootball-outline:before,.ion-ios7-analytics:before,.ion-ios7-analytics-outline:before,.ion-ios7-arrow-back:before,.ion-ios7-arrow-down:before,.ion-ios7-arrow-forward:before,.ion-ios7-arrow-left:before,.ion-ios7-arrow-right:before,.ion-ios7-arrow-thin-down:before,.ion-ios7-arrow-thin-left:before,.ion-ios7-arrow-thin-right:before,.ion-ios7-arrow-thin-up:before,.ion-ios7-arrow-up:before,.ion-ios7-at:before,.ion-ios7-at-outline:before,.ion-ios7-barcode:before,.ion-ios7-barcode-outline:before,.ion-ios7-baseball:before,.ion-ios7-baseball-outline:before,.ion-ios7-basketball:before,.ion-ios7-basketball-outline:before,.ion-ios7-bell:before,.ion-ios7-bell-outline:before,.ion-ios7-bolt:before,.ion-ios7-bolt-outline:before,.ion-ios7-bookmarks:before,.ion-ios7-bookmarks-outline:before,.ion-ios7-box:before,.ion-ios7-box-outline:before,.ion-ios7-briefcase:before,.ion-ios7-briefcase-outline:before,.ion-ios7-browsers:before,.ion-ios7-browsers-outline:before,.ion-ios7-calculator:before,.ion-ios7-calculator-outline:before,.ion-ios7-calendar:before,.ion-ios7-calendar-outline:before,.ion-ios7-camera:before,.ion-ios7-camera-outline:before,.ion-ios7-cart:before,.ion-ios7-cart-outline:before,.ion-ios7-chatboxes:before,.ion-ios7-chatboxes-outline:before,.ion-ios7-chatbubble:before,.ion-ios7-chatbubble-outline:before,.ion-ios7-checkmark:before,.ion-ios7-checkmark-empty:before,.ion-ios7-checkmark-outline:before,.ion-ios7-circle-filled:before,.ion-ios7-circle-outline:before,.ion-ios7-clock:before,.ion-ios7-clock-outline:before,.ion-ios7-close:before,.ion-ios7-close-empty:before,.ion-ios7-close-outline:before,.ion-ios7-cloud:before,.ion-ios7-cloud-download:before,.ion-ios7-cloud-download-outline:before,.ion-ios7-cloud-outline:before,.ion-ios7-cloud-upload:before,.ion-ios7-cloud-upload-outline:before,.ion-ios7-cloudy:before,.ion-ios7-cloudy-night:before,.ion-ios7-cloudy-night-outline:before,.ion-ios7-cloudy-outline:before,.ion-ios7-cog:before,.ion-ios7-cog-outline:before,.ion-ios7-compose:before,.ion-ios7-compose-outline:before,.ion-ios7-contact:before,.ion-ios7-contact-outline:before,.ion-ios7-copy:before,.ion-ios7-copy-outline:before,.ion-ios7-download:before,.ion-ios7-download-outline:before,.ion-ios7-drag:before,.ion-ios7-email:before,.ion-ios7-email-outline:before,.ion-ios7-expand:before,.ion-ios7-eye:before,.ion-ios7-eye-outline:before,.ion-ios7-fastforward:before,.ion-ios7-fastforward-outline:before,.ion-ios7-filing:before,.ion-ios7-filing-outline:before,.ion-ios7-film:before,.ion-ios7-film-outline:before,.ion-ios7-flag:before,.ion-ios7-flag-outline:before,.ion-ios7-folder:before,.ion-ios7-folder-outline:before,.ion-ios7-football:before,.ion-ios7-football-outline:before,.ion-ios7-gear:before,.ion-ios7-gear-outline:before,.ion-ios7-glasses:before,.ion-ios7-glasses-outline:before,.ion-ios7-heart:before,.ion-ios7-heart-outline:before,.ion-ios7-help:before,.ion-ios7-help-empty:before,.ion-ios7-help-outline:before,.ion-ios7-home:before,.ion-ios7-home-outline:before,.ion-ios7-infinite:before,.ion-ios7-infinite-outline:before,.ion-ios7-information:before,.ion-ios7-information-empty:before,.ion-ios7-information-outline:before,.ion-ios7-ionic-outline:before,.ion-ios7-keypad:before,.ion-ios7-keypad-outline:before,.ion-ios7-lightbulb:before,.ion-ios7-lightbulb-outline:before,.ion-ios7-location:before,.ion-ios7-location-outline:before,.ion-ios7-locked:before,.ion-ios7-locked-outline:before,.ion-ios7-loop:before,.ion-ios7-loop-strong:before,.ion-ios7-medkit:before,.ion-ios7-medkit-outline:before,.ion-ios7-mic:before,.ion-ios7-mic-off:before,.ion-ios7-mic-outline:before,.ion-ios7-minus:before,.ion-ios7-minus-empty:before,.ion-ios7-minus-outline:before,.ion-ios7-monitor:before,.ion-ios7-monitor-outline:before,.ion-ios7-moon:before,.ion-ios7-moon-outline:before,.ion-ios7-more:before,.ion-ios7-more-outline:before,.ion-ios7-musical-note:before,.ion-ios7-musical-notes:before,.ion-ios7-navigate:before,.ion-ios7-navigate-outline:before,.ion-ios7-paper:before,.ion-ios7-paper-outline:before,.ion-ios7-paperplane:before,.ion-ios7-paperplane-outline:before,.ion-ios7-partlysunny:before,.ion-ios7-partlysunny-outline:before,.ion-ios7-pause:before,.ion-ios7-pause-outline:before,.ion-ios7-paw:before,.ion-ios7-paw-outline:before,.ion-ios7-people:before,.ion-ios7-people-outline:before,.ion-ios7-person:before,.ion-ios7-person-outline:before,.ion-ios7-personadd:before,.ion-ios7-personadd-outline:before,.ion-ios7-photos:before,.ion-ios7-photos-outline:before,.ion-ios7-pie:before,.ion-ios7-pie-outline:before,.ion-ios7-play:before,.ion-ios7-play-outline:before,.ion-ios7-plus:before,.ion-ios7-plus-empty:before,.ion-ios7-plus-outline:before,.ion-ios7-pricetag:before,.ion-ios7-pricetag-outline:before,.ion-ios7-pricetags:before,.ion-ios7-pricetags-outline:before,.ion-ios7-printer:before,.ion-ios7-printer-outline:before,.ion-ios7-pulse:before,.ion-ios7-pulse-strong:before,.ion-ios7-rainy:before,.ion-ios7-rainy-outline:before,.ion-ios7-recording:before,.ion-ios7-recording-outline:before,.ion-ios7-redo:before,.ion-ios7-redo-outline:before,.ion-ios7-refresh:before,.ion-ios7-refresh-empty:before,.ion-ios7-refresh-outline:before,.ion-ios7-reload:before,.ion-ios7-reverse-camera:before,.ion-ios7-reverse-camera-outline:before,.ion-ios7-rewind:before,.ion-ios7-rewind-outline:before,.ion-ios7-search:before,.ion-ios7-search-strong:before,.ion-ios7-settings:before,.ion-ios7-settings-strong:before,.ion-ios7-shrink:before,.ion-ios7-skipbackward:before,.ion-ios7-skipbackward-outline:before,.ion-ios7-skipforward:before,.ion-ios7-skipforward-outline:before,.ion-ios7-snowy:before,.ion-ios7-speedometer:before,.ion-ios7-speedometer-outline:before,.ion-ios7-star:before,.ion-ios7-star-half:before,.ion-ios7-star-outline:before,.ion-ios7-stopwatch:before,.ion-ios7-stopwatch-outline:before,.ion-ios7-sunny:before,.ion-ios7-sunny-outline:before,.ion-ios7-telephone:before,.ion-ios7-telephone-outline:before,.ion-ios7-tennisball:before,.ion-ios7-tennisball-outline:before,.ion-ios7-thunderstorm:before,.ion-ios7-thunderstorm-outline:before,.ion-ios7-time:before,.ion-ios7-time-outline:before,.ion-ios7-timer:before,.ion-ios7-timer-outline:before,.ion-ios7-toggle:before,.ion-ios7-toggle-outline:before,.ion-ios7-trash:before,.ion-ios7-trash-outline:before,.ion-ios7-undo:before,.ion-ios7-undo-outline:before,.ion-ios7-unlocked:before,.ion-ios7-unlocked-outline:before,.ion-ios7-upload:before,.ion-ios7-upload-outline:before,.ion-ios7-videocam:before,.ion-ios7-videocam-outline:before,.ion-ios7-volume-high:before,.ion-ios7-volume-low:before,.ion-ios7-wineglass:before,.ion-ios7-wineglass-outline:before,.ion-ios7-world:before,.ion-ios7-world-outline:before,.ion-ipad:before,.ion-iphone:before,.ion-ipod:before,.ion-jet:before,.ion-key:before,.ion-knife:before,.ion-laptop:before,.ion-leaf:before,.ion-levels:before,.ion-lightbulb:before,.ion-link:before,.ion-load-a:before,.ion-load-b:before,.ion-load-c:before,.ion-load-d:before,.ion-location:before,.ion-locked:before,.ion-log-in:before,.ion-log-out:before,.ion-loop:before,.ion-magnet:before,.ion-male:before,.ion-man:before,.ion-map:before,.ion-medkit:before,.ion-merge:before,.ion-mic-a:before,.ion-mic-b:before,.ion-mic-c:before,.ion-minus:before,.ion-minus-circled:before,.ion-minus-round:before,.ion-model-s:before,.ion-monitor:before,.ion-more:before,.ion-mouse:before,.ion-music-note:before,.ion-navicon:before,.ion-navicon-round:before,.ion-navigate:before,.ion-network:before,.ion-no-smoking:before,.ion-nuclear:before,.ion-outlet:before,.ion-paper-airplane:before,.ion-paperclip:before,.ion-pause:before,.ion-person:before,.ion-person-add:before,.ion-person-stalker:before,.ion-pie-graph:before,.ion-pin:before,.ion-pinpoint:before,.ion-pizza:before,.ion-plane:before,.ion-planet:before,.ion-play:before,.ion-playstation:before,.ion-plus:before,.ion-plus-circled:before,.ion-plus-round:before,.ion-podium:before,.ion-pound:before,.ion-power:before,.ion-pricetag:before,.ion-pricetags:before,.ion-printer:before,.ion-pull-request:before,.ion-qr-scanner:before,.ion-quote:before,.ion-radio-waves:before,.ion-record:before,.ion-refresh:before,.ion-reply:before,.ion-reply-all:before,.ion-ribbon-a:before,.ion-ribbon-b:before,.ion-sad:before,.ion-scissors:before,.ion-search:before,.ion-settings:before,.ion-share:before,.ion-shuffle:before,.ion-skip-backward:before,.ion-skip-forward:before,.ion-social-android:before,.ion-social-android-outline:before,.ion-social-apple:before,.ion-social-apple-outline:before,.ion-social-bitcoin:before,.ion-social-bitcoin-outline:before,.ion-social-buffer:before,.ion-social-buffer-outline:before,.ion-social-designernews:before,.ion-social-designernews-outline:before,.ion-social-dribbble:before,.ion-social-dribbble-outline:before,.ion-social-dropbox:before,.ion-social-dropbox-outline:before,.ion-social-facebook:before,.ion-social-facebook-outline:before,.ion-social-foursquare:before,.ion-social-foursquare-outline:before,.ion-social-freebsd-devil:before,.ion-social-github:before,.ion-social-github-outline:before,.ion-social-google:before,.ion-social-google-outline:before,.ion-social-googleplus:before,.ion-social-googleplus-outline:before,.ion-social-hackernews:before,.ion-social-hackernews-outline:before,.ion-social-instagram:before,.ion-social-instagram-outline:before,.ion-social-linkedin:before,.ion-social-linkedin-outline:before,.ion-social-pinterest:before,.ion-social-pinterest-outline:before,.ion-social-reddit:before,.ion-social-reddit-outline:before,.ion-social-rss:before,.ion-social-rss-outline:before,.ion-social-skype:before,.ion-social-skype-outline:before,.ion-social-tumblr:before,.ion-social-tumblr-outline:before,.ion-social-tux:before,.ion-social-twitter:before,.ion-social-twitter-outline:before,.ion-social-usd:before,.ion-social-usd-outline:before,.ion-social-vimeo:before,.ion-social-vimeo-outline:before,.ion-social-windows:before,.ion-social-windows-outline:before,.ion-social-wordpress:before,.ion-social-wordpress-outline:before,.ion-social-yahoo:before,.ion-social-yahoo-outline:before,.ion-social-youtube:before,.ion-social-youtube-outline:before,.ion-speakerphone:before,.ion-speedometer:before,.ion-spoon:before,.ion-star:before,.ion-stats-bars:before,.ion-steam:before,.ion-stop:before,.ion-thermometer:before,.ion-thumbsdown:before,.ion-thumbsup:before,.ion-toggle:before,.ion-toggle-filled:before,.ion-trash-a:before,.ion-trash-b:before,.ion-trophy:before,.ion-umbrella:before,.ion-university:before,.ion-unlocked:before,.ion-upload:before,.ion-usb:before,.ion-videocamera:before,.ion-volume-high:before,.ion-volume-low:before,.ion-volume-medium:before,.ion-volume-mute:before,.ion-wand:before,.ion-waterdrop:before,.ion-wifi:before,.ion-wineglass:before,.ion-woman:before,.ion-wrench:before,.ion-xbox:before{display:inline-block;font-family:Ionicons;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;text-rendering:auto;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ion-spin,.ion-loading-a,.ion-loading-b,.ion-loading-c,.ion-loading-d,.ion-looping,.ion-refreshing,.ion-ios7-reloading{-webkit-animation:spin 1s infinite linear;-moz-animation:spin 1s infinite linear;-o-animation:spin 1s infinite linear;animation:spin 1s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.ion-loading-a{-webkit-animation-timing-function:steps(8,start);-moz-animation-timing-function:steps(8,start);animation-timing-function:steps(8,start)}.ion-alert:before{content:"\f101"}.ion-alert-circled:before{content:"\f100"}.ion-android-add:before{content:"\f2c7"}.ion-android-add-contact:before{content:"\f2c6"}.ion-android-alarm:before{content:"\f2c8"}.ion-android-archive:before{content:"\f2c9"}.ion-android-arrow-back:before{content:"\f2ca"}.ion-android-arrow-down-left:before{content:"\f2cb"}.ion-android-arrow-down-right:before{content:"\f2cc"}.ion-android-arrow-forward:before{content:"\f30f"}.ion-android-arrow-up-left:before{content:"\f2cd"}.ion-android-arrow-up-right:before{content:"\f2ce"}.ion-android-battery:before{content:"\f2cf"}.ion-android-book:before{content:"\f2d0"}.ion-android-calendar:before{content:"\f2d1"}.ion-android-call:before{content:"\f2d2"}.ion-android-camera:before{content:"\f2d3"}.ion-android-chat:before{content:"\f2d4"}.ion-android-checkmark:before{content:"\f2d5"}.ion-android-clock:before{content:"\f2d6"}.ion-android-close:before{content:"\f2d7"}.ion-android-contact:before{content:"\f2d8"}.ion-android-contacts:before{content:"\f2d9"}.ion-android-data:before{content:"\f2da"}.ion-android-developer:before{content:"\f2db"}.ion-android-display:before{content:"\f2dc"}.ion-android-download:before{content:"\f2dd"}.ion-android-drawer:before{content:"\f310"}.ion-android-dropdown:before{content:"\f2de"}.ion-android-earth:before{content:"\f2df"}.ion-android-folder:before{content:"\f2e0"}.ion-android-forums:before{content:"\f2e1"}.ion-android-friends:before{content:"\f2e2"}.ion-android-hand:before{content:"\f2e3"}.ion-android-image:before{content:"\f2e4"}.ion-android-inbox:before{content:"\f2e5"}.ion-android-information:before{content:"\f2e6"}.ion-android-keypad:before{content:"\f2e7"}.ion-android-lightbulb:before{content:"\f2e8"}.ion-android-locate:before{content:"\f2e9"}.ion-android-location:before{content:"\f2ea"}.ion-android-mail:before{content:"\f2eb"}.ion-android-microphone:before{content:"\f2ec"}.ion-android-mixer:before{content:"\f2ed"}.ion-android-more:before{content:"\f2ee"}.ion-android-note:before{content:"\f2ef"}.ion-android-playstore:before{content:"\f2f0"}.ion-android-printer:before{content:"\f2f1"}.ion-android-promotion:before{content:"\f2f2"}.ion-android-reminder:before{content:"\f2f3"}.ion-android-remove:before{content:"\f2f4"}.ion-android-search:before{content:"\f2f5"}.ion-android-send:before{content:"\f2f6"}.ion-android-settings:before{content:"\f2f7"}.ion-android-share:before{content:"\f2f8"}.ion-android-social:before{content:"\f2fa"}.ion-android-social-user:before{content:"\f2f9"}.ion-android-sort:before{content:"\f2fb"}.ion-android-stair-drawer:before{content:"\f311"}.ion-android-star:before{content:"\f2fc"}.ion-android-stopwatch:before{content:"\f2fd"}.ion-android-storage:before{content:"\f2fe"}.ion-android-system-back:before{content:"\f2ff"}.ion-android-system-home:before{content:"\f300"}.ion-android-system-windows:before{content:"\f301"}.ion-android-timer:before{content:"\f302"}.ion-android-trash:before{content:"\f303"}.ion-android-user-menu:before{content:"\f312"}.ion-android-volume:before{content:"\f304"}.ion-android-wifi:before{content:"\f305"}.ion-aperture:before{content:"\f313"}.ion-archive:before{content:"\f102"}.ion-arrow-down-a:before{content:"\f103"}.ion-arrow-down-b:before{content:"\f104"}.ion-arrow-down-c:before{content:"\f105"}.ion-arrow-expand:before{content:"\f25e"}.ion-arrow-graph-down-left:before{content:"\f25f"}.ion-arrow-graph-down-right:before{content:"\f260"}.ion-arrow-graph-up-left:before{content:"\f261"}.ion-arrow-graph-up-right:before{content:"\f262"}.ion-arrow-left-a:before{content:"\f106"}.ion-arrow-left-b:before{content:"\f107"}.ion-arrow-left-c:before{content:"\f108"}.ion-arrow-move:before{content:"\f263"}.ion-arrow-resize:before{content:"\f264"}.ion-arrow-return-left:before{content:"\f265"}.ion-arrow-return-right:before{content:"\f266"}.ion-arrow-right-a:before{content:"\f109"}.ion-arrow-right-b:before{content:"\f10a"}.ion-arrow-right-c:before{content:"\f10b"}.ion-arrow-shrink:before{content:"\f267"}.ion-arrow-swap:before{content:"\f268"}.ion-arrow-up-a:before{content:"\f10c"}.ion-arrow-up-b:before{content:"\f10d"}.ion-arrow-up-c:before{content:"\f10e"}.ion-asterisk:before{content:"\f314"}.ion-at:before{content:"\f10f"}.ion-bag:before{content:"\f110"}.ion-battery-charging:before{content:"\f111"}.ion-battery-empty:before{content:"\f112"}.ion-battery-full:before{content:"\f113"}.ion-battery-half:before{content:"\f114"}.ion-battery-low:before{content:"\f115"}.ion-beaker:before{content:"\f269"}.ion-beer:before{content:"\f26a"}.ion-bluetooth:before{content:"\f116"}.ion-bonfire:before{content:"\f315"}.ion-bookmark:before{content:"\f26b"}.ion-briefcase:before{content:"\f26c"}.ion-bug:before{content:"\f2be"}.ion-calculator:before{content:"\f26d"}.ion-calendar:before{content:"\f117"}.ion-camera:before{content:"\f118"}.ion-card:before{content:"\f119"}.ion-cash:before{content:"\f316"}.ion-chatbox:before{content:"\f11b"}.ion-chatbox-working:before{content:"\f11a"}.ion-chatboxes:before{content:"\f11c"}.ion-chatbubble:before{content:"\f11e"}.ion-chatbubble-working:before{content:"\f11d"}.ion-chatbubbles:before{content:"\f11f"}.ion-checkmark:before{content:"\f122"}.ion-checkmark-circled:before{content:"\f120"}.ion-checkmark-round:before{content:"\f121"}.ion-chevron-down:before{content:"\f123"}.ion-chevron-left:before{content:"\f124"}.ion-chevron-right:before{content:"\f125"}.ion-chevron-up:before{content:"\f126"}.ion-clipboard:before{content:"\f127"}.ion-clock:before{content:"\f26e"}.ion-close:before{content:"\f12a"}.ion-close-circled:before{content:"\f128"}.ion-close-round:before{content:"\f129"}.ion-closed-captioning:before{content:"\f317"}.ion-cloud:before{content:"\f12b"}.ion-code:before{content:"\f271"}.ion-code-download:before{content:"\f26f"}.ion-code-working:before{content:"\f270"}.ion-coffee:before{content:"\f272"}.ion-compass:before{content:"\f273"}.ion-compose:before{content:"\f12c"}.ion-connection-bars:before{content:"\f274"}.ion-contrast:before{content:"\f275"}.ion-cube:before{content:"\f318"}.ion-disc:before{content:"\f12d"}.ion-document:before{content:"\f12f"}.ion-document-text:before{content:"\f12e"}.ion-drag:before{content:"\f130"}.ion-earth:before{content:"\f276"}.ion-edit:before{content:"\f2bf"}.ion-egg:before{content:"\f277"}.ion-eject:before{content:"\f131"}.ion-email:before{content:"\f132"}.ion-eye:before{content:"\f133"}.ion-eye-disabled:before{content:"\f306"}.ion-female:before{content:"\f278"}.ion-filing:before{content:"\f134"}.ion-film-marker:before{content:"\f135"}.ion-fireball:before{content:"\f319"}.ion-flag:before{content:"\f279"}.ion-flame:before{content:"\f31a"}.ion-flash:before{content:"\f137"}.ion-flash-off:before{content:"\f136"}.ion-flask:before{content:"\f138"}.ion-folder:before{content:"\f139"}.ion-fork:before{content:"\f27a"}.ion-fork-repo:before{content:"\f2c0"}.ion-forward:before{content:"\f13a"}.ion-funnel:before{content:"\f31b"}.ion-game-controller-a:before{content:"\f13b"}.ion-game-controller-b:before{content:"\f13c"}.ion-gear-a:before{content:"\f13d"}.ion-gear-b:before{content:"\f13e"}.ion-grid:before{content:"\f13f"}.ion-hammer:before{content:"\f27b"}.ion-happy:before{content:"\f31c"}.ion-headphone:before{content:"\f140"}.ion-heart:before{content:"\f141"}.ion-heart-broken:before{content:"\f31d"}.ion-help:before{content:"\f143"}.ion-help-buoy:before{content:"\f27c"}.ion-help-circled:before{content:"\f142"}.ion-home:before{content:"\f144"}.ion-icecream:before{content:"\f27d"}.ion-icon-social-google-plus:before{content:"\f146"}.ion-icon-social-google-plus-outline:before{content:"\f145"}.ion-image:before{content:"\f147"}.ion-images:before{content:"\f148"}.ion-information:before{content:"\f14a"}.ion-information-circled:before{content:"\f149"}.ion-ionic:before{content:"\f14b"}.ion-ios7-alarm:before{content:"\f14d"}.ion-ios7-alarm-outline:before{content:"\f14c"}.ion-ios7-albums:before{content:"\f14f"}.ion-ios7-albums-outline:before{content:"\f14e"}.ion-ios7-americanfootball:before{content:"\f31f"}.ion-ios7-americanfootball-outline:before{content:"\f31e"}.ion-ios7-analytics:before{content:"\f321"}.ion-ios7-analytics-outline:before{content:"\f320"}.ion-ios7-arrow-back:before{content:"\f150"}.ion-ios7-arrow-down:before{content:"\f151"}.ion-ios7-arrow-forward:before{content:"\f152"}.ion-ios7-arrow-left:before{content:"\f153"}.ion-ios7-arrow-right:before{content:"\f154"}.ion-ios7-arrow-thin-down:before{content:"\f27e"}.ion-ios7-arrow-thin-left:before{content:"\f27f"}.ion-ios7-arrow-thin-right:before{content:"\f280"}.ion-ios7-arrow-thin-up:before{content:"\f281"}.ion-ios7-arrow-up:before{content:"\f155"}.ion-ios7-at:before{content:"\f157"}.ion-ios7-at-outline:before{content:"\f156"}.ion-ios7-barcode:before{content:"\f323"}.ion-ios7-barcode-outline:before{content:"\f322"}.ion-ios7-baseball:before{content:"\f325"}.ion-ios7-baseball-outline:before{content:"\f324"}.ion-ios7-basketball:before{content:"\f327"}.ion-ios7-basketball-outline:before{content:"\f326"}.ion-ios7-bell:before{content:"\f159"}.ion-ios7-bell-outline:before{content:"\f158"}.ion-ios7-bolt:before{content:"\f15b"}.ion-ios7-bolt-outline:before{content:"\f15a"}.ion-ios7-bookmarks:before{content:"\f15d"}.ion-ios7-bookmarks-outline:before{content:"\f15c"}.ion-ios7-box:before{content:"\f15f"}.ion-ios7-box-outline:before{content:"\f15e"}.ion-ios7-briefcase:before{content:"\f283"}.ion-ios7-briefcase-outline:before{content:"\f282"}.ion-ios7-browsers:before{content:"\f161"}.ion-ios7-browsers-outline:before{content:"\f160"}.ion-ios7-calculator:before{content:"\f285"}.ion-ios7-calculator-outline:before{content:"\f284"}.ion-ios7-calendar:before{content:"\f163"}.ion-ios7-calendar-outline:before{content:"\f162"}.ion-ios7-camera:before{content:"\f165"}.ion-ios7-camera-outline:before{content:"\f164"}.ion-ios7-cart:before{content:"\f167"}.ion-ios7-cart-outline:before{content:"\f166"}.ion-ios7-chatboxes:before{content:"\f169"}.ion-ios7-chatboxes-outline:before{content:"\f168"}.ion-ios7-chatbubble:before{content:"\f16b"}.ion-ios7-chatbubble-outline:before{content:"\f16a"}.ion-ios7-checkmark:before{content:"\f16e"}.ion-ios7-checkmark-empty:before{content:"\f16c"}.ion-ios7-checkmark-outline:before{content:"\f16d"}.ion-ios7-circle-filled:before{content:"\f16f"}.ion-ios7-circle-outline:before{content:"\f170"}.ion-ios7-clock:before{content:"\f172"}.ion-ios7-clock-outline:before{content:"\f171"}.ion-ios7-close:before{content:"\f2bc"}.ion-ios7-close-empty:before{content:"\f2bd"}.ion-ios7-close-outline:before{content:"\f2bb"}.ion-ios7-cloud:before{content:"\f178"}.ion-ios7-cloud-download:before{content:"\f174"}.ion-ios7-cloud-download-outline:before{content:"\f173"}.ion-ios7-cloud-outline:before{content:"\f175"}.ion-ios7-cloud-upload:before{content:"\f177"}.ion-ios7-cloud-upload-outline:before{content:"\f176"}.ion-ios7-cloudy:before{content:"\f17a"}.ion-ios7-cloudy-night:before{content:"\f308"}.ion-ios7-cloudy-night-outline:before{content:"\f307"}.ion-ios7-cloudy-outline:before{content:"\f179"}.ion-ios7-cog:before{content:"\f17c"}.ion-ios7-cog-outline:before{content:"\f17b"}.ion-ios7-compose:before{content:"\f17e"}.ion-ios7-compose-outline:before{content:"\f17d"}.ion-ios7-contact:before{content:"\f180"}.ion-ios7-contact-outline:before{content:"\f17f"}.ion-ios7-copy:before{content:"\f182"}.ion-ios7-copy-outline:before{content:"\f181"}.ion-ios7-download:before{content:"\f184"}.ion-ios7-download-outline:before{content:"\f183"}.ion-ios7-drag:before{content:"\f185"}.ion-ios7-email:before{content:"\f187"}.ion-ios7-email-outline:before{content:"\f186"}.ion-ios7-expand:before{content:"\f30d"}.ion-ios7-eye:before{content:"\f189"}.ion-ios7-eye-outline:before{content:"\f188"}.ion-ios7-fastforward:before{content:"\f18b"}.ion-ios7-fastforward-outline:before{content:"\f18a"}.ion-ios7-filing:before{content:"\f18d"}.ion-ios7-filing-outline:before{content:"\f18c"}.ion-ios7-film:before{content:"\f18f"}.ion-ios7-film-outline:before{content:"\f18e"}.ion-ios7-flag:before{content:"\f191"}.ion-ios7-flag-outline:before{content:"\f190"}.ion-ios7-folder:before{content:"\f193"}.ion-ios7-folder-outline:before{content:"\f192"}.ion-ios7-football:before{content:"\f329"}.ion-ios7-football-outline:before{content:"\f328"}.ion-ios7-gear:before{content:"\f195"}.ion-ios7-gear-outline:before{content:"\f194"}.ion-ios7-glasses:before{content:"\f197"}.ion-ios7-glasses-outline:before{content:"\f196"}.ion-ios7-heart:before{content:"\f199"}.ion-ios7-heart-outline:before{content:"\f198"}.ion-ios7-help:before{content:"\f19c"}.ion-ios7-help-empty:before{content:"\f19a"}.ion-ios7-help-outline:before{content:"\f19b"}.ion-ios7-home:before{content:"\f32b"}.ion-ios7-home-outline:before{content:"\f32a"}.ion-ios7-infinite:before{content:"\f19e"}.ion-ios7-infinite-outline:before{content:"\f19d"}.ion-ios7-information:before{content:"\f1a1"}.ion-ios7-information-empty:before{content:"\f19f"}.ion-ios7-information-outline:before{content:"\f1a0"}.ion-ios7-ionic-outline:before{content:"\f1a2"}.ion-ios7-keypad:before{content:"\f1a4"}.ion-ios7-keypad-outline:before{content:"\f1a3"}.ion-ios7-lightbulb:before{content:"\f287"}.ion-ios7-lightbulb-outline:before{content:"\f286"}.ion-ios7-location:before{content:"\f1a6"}.ion-ios7-location-outline:before{content:"\f1a5"}.ion-ios7-locked:before{content:"\f1a8"}.ion-ios7-locked-outline:before{content:"\f1a7"}.ion-ios7-loop:before{content:"\f32d"}.ion-ios7-loop-strong:before{content:"\f32c"}.ion-ios7-medkit:before{content:"\f289"}.ion-ios7-medkit-outline:before{content:"\f288"}.ion-ios7-mic:before{content:"\f1ab"}.ion-ios7-mic-off:before{content:"\f1a9"}.ion-ios7-mic-outline:before{content:"\f1aa"}.ion-ios7-minus:before{content:"\f1ae"}.ion-ios7-minus-empty:before{content:"\f1ac"}.ion-ios7-minus-outline:before{content:"\f1ad"}.ion-ios7-monitor:before{content:"\f1b0"}.ion-ios7-monitor-outline:before{content:"\f1af"}.ion-ios7-moon:before{content:"\f1b2"}.ion-ios7-moon-outline:before{content:"\f1b1"}.ion-ios7-more:before{content:"\f1b4"}.ion-ios7-more-outline:before{content:"\f1b3"}.ion-ios7-musical-note:before{content:"\f1b5"}.ion-ios7-musical-notes:before{content:"\f1b6"}.ion-ios7-navigate:before{content:"\f1b8"}.ion-ios7-navigate-outline:before{content:"\f1b7"}.ion-ios7-paper:before{content:"\f32f"}.ion-ios7-paper-outline:before{content:"\f32e"}.ion-ios7-paperplane:before{content:"\f1ba"}.ion-ios7-paperplane-outline:before{content:"\f1b9"}.ion-ios7-partlysunny:before{content:"\f1bc"}.ion-ios7-partlysunny-outline:before{content:"\f1bb"}.ion-ios7-pause:before{content:"\f1be"}.ion-ios7-pause-outline:before{content:"\f1bd"}.ion-ios7-paw:before{content:"\f331"}.ion-ios7-paw-outline:before{content:"\f330"}.ion-ios7-people:before{content:"\f1c0"}.ion-ios7-people-outline:before{content:"\f1bf"}.ion-ios7-person:before{content:"\f1c2"}.ion-ios7-person-outline:before{content:"\f1c1"}.ion-ios7-personadd:before{content:"\f1c4"}.ion-ios7-personadd-outline:before{content:"\f1c3"}.ion-ios7-photos:before{content:"\f1c6"}.ion-ios7-photos-outline:before{content:"\f1c5"}.ion-ios7-pie:before{content:"\f28b"}.ion-ios7-pie-outline:before{content:"\f28a"}.ion-ios7-play:before{content:"\f1c8"}.ion-ios7-play-outline:before{content:"\f1c7"}.ion-ios7-plus:before{content:"\f1cb"}.ion-ios7-plus-empty:before{content:"\f1c9"}.ion-ios7-plus-outline:before{content:"\f1ca"}.ion-ios7-pricetag:before{content:"\f28d"}.ion-ios7-pricetag-outline:before{content:"\f28c"}.ion-ios7-pricetags:before{content:"\f333"}.ion-ios7-pricetags-outline:before{content:"\f332"}.ion-ios7-printer:before{content:"\f1cd"}.ion-ios7-printer-outline:before{content:"\f1cc"}.ion-ios7-pulse:before{content:"\f335"}.ion-ios7-pulse-strong:before{content:"\f334"}.ion-ios7-rainy:before{content:"\f1cf"}.ion-ios7-rainy-outline:before{content:"\f1ce"}.ion-ios7-recording:before{content:"\f1d1"}.ion-ios7-recording-outline:before{content:"\f1d0"}.ion-ios7-redo:before{content:"\f1d3"}.ion-ios7-redo-outline:before{content:"\f1d2"}.ion-ios7-refresh:before{content:"\f1d6"}.ion-ios7-refresh-empty:before{content:"\f1d4"}.ion-ios7-refresh-outline:before{content:"\f1d5"}.ion-ios7-reload:before{content:"\f28e"}.ion-ios7-reverse-camera:before{content:"\f337"}.ion-ios7-reverse-camera-outline:before{content:"\f336"}.ion-ios7-rewind:before{content:"\f1d8"}.ion-ios7-rewind-outline:before{content:"\f1d7"}.ion-ios7-search:before{content:"\f1da"}.ion-ios7-search-strong:before{content:"\f1d9"}.ion-ios7-settings:before{content:"\f339"}.ion-ios7-settings-strong:before{content:"\f338"}.ion-ios7-shrink:before{content:"\f30e"}.ion-ios7-skipbackward:before{content:"\f1dc"}.ion-ios7-skipbackward-outline:before{content:"\f1db"}.ion-ios7-skipforward:before{content:"\f1de"}.ion-ios7-skipforward-outline:before{content:"\f1dd"}.ion-ios7-snowy:before{content:"\f309"}.ion-ios7-speedometer:before{content:"\f290"}.ion-ios7-speedometer-outline:before{content:"\f28f"}.ion-ios7-star:before{content:"\f1e0"}.ion-ios7-star-half:before{content:"\f33a"}.ion-ios7-star-outline:before{content:"\f1df"}.ion-ios7-stopwatch:before{content:"\f1e2"}.ion-ios7-stopwatch-outline:before{content:"\f1e1"}.ion-ios7-sunny:before{content:"\f1e4"}.ion-ios7-sunny-outline:before{content:"\f1e3"}.ion-ios7-telephone:before{content:"\f1e6"}.ion-ios7-telephone-outline:before{content:"\f1e5"}.ion-ios7-tennisball:before{content:"\f33c"}.ion-ios7-tennisball-outline:before{content:"\f33b"}.ion-ios7-thunderstorm:before{content:"\f1e8"}.ion-ios7-thunderstorm-outline:before{content:"\f1e7"}.ion-ios7-time:before{content:"\f292"}.ion-ios7-time-outline:before{content:"\f291"}.ion-ios7-timer:before{content:"\f1ea"}.ion-ios7-timer-outline:before{content:"\f1e9"}.ion-ios7-toggle:before{content:"\f33e"}.ion-ios7-toggle-outline:before{content:"\f33d"}.ion-ios7-trash:before{content:"\f1ec"}.ion-ios7-trash-outline:before{content:"\f1eb"}.ion-ios7-undo:before{content:"\f1ee"}.ion-ios7-undo-outline:before{content:"\f1ed"}.ion-ios7-unlocked:before{content:"\f1f0"}.ion-ios7-unlocked-outline:before{content:"\f1ef"}.ion-ios7-upload:before{content:"\f1f2"}.ion-ios7-upload-outline:before{content:"\f1f1"}.ion-ios7-videocam:before{content:"\f1f4"}.ion-ios7-videocam-outline:before{content:"\f1f3"}.ion-ios7-volume-high:before{content:"\f1f5"}.ion-ios7-volume-low:before{content:"\f1f6"}.ion-ios7-wineglass:before{content:"\f294"}.ion-ios7-wineglass-outline:before{content:"\f293"}.ion-ios7-world:before{content:"\f1f8"}.ion-ios7-world-outline:before{content:"\f1f7"}.ion-ipad:before{content:"\f1f9"}.ion-iphone:before{content:"\f1fa"}.ion-ipod:before{content:"\f1fb"}.ion-jet:before{content:"\f295"}.ion-key:before{content:"\f296"}.ion-knife:before{content:"\f297"}.ion-laptop:before{content:"\f1fc"}.ion-leaf:before{content:"\f1fd"}.ion-levels:before{content:"\f298"}.ion-lightbulb:before{content:"\f299"}.ion-link:before{content:"\f1fe"}.ion-load-a:before{content:"\f29a"}.ion-load-b:before{content:"\f29b"}.ion-load-c:before{content:"\f29c"}.ion-load-d:before{content:"\f29d"}.ion-location:before{content:"\f1ff"}.ion-locked:before{content:"\f200"}.ion-log-in:before{content:"\f29e"}.ion-log-out:before{content:"\f29f"}.ion-loop:before{content:"\f201"}.ion-magnet:before{content:"\f2a0"}.ion-male:before{content:"\f2a1"}.ion-man:before{content:"\f202"}.ion-map:before{content:"\f203"}.ion-medkit:before{content:"\f2a2"}.ion-merge:before{content:"\f33f"}.ion-mic-a:before{content:"\f204"}.ion-mic-b:before{content:"\f205"}.ion-mic-c:before{content:"\f206"}.ion-minus:before{content:"\f209"}.ion-minus-circled:before{content:"\f207"}.ion-minus-round:before{content:"\f208"}.ion-model-s:before{content:"\f2c1"}.ion-monitor:before{content:"\f20a"}.ion-more:before{content:"\f20b"}.ion-mouse:before{content:"\f340"}.ion-music-note:before{content:"\f20c"}.ion-navicon:before{content:"\f20e"}.ion-navicon-round:before{content:"\f20d"}.ion-navigate:before{content:"\f2a3"}.ion-network:before{content:"\f341"}.ion-no-smoking:before{content:"\f2c2"}.ion-nuclear:before{content:"\f2a4"}.ion-outlet:before{content:"\f342"}.ion-paper-airplane:before{content:"\f2c3"}.ion-paperclip:before{content:"\f20f"}.ion-pause:before{content:"\f210"}.ion-person:before{content:"\f213"}.ion-person-add:before{content:"\f211"}.ion-person-stalker:before{content:"\f212"}.ion-pie-graph:before{content:"\f2a5"}.ion-pin:before{content:"\f2a6"}.ion-pinpoint:before{content:"\f2a7"}.ion-pizza:before{content:"\f2a8"}.ion-plane:before{content:"\f214"}.ion-planet:before{content:"\f343"}.ion-play:before{content:"\f215"}.ion-playstation:before{content:"\f30a"}.ion-plus:before{content:"\f218"}.ion-plus-circled:before{content:"\f216"}.ion-plus-round:before{content:"\f217"}.ion-podium:before{content:"\f344"}.ion-pound:before{content:"\f219"}.ion-power:before{content:"\f2a9"}.ion-pricetag:before{content:"\f2aa"}.ion-pricetags:before{content:"\f2ab"}.ion-printer:before{content:"\f21a"}.ion-pull-request:before{content:"\f345"}.ion-qr-scanner:before{content:"\f346"}.ion-quote:before{content:"\f347"}.ion-radio-waves:before{content:"\f2ac"}.ion-record:before{content:"\f21b"}.ion-refresh:before{content:"\f21c"}.ion-reply:before{content:"\f21e"}.ion-reply-all:before{content:"\f21d"}.ion-ribbon-a:before{content:"\f348"}.ion-ribbon-b:before{content:"\f349"}.ion-sad:before{content:"\f34a"}.ion-scissors:before{content:"\f34b"}.ion-search:before{content:"\f21f"}.ion-settings:before{content:"\f2ad"}.ion-share:before{content:"\f220"}.ion-shuffle:before{content:"\f221"}.ion-skip-backward:before{content:"\f222"}.ion-skip-forward:before{content:"\f223"}.ion-social-android:before{content:"\f225"}.ion-social-android-outline:before{content:"\f224"}.ion-social-apple:before{content:"\f227"}.ion-social-apple-outline:before{content:"\f226"}.ion-social-bitcoin:before{content:"\f2af"}.ion-social-bitcoin-outline:before{content:"\f2ae"}.ion-social-buffer:before{content:"\f229"}.ion-social-buffer-outline:before{content:"\f228"}.ion-social-designernews:before{content:"\f22b"}.ion-social-designernews-outline:before{content:"\f22a"}.ion-social-dribbble:before{content:"\f22d"}.ion-social-dribbble-outline:before{content:"\f22c"}.ion-social-dropbox:before{content:"\f22f"}.ion-social-dropbox-outline:before{content:"\f22e"}.ion-social-facebook:before{content:"\f231"}.ion-social-facebook-outline:before{content:"\f230"}.ion-social-foursquare:before{content:"\f34d"}.ion-social-foursquare-outline:before{content:"\f34c"}.ion-social-freebsd-devil:before{content:"\f2c4"}.ion-social-github:before{content:"\f233"}.ion-social-github-outline:before{content:"\f232"}.ion-social-google:before{content:"\f34f"}.ion-social-google-outline:before{content:"\f34e"}.ion-social-googleplus:before{content:"\f235"}.ion-social-googleplus-outline:before{content:"\f234"}.ion-social-hackernews:before{content:"\f237"}.ion-social-hackernews-outline:before{content:"\f236"}.ion-social-instagram:before{content:"\f351"}.ion-social-instagram-outline:before{content:"\f350"}.ion-social-linkedin:before{content:"\f239"}.ion-social-linkedin-outline:before{content:"\f238"}.ion-social-pinterest:before{content:"\f2b1"}.ion-social-pinterest-outline:before{content:"\f2b0"}.ion-social-reddit:before{content:"\f23b"}.ion-social-reddit-outline:before{content:"\f23a"}.ion-social-rss:before{content:"\f23d"}.ion-social-rss-outline:before{content:"\f23c"}.ion-social-skype:before{content:"\f23f"}.ion-social-skype-outline:before{content:"\f23e"}.ion-social-tumblr:before{content:"\f241"}.ion-social-tumblr-outline:before{content:"\f240"}.ion-social-tux:before{content:"\f2c5"}.ion-social-twitter:before{content:"\f243"}.ion-social-twitter-outline:before{content:"\f242"}.ion-social-usd:before{content:"\f353"}.ion-social-usd-outline:before{content:"\f352"}.ion-social-vimeo:before{content:"\f245"}.ion-social-vimeo-outline:before{content:"\f244"}.ion-social-windows:before{content:"\f247"}.ion-social-windows-outline:before{content:"\f246"}.ion-social-wordpress:before{content:"\f249"}.ion-social-wordpress-outline:before{content:"\f248"}.ion-social-yahoo:before{content:"\f24b"}.ion-social-yahoo-outline:before{content:"\f24a"}.ion-social-youtube:before{content:"\f24d"}.ion-social-youtube-outline:before{content:"\f24c"}.ion-speakerphone:before{content:"\f2b2"}.ion-speedometer:before{content:"\f2b3"}.ion-spoon:before{content:"\f2b4"}.ion-star:before{content:"\f24e"}.ion-stats-bars:before{content:"\f2b5"}.ion-steam:before{content:"\f30b"}.ion-stop:before{content:"\f24f"}.ion-thermometer:before{content:"\f2b6"}.ion-thumbsdown:before{content:"\f250"}.ion-thumbsup:before{content:"\f251"}.ion-toggle:before{content:"\f355"}.ion-toggle-filled:before{content:"\f354"}.ion-trash-a:before{content:"\f252"}.ion-trash-b:before{content:"\f253"}.ion-trophy:before{content:"\f356"}.ion-umbrella:before{content:"\f2b7"}.ion-university:before{content:"\f357"}.ion-unlocked:before{content:"\f254"}.ion-upload:before{content:"\f255"}.ion-usb:before{content:"\f2b8"}.ion-videocamera:before{content:"\f256"}.ion-volume-high:before{content:"\f257"}.ion-volume-low:before{content:"\f258"}.ion-volume-medium:before{content:"\f259"}.ion-volume-mute:before{content:"\f25a"}.ion-wand:before{content:"\f358"}.ion-waterdrop:before{content:"\f25b"}.ion-wifi:before{content:"\f25c"}.ion-wineglass:before{content:"\f2b9"}.ion-woman:before{content:"\f25d"}.ion-wrench:before{content:"\f2ba"}.ion-xbox:before{content:"\f30c"}/*! +Animate.css - http://daneden.me/animate +Version - 3.4.0 +Licensed under the MIT license - http://opensource.org/licenses/MIT + +Copyright (c) 2015 Daniel Eden +*/.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}.animated.bounceIn,.animated.bounceOut{-webkit-animation-duration:.75s;animation-duration:.75s}.animated.flipOutX,.animated.flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounce{from,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{from,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{from,50%,to{opacity:1}25%,75%{opacity:0}}@keyframes flash{from,50%,to{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{from,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{from,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}to{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}to{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}.swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{from{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}to{-webkit-transform:none;transform:none}}@keyframes wobble{from{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}to{-webkit-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes jello{from,11.1%,to{-webkit-transform:none;transform:none}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}@keyframes jello{from,11.1%,to{-webkit-transform:none;transform:none}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}.jello{-webkit-animation-name:jello;animation-name:jello;-webkit-transform-origin:center;transform-origin:center}@-webkit-keyframes bounceIn{from,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{from,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceInDown{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInDown{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}from{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInRight{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}from{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}from{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1.000);animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}from{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes fadeIn{from{opacity:0}to{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{from{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{from{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{from{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{from{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{from{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{from{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{from{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{from{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{from{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{from{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{from{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{from{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{from{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{from{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{from{opacity:1}to{opacity:0}}@keyframes fadeOut{from{opacity:1}to{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{from{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}to{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{from{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}to{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{from{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{from{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{from{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{from{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}@keyframes flipOutX{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}@keyframes flipOutY{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}.flipOutY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY}@-webkit-keyframes lightSpeedIn{from{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}to{-webkit-transform:none;transform:none;opacity:1}}@keyframes lightSpeedIn{from{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}to{-webkit-transform:none;transform:none;opacity:1}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{from{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOut{from{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{from{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateIn{from{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{from{-webkit-transform-origin:center;transform-origin:center;opacity:1}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}@keyframes rotateOut{from{-webkit-transform-origin:center;transform-origin:center;opacity:1}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}}@keyframes rotateOutDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}@keyframes rotateOutUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{from{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{from{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}to{opacity:1;-webkit-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}@keyframes rollOut{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInDown{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInLeft{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInRight{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInUp{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes zoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp}@-webkit-keyframes slideInDown{from{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInDown{from{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInDown{-webkit-animation-name:slideInDown;animation-name:slideInDown}@-webkit-keyframes slideInLeft{from{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInLeft{from{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInLeft{-webkit-animation-name:slideInLeft;animation-name:slideInLeft}@-webkit-keyframes slideInRight{from{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInRight{from{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInRight{-webkit-animation-name:slideInRight;animation-name:slideInRight}@-webkit-keyframes slideInUp{from{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInUp{from{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInUp{-webkit-animation-name:slideInUp;animation-name:slideInUp}@-webkit-keyframes slideOutDown{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes slideOutDown{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.slideOutDown{-webkit-animation-name:slideOutDown;animation-name:slideOutDown}@-webkit-keyframes slideOutLeft{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes slideOutLeft{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.slideOutLeft{-webkit-animation-name:slideOutLeft;animation-name:slideOutLeft}@-webkit-keyframes slideOutRight{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes slideOutRight{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.slideOutRight{-webkit-animation-name:slideOutRight;animation-name:slideOutRight}@-webkit-keyframes slideOutUp{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes slideOutUp{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.slideOutUp{-webkit-animation-name:slideOutUp;animation-name:slideOutUp}.hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-number,.hljs-literal,.hljs-variable,.hljs-template-variable,.hljs-tag .hljs-attr{color:teal}.hljs-string,.hljs-doctag{color:#d14}.hljs-title,.hljs-section,.hljs-selector-id{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-type,.hljs-class .hljs-title{color:#458;font-weight:700}.hljs-tag,.hljs-name,.hljs-attribute{color:navy;font-weight:400}.hljs-regexp,.hljs-link{color:#009926}.hljs-symbol,.hljs-bullet{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1.0;-webkit-transform:rotate(3deg) translate(0,-4px);-ms-transform:rotate(3deg) translate(0,-4px);transform:rotate(3deg) translate(0,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .spinner,.nprogress-custom-parent #nprogress .bar{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.dropper{overflow:hidden}.dropper,.dropper *,.dropper :before,.dropper :after{box-sizing:border-box}.dropper-dropzone{background:#fff;border:3px dashed #ccc;border-radius:0;color:#666;cursor:pointer;font-size:14px;margin:0;padding:25px;text-align:center}.dropper.dropping .dropper-dropzone,.no-touch .dropper:hover .dropper-dropzone{background:#eee;border-color:#999;color:#333}.dropper-input{position:absolute;left:100%;opacity:0}.no-opacity .dropper-input{left:-999px}/*! jQuery UI - v1.11.4 - 2015-12-12 +* http://jqueryui.com +* Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, menu.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=3px&bgColorHeader=e9e9e9&bgTextureHeader=flat&borderColorHeader=dddddd&fcHeader=333333&iconColorHeader=444444&bgColorContent=ffffff&bgTextureContent=flat&borderColorContent=dddddd&fcContent=333333&iconColorContent=444444&bgColorDefault=f6f6f6&bgTextureDefault=flat&borderColorDefault=c5c5c5&fcDefault=454545&iconColorDefault=777777&bgColorHover=ededed&bgTextureHover=flat&borderColorHover=cccccc&fcHover=2b2b2b&iconColorHover=555555&bgColorActive=007fff&bgTextureActive=flat&borderColorActive=003eff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=fffa90&bgTextureHighlight=flat&borderColorHighlight=dad55e&fcHighlight=777620&iconColorHighlight=777620&bgColorError=fddfdf&bgTextureError=flat&borderColorError=f1a899&fcError=5f3f3f&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px +* Copyright jQuery Foundation and other contributors; Licensed MIT */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0;padding:.5em .5em .5em .7em;min-height:0;font-size:100%}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url(data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==);height:100%;filter:alpha(opacity=25);opacity:.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:700;line-height:1.5;padding:2px .4em;margin:.5em 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;cursor:pointer}.ui-selectmenu-button span.ui-icon{right:.5em;left:auto;margin-top:-8px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:.4em 2.1em .4em 1em;display:block;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:700}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:400;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #ccc;background:#ededed;font-weight:400;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#2b2b2b;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #003eff;background:#007fff;font-weight:400;color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:700}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:400}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url(images/ui-icons_444444_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_444444_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_777777_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_555555_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_ffffff_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_777620_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_cc0000_256x240.png)}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:0;padding:5px;background:#666;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px}.md-editor{display:block;border:1px solid #ddd}.md-editor .md-footer,.md-editor>.md-header{display:block;padding:6px 4px;background:#f5f5f5}.md-editor>.md-header{margin:0}.md-editor>.md-preview{background:#fff;border-top:1px dashed #ddd;border-bottom:1px dashed #ddd;min-height:10px;overflow:auto}.md-editor>textarea{font-family:Menlo,Monaco,Consolas,Courier New,monospace;font-size:14px;outline:0;margin:0;display:block;padding:0;width:100%;border:0;border-top:1px dashed #ddd;border-bottom:1px dashed #ddd;border-radius:0;box-shadow:none;background:#eee}.md-editor>textarea:focus{box-shadow:none;background:#fff}.md-editor.active{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.md-editor .md-controls{float:right;padding:3px}.md-editor .md-controls .md-control{right:5px;color:#bebebe;padding:3px 3px 3px 10px}.md-editor .md-controls .md-control:hover{color:#333}.md-editor.md-fullscreen-mode{width:100%;height:100%;position:fixed;top:0;left:0;z-index:99999;padding:60px 30px 15px;background:#fff!important;border:0!important}.md-editor.md-fullscreen-mode .md-footer{display:none}.md-editor.md-fullscreen-mode .md-input,.md-editor.md-fullscreen-mode .md-preview{margin:0 auto!important;height:100%!important;font-size:20px!important;padding:20px!important;color:#999;line-height:1.6em!important;resize:none!important;box-shadow:none!important;background:#fff!important;border:0!important}.md-editor.md-fullscreen-mode .md-preview{color:#333;overflow:auto}.md-editor.md-fullscreen-mode .md-input:focus,.md-editor.md-fullscreen-mode .md-input:hover{color:#333;background:#fff!important}.md-editor.md-fullscreen-mode .md-header{background:0 0;text-align:center;position:fixed;width:100%;top:20px}.md-editor.md-fullscreen-mode .btn-group{float:none}.md-editor.md-fullscreen-mode .btn{border:0;background:0 0;color:#b3b3b3}.md-editor.md-fullscreen-mode .btn.active,.md-editor.md-fullscreen-mode .btn:active,.md-editor.md-fullscreen-mode .btn:focus,.md-editor.md-fullscreen-mode .btn:hover{box-shadow:none;color:#333}.md-editor.md-fullscreen-mode .md-fullscreen-controls{position:absolute;top:20px;right:20px;text-align:right;z-index:1002;display:block}.md-editor.md-fullscreen-mode .md-fullscreen-controls a{color:#b3b3b3;clear:right;margin:10px;width:30px;height:30px;text-align:center}.md-editor.md-fullscreen-mode .md-fullscreen-controls a:hover{color:#333;text-decoration:none}.md-editor.md-fullscreen-mode .md-editor{height:100%!important;position:relative}.md-editor .md-fullscreen-controls{display:none}.md-nooverflow{overflow:hidden;position:fixed;width:100%}/*! + * Bootstrap-select v1.10.0 (http://silviomoreto.github.io/bootstrap-select) + * + * Copyright 2013-2016 bootstrap-select + * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) + */select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px \0}.bootstrap-select>.dropdown-toggle{width:100%;padding-right:25px;z-index:1}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:active{color:#999}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2}.has-error .bootstrap-select .dropdown-toggle,.error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{z-index:auto}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-inline .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-group .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.bootstrap-select.btn-group.disabled,.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group.disabled:focus,.bootstrap-select.btn-group>.disabled:focus{outline:none!important}.bootstrap-select.btn-group.bs-container{position:absolute}.bootstrap-select.btn-group.bs-container .dropdown-menu{z-index:1060}.bootstrap-select.btn-group .dropdown-toggle .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li.active small{color:#fff}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.btn-group.fit-width .dropdown-toggle .filter-option{position:static}.bootstrap-select.btn-group.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid white;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid white;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after{display:block}.bs-searchbox,.bs-actionsbox,.bs-donebutton{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none}/*! +(The MIT License) + +Copyright (c) 2012-2014 Marcin Warpechowski +Copyright (c) 2015 Handsoncode sp. z o.o. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/.handsontable{position:relative}.handsontable .hide{display:none}.handsontable .relative{position:relative}.handsontable.htAutoSize{visibility:hidden;left:-99000px;position:absolute;top:-99000px}.handsontable .wtHider{width:0}.handsontable .wtSpreader{position:relative;width:0;height:auto}.handsontable table,.handsontable tbody,.handsontable thead,.handsontable td,.handsontable th,.handsontable input,.handsontable textarea,.handsontable div{box-sizing:content-box;-webkit-box-sizing:content-box;-moz-box-sizing:content-box}.handsontable input,.handsontable textarea{min-height:initial}.handsontable table.htCore{border-collapse:separate;border-spacing:0;margin:0;border-width:0;table-layout:fixed;width:0;outline-width:0;max-width:none;max-height:none}.handsontable col{width:50px}.handsontable col.rowHeader{width:50px}.handsontable th,.handsontable td{border-top-width:0;border-left-width:0;border-right:1px solid #CCC;border-bottom:1px solid #CCC;height:22px;empty-cells:show;line-height:21px;padding:0 4px;background-color:#FFF;vertical-align:top;overflow:hidden;outline-width:0;white-space:pre-line}.handsontable td.htInvalid{background-color:#ff4c42!important}.handsontable td.htNoWrap{white-space:nowrap}.handsontable th:last-child{border-right:1px solid #CCC;border-bottom:1px solid #CCC}.handsontable tr:first-child th.htNoFrame,.handsontable th:first-child.htNoFrame,.handsontable th.htNoFrame{border-left-width:0;background-color:white;border-color:#FFF}.handsontable th:first-child,.handsontable td:first-of-type,.handsontable .htNoFrame+th,.handsontable .htNoFrame+td{border-left:1px solid #CCC}.handsontable.htRowHeaders thead tr th:nth-child(2){border-left:1px solid #CCC}.handsontable tr:first-child th,.handsontable tr:first-child td{border-top:1px solid #CCC}.ht_master:not(.innerBorderLeft)~.handsontable tbody tr th,.ht_master:not(.innerBorderLeft)~.handsontable:not(.ht_clone_top) thead tr th:first-child{border-right-width:0}.ht_master:not(.innerBorderTop) thead tr:last-child th,.ht_master:not(.innerBorderTop)~.handsontable thead tr:last-child th,.ht_master:not(.innerBorderTop) thead tr.lastChild th,.ht_master:not(.innerBorderTop)~.handsontable thead tr.lastChild th{border-bottom-width:0}.handsontable th{background-color:#EEE;color:#222;text-align:center;font-weight:400;white-space:nowrap}.handsontable thead th{padding:0}.handsontable th.active{background-color:#CCC}.handsontable thead th .relative{padding:2px 4px}.handsontable .manualColumnMover{position:fixed;left:0;top:0;background-color:transparent;width:5px;height:25px;z-index:999;cursor:move}.handsontable .manualRowMover{position:fixed;left:-4px;top:0;background-color:transparent;height:5px;width:50px;z-index:999;cursor:move}.handsontable .manualColumnMoverGuide,.handsontable .manualRowMoverGuide{position:fixed;left:0;top:0;background-color:#CCC;width:25px;height:25px;opacity:.7;display:none}.handsontable .manualColumnMoverGuide.active,.handsontable .manualRowMoverGuide.active{display:block}.handsontable .manualColumnMover:hover,.handsontable .manualColumnMover.active,.handsontable .manualRowMover:hover,.handsontable .manualRowMover.active{background-color:#88F}.handsontable .manualColumnResizer{position:fixed;top:0;cursor:col-resize;z-index:110;width:5px;height:25px}.handsontable .manualRowResizer{position:fixed;left:0;cursor:row-resize;z-index:110;height:5px;width:50px}.handsontable .manualColumnResizer:hover,.handsontable .manualColumnResizer.active,.handsontable .manualRowResizer:hover,.handsontable .manualRowResizer.active{background-color:#AAB}.handsontable .manualColumnResizerGuide{position:fixed;right:0;top:0;background-color:#AAB;display:none;width:0;border-right:1px dashed #777;margin-left:5px}.handsontable .manualRowResizerGuide{position:fixed;left:0;bottom:0;background-color:#AAB;display:none;height:0;border-bottom:1px dashed #777;margin-top:5px}.handsontable .manualColumnResizerGuide.active,.handsontable .manualRowResizerGuide.active{display:block}.handsontable .columnSorting{position:relative}.handsontable .columnSorting:hover{text-decoration:underline;cursor:pointer}.handsontable .columnSorting.ascending::after{content:'\25B2';color:#5f5f5f;position:absolute;right:-15px}.handsontable .columnSorting.descending::after{content:'\25BC';color:#5f5f5f;position:absolute;right:-15px}.handsontable th.beforeHiddenColumn{position:relative}.handsontable th.beforeHiddenColumn::after,.handsontable th.afterHiddenColumn::before{content:'\25C0';color:#bbb;position:absolute;right:1px;top:2px;font-size:5pt}.handsontable th.afterHiddenColumn{position:relative}.handsontable th.afterHiddenColumn::before{left:1px;top:2px;right:auto;content:'\25B6'}.handsontable td.afterHiddenColumn.firstVisible{border-left:1px solid #CCC}.handsontable thead th.hiddenHeader{display:none}.handsontable .wtBorder{position:absolute;font-size:0}.handsontable .wtBorder.hidden{display:none!important}.handsontable td.area{background:-moz-linear-gradient(top,rgba(181,209,255,.34) 0,rgba(181,209,255,.34) 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(181,209,255,.34)),color-stop(100%,rgba(181,209,255,.34)));background:-webkit-linear-gradient(top,rgba(181,209,255,.34) 0,rgba(181,209,255,.34) 100%);background:-o-linear-gradient(top,rgba(181,209,255,.34) 0,rgba(181,209,255,.34) 100%);background:-ms-linear-gradient(top,rgba(181,209,255,.34) 0,rgba(181,209,255,.34) 100%);background:linear-gradient(to bottom,rgba(181,209,255,.34) 0,rgba(181,209,255,.34) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#57b5d1ff',endColorstr='#57b5d1ff',GradientType=0);background-color:#fff}.handsontable .wtBorder.corner{font-size:0;cursor:crosshair}.handsontable .htBorder.htFillBorder{background:red;width:1px;height:1px}.handsontableInput{border:0;outline-width:0;margin:0;padding:1px 5px 0;font-family:inherit;line-height:21px;font-size:inherit;box-shadow:0 0 0 2px #5292f7 inset;resize:none;display:inline-block;color:#000;border-radius:0;background-color:#FFF}.handsontableInputHolder{position:absolute;top:0;left:0;z-index:100}.htSelectEditor{-webkit-appearance:menulist-button!important;position:absolute;width:auto}.handsontable .htDimmed{color:#777}.handsontable .htSubmenu{position:relative}.handsontable .htSubmenu :after{content:'▶';color:#777;position:absolute;right:5px}.handsontable .htLeft{text-align:left}.handsontable .htCenter{text-align:center}.handsontable .htRight{text-align:right}.handsontable .htJustify{text-align:justify}.handsontable .htTop{vertical-align:top}.handsontable .htMiddle{vertical-align:middle}.handsontable .htBottom{vertical-align:bottom}.handsontable .htPlaceholder{color:#999}.handsontable .htAutocompleteArrow{float:right;font-size:10px;color:#EEE;cursor:default;width:16px;text-align:center}.handsontable td .htAutocompleteArrow:hover{color:#777}.handsontable td.area .htAutocompleteArrow{color:#d3d3d3}.handsontable .htCheckboxRendererInput.noValue{opacity:.5}.handsontable .htCheckboxRendererLabel{cursor:pointer;display:inline-block;width:100%}.handsontable .htNumeric{text-align:right}.htCommentCell{position:relative}.htCommentCell:after{content:'';position:absolute;top:0;right:0;border-left:6px solid transparent;border-top:6px solid red}@-webkit-keyframes opacity-hide{from{opacity:1}to{opacity:0}}@keyframes opacity-hide{from{opacity:1}to{opacity:0}}@-webkit-keyframes opacity-show{from{opacity:0}to{opacity:1}}@keyframes opacity-show{from{opacity:0}to{opacity:1}}.handsontable .handsontable.ht_clone_top .wtHider{padding:0 0 5px}.handsontable .autocompleteEditor.handsontable{padding-right:17px}.handsontable .autocompleteEditor.handsontable.htMacScroll{padding-right:15px}.handsontable.listbox{margin:0}.handsontable.listbox .ht_master table{border:1px solid #ccc;border-collapse:separate;background:white}.handsontable.listbox th,.handsontable.listbox tr:first-child th,.handsontable.listbox tr:last-child th,.handsontable.listbox tr:first-child td,.handsontable.listbox td{border-color:transparent}.handsontable.listbox th,.handsontable.listbox td{white-space:nowrap;text-overflow:ellipsis}.handsontable.listbox td.htDimmed{cursor:default;color:inherit;font-style:inherit}.handsontable.listbox .wtBorder{visibility:hidden}.handsontable.listbox tr td.current,.handsontable.listbox tr:hover td{background:#eee}.ht_clone_top{z-index:101}.ht_clone_left{z-index:102}.ht_clone_top_left_corner,.ht_clone_bottom_left_corner{z-index:103}.ht_clone_debug{z-index:103}.handsontable td.htSearchResult{background:#fcedd9;color:#583707}.htBordered{border-width:1px}.htBordered.htTopBorderSolid{border-top-style:solid;border-top-color:#000}.htBordered.htRightBorderSolid{border-right-style:solid;border-right-color:#000}.htBordered.htBottomBorderSolid{border-bottom-style:solid;border-bottom-color:#000}.htBordered.htLeftBorderSolid{border-left-style:solid;border-left-color:#000}.htCommentTextArea{-moz-box-shadow:1px 1px 2px #bbb;-webkit-box-shadow:1px 1px 2px #bbb;background-color:#fffacd;border:1px solid #999;box-shadow:1px 1px 2px #bbb;font-family:Arial}.handsontable tbody tr th:nth-last-child(2){border-right:1px solid #CCC}.handsontable thead tr:nth-last-child(2) th.htGroupIndicatorContainer{border-bottom:1px solid #CCC;padding-bottom:5px}.ht_clone_top_left_corner thead tr th:nth-last-child(2){border-right:1px solid #CCC}.htCollapseButton{width:10px;height:10px;line-height:10px;text-align:center;border-radius:5px;border:1px solid #f3f3f3;-webkit-box-shadow:1px 1px 3px rgba(0,0,0,.4);box-shadow:1px 1px 3px rgba(0,0,0,.4);cursor:pointer;margin-bottom:3px;position:relative}.htCollapseButton:after{content:"";height:300%;width:1px;display:block;background:#ccc;margin-left:4px;position:absolute;bottom:10px}thead .htCollapseButton{right:5px;position:absolute;top:5px;background:#fff}thead .htCollapseButton:after{height:1px;width:700%;right:10px;top:4px}.handsontable tr th .htExpandButton{position:absolute;width:10px;height:10px;line-height:10px;text-align:center;border-radius:5px;border:1px solid #f3f3f3;-webkit-box-shadow:1px 1px 3px rgba(0,0,0,.4);box-shadow:1px 1px 3px rgba(0,0,0,.4);cursor:pointer;top:0;display:none}.handsontable thead tr th .htExpandButton{top:5px}.handsontable tr th .htExpandButton.clickable{display:block}.collapsibleIndicator{position:absolute;top:50%;transform:translate(0,-50%);right:5px;border:1px solid #a6a6a6;line-height:10px;color:#222;border-radius:10px;font-size:10px;width:10px;height:10px;cursor:pointer;-webkit-box-shadow:0 0 0 6px #eee;-moz-box-shadow:0 0 0 6px #eee;box-shadow:0 0 0 6px #eee;background:#eee}.handsontable col.hidden{width:0!important}.handsontable table tr th.lightRightBorder{border-right:1px solid #e6e6e6}.handsontable tr.hidden,.handsontable tr.hidden td,.handsontable tr.hidden th{display:none}.ht_master,.ht_clone_left,.ht_clone_top,.ht_clone_bottom{overflow:hidden}.ht_master .wtHolder{overflow:auto}.ht_clone_left .wtHolder{overflow-x:hidden;overflow-y:auto}.ht_clone_top .wtHolder,.ht_clone_bottom .wtHolder{overflow-x:auto;overflow-y:hidden}.wtDebugHidden{display:none}.wtDebugVisible{display:block;-webkit-animation-duration:.5s;-webkit-animation-name:wtFadeInFromNone;animation-duration:.5s;animation-name:wtFadeInFromNone}@keyframes wtFadeInFromNone{0%{display:none;opacity:0}1%{display:block;opacity:0}100%{display:block;opacity:1}}@-webkit-keyframes wtFadeInFromNone{0%{display:none;opacity:0}1%{display:block;opacity:0}100%{display:block;opacity:1}}.handsontable.mobile,.handsontable.mobile .wtHolder{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-overflow-scrolling:touch}.htMobileEditorContainer{display:none;position:absolute;top:0;width:70%;height:54pt;background:#f8f8f8;border-radius:20px;border:1px solid #ebebeb;z-index:999;box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-text-size-adjust:none}.topLeftSelectionHandle:not(.ht_master .topLeftSelectionHandle),.topLeftSelectionHandle-HitArea:not(.ht_master .topLeftSelectionHandle-HitArea){z-index:9999}.topLeftSelectionHandle,.topLeftSelectionHandle-HitArea,.bottomRightSelectionHandle,.bottomRightSelectionHandle-HitArea{left:-10000px;top:-10000px}.htMobileEditorContainer.active{display:block}.htMobileEditorContainer .inputs{position:absolute;right:210pt;bottom:10pt;top:10pt;left:14px;height:34pt}.htMobileEditorContainer .inputs textarea{font-size:13pt;border:1px solid #a1a1a1;-webkit-appearance:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;position:absolute;left:14px;right:14px;top:0;bottom:0;padding:7pt}.htMobileEditorContainer .cellPointer{position:absolute;top:-13pt;height:0;width:0;left:30px;border-left:13pt solid transparent;border-right:13pt solid transparent;border-bottom:13pt solid #ebebeb}.htMobileEditorContainer .cellPointer.hidden{display:none}.htMobileEditorContainer .cellPointer:before{content:'';display:block;position:absolute;top:2px;height:0;width:0;left:-13pt;border-left:13pt solid transparent;border-right:13pt solid transparent;border-bottom:13pt solid #f8f8f8}.htMobileEditorContainer .moveHandle{position:absolute;top:10pt;left:5px;width:30px;bottom:0;cursor:move;z-index:9999}.htMobileEditorContainer .moveHandle:after{content:"..\a..\a..\a..";white-space:pre;line-height:10px;font-size:20pt;display:inline-block;margin-top:-8px;color:#ebebeb}.htMobileEditorContainer .positionControls{width:205pt;position:absolute;right:5pt;top:0;bottom:0}.htMobileEditorContainer .positionControls>div{width:50pt;height:100%;float:left}.htMobileEditorContainer .positionControls>div:after{content:" ";display:block;width:15pt;height:15pt;text-align:center;line-height:50pt}.htMobileEditorContainer .leftButton:after,.htMobileEditorContainer .rightButton:after,.htMobileEditorContainer .upButton:after,.htMobileEditorContainer .downButton:after{transform-origin:5pt 5pt;-webkit-transform-origin:5pt 5pt;margin:21pt 0 0 21pt}.htMobileEditorContainer .leftButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(-45deg)}.htMobileEditorContainer .leftButton:active:after{border-color:#cfcfcf}.htMobileEditorContainer .rightButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(135deg)}.htMobileEditorContainer .rightButton:active:after{border-color:#cfcfcf}.htMobileEditorContainer .upButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(45deg)}.htMobileEditorContainer .upButton:active:after{border-color:#cfcfcf}.htMobileEditorContainer .downButton:after{border-top:2px solid #288ffe;border-left:2px solid #288ffe;-webkit-transform:rotate(225deg)}.htMobileEditorContainer .downButton:active:after{border-color:#cfcfcf}.handsontable.hide-tween{-webkit-animation:opacity-hide .3s;animation:opacity-hide .3s;animation-fill-mode:forwards;-webkit-animation-fill-mode:forwards}.handsontable.show-tween{-webkit-animation:opacity-show .3s;animation:opacity-show .3s;animation-fill-mode:forwards;-webkit-animation-fill-mode:forwards}/*! + * Handsontable ContextMenu + */.htContextMenu{display:none;position:absolute;z-index:1060}.htContextMenu .ht_clone_top,.htContextMenu .ht_clone_left,.htContextMenu .ht_clone_corner,.htContextMenu .ht_clone_debug{display:none}.htContextMenu table.htCore{border:1px solid #bbb;border-bottom-width:2px;border-right-width:2px}.htContextMenu .wtBorder{visibility:hidden}.htContextMenu table tbody tr td{background:white;border-width:0;padding:4px 6px 0;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.htContextMenu table tbody tr td:first-child{border:0}.htContextMenu table tbody tr td.htDimmed{font-style:normal;color:#323232}.htContextMenu table tbody tr td.current,.htContextMenu table tbody tr td.zeroclipboard-is-hover{background:#e9e9e9}.htContextMenu table tbody tr td.htSeparator{border-top:1px solid #bbb;height:0;padding:0;cursor:default}.htContextMenu table tbody tr td.htDisabled{color:#999}.htContextMenu table tbody tr td.htDisabled:hover{background:#fff;color:#999;cursor:default}.htContextMenu table tbody tr.htHidden{display:none}.htContextMenu table tbody tr td .htItemWrapper{margin-left:10px;margin-right:6px}.htContextMenu table tbody tr td div span.selected{margin-top:-2px;position:absolute;left:4px}.htContextMenu .ht_master .wtHolder{overflow:hidden}/*! + * Pikaday + * Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/ + */.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid #ccc;border-bottom-color:#bbb;font-family:Helvetica Neue,Helvetica,Arial,sans-serif}.pika-single:before,.pika-single:after{content:" ";display:table}.pika-single:after{clear:both}.pika-single{zoom:1}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;display:inline;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:700;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;filter:alpha(opacity=0);opacity:0}.pika-prev,.pika-next{display:block;cursor:pointer;position:relative;outline:0;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:center center;background-repeat:no-repeat;background-size:75% 75%;opacity:.5;position:absolute;top:0}.pika-prev:hover,.pika-next:hover{opacity:1}.pika-prev,.is-rtl .pika-next{float:left;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==);left:0}.pika-next,.is-rtl .pika-prev{float:right;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=);right:0}.pika-prev.is-disabled,.pika-next.is-disabled{cursor:default;opacity:.2}.pika-select{display:inline-block;display:inline}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table th,.pika-table td{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:700;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:0;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:700}.is-selected .pika-button{color:#fff;font-weight:700;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.is-inrange .pika-button{background:#d5e9f7}.is-startrange .pika-button{color:#fff;background:#6cb31d;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.is-disabled .pika-button{pointer-events:none;cursor:default;color:#999;opacity:.3}.pika-button:hover{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:0;cursor:help}.tag-editor{list-style-type:none;padding:0 5px 0 0;margin:0;overflow:hidden;border:1px solid #eee;cursor:text;font:normal 14px sans-serif;color:#555;background:#fff}.tag-editor li{display:block;float:left;overflow:hidden;margin:3px 0;line-height:1.5}.tag-editor div{float:left;padding:0 4px}.tag-editor .placeholder{padding:0 8px;color:#bbb}.tag-editor .tag-editor-spacer{padding:0;width:8px;overflow:hidden;color:transparent;background:none}.tag-editor input{vertical-align:inherit;border:0;outline:none;padding:0;margin:0;cursor:text;font-family:inherit;font-weight:inherit;font-size:inherit;font-style:inherit;box-shadow:none;background:none}.tag-editor-hidden-src{position:absolute!important;left:-99999px}.tag-editor ::-ms-clear{display:none}.tag-editor .tag-editor-tag{padding-left:5px;color:#46799b;background:#e0eaf1;white-space:nowrap;overflow:hidden;cursor:pointer;border-radius:2px 0 0 2px}.tag-editor .tag-editor-delete{background:#e0eaf1;cursor:pointer;border-radius:0 2px 2px 0;padding-left:3px;padding-right:4px}.tag-editor .tag-editor-delete i:before{display:inline-block;line-height:20px;font-size:16px;color:#8ba7ba;content:"×"}.tag-editor .tag-editor-delete:hover i:before{color:#d65454}.tag-editor .tag-editor-tag.active+.tag-editor-delete,.tag-editor .tag-editor-tag.active+.tag-editor-delete i{visibility:hidden;cursor:text}.tag-editor .tag-editor-tag.active{background:none!important}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default;font-size:14px}.ui-front{z-index:9999}.ui-menu{list-style:none;padding:1px;margin:0;display:block;outline:none}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.4;min-height:0}.ui-widget-content{border:1px solid #bbb;background:#fff;color:#555}.ui-widget-content a{color:#46799b}.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{background:#e0eaf1}.ui-helper-hidden-accessible{display:none}/*! + * Social Share Kit v1.0.3 (http://socialsharekit.com) + * Copyright 2015 Social Share Kit / Kaspars Sprogis. + * Licensed under Creative Commons Attribution-NonCommercial 3.0 license: + * https://github.com/darklow/social-share-kit/blob/master/LICENSE + * --- + */@font-face{font-family:social-share-kit;src:url(../fonts/social-share-kit.eot);src:url(../fonts/social-share-kit.eot?#iefix) format('embedded-opentype'),url(../fonts/social-share-kit.woff) format('woff'),url(../fonts/social-share-kit.ttf) format('truetype'),url(../fonts/social-share-kit.svg#social-share-kit) format('svg')}.ssk:before{display:inline-block;font-family:social-share-kit!important;font-style:normal!important;font-weight:400!important;font-variant:normal!important;text-transform:none!important;speak:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ssk-facebook:before{content:"a";text-indent:4px;margin-right:-4px}.ssk-twitter:before{content:"b"}.ssk-google-plus:before{content:"c"}.ssk-email:before{content:"d";top:-1px;position:relative}.ssk-pinterest:before{content:"e"}.ssk-tumblr:before{content:"f"}.ssk-linkedin:before{content:"g"}.ssk-github:before{content:"h"}.ssk-vk:before{content:"i"}.ssk-instagram:before{content:"j"}.ssk-amazon:before{content:"k"}.ssk-skype:before{content:"s"}.ssk-youtube:before{content:"x"}.ssk-vimeo:before{content:"u"}.ssk-ebay:before{content:"p"}.ssk-apple:before{content:"l"}.ssk-behance:before{content:"q"}.ssk-dribble:before{content:"n"}.ssk-android:before{content:"o"}.ssk-whatsapp:before{content:"m"}.ssk-reddit:before{content:"r"}.ssk-reddit2:before{content:"t"}.ssk{background-color:#757575;color:white;display:inline-block;font-size:22px;line-height:1px;margin-right:2px;margin-bottom:2px;padding:7px;text-align:center;text-decoration:none;transition:background-color .1s;-webkit-transition:background-color .1s;-moz-transition:background-color .1s;-ms-transition:background-color .1s;-o-transition:background-color .1s}.ssk:before,.ssk .glyphicon,.ssk .fa{position:relative;font-size:22px;top:0;vertical-align:middle}.ssk.ssk-xs,.ssk-xs>.ssk{padding:4px}.ssk.ssk-xs:before,.ssk-xs>.ssk:before,.ssk.ssk-xs .glyphicon,.ssk-xs>.ssk .glyphicon,.ssk.ssk-xs .fa,.ssk-xs>.ssk .fa{font-size:15px}.ssk.ssk-sm,.ssk-sm>.ssk{padding:5px}.ssk.ssk-sm:before,.ssk-sm>.ssk:before,.ssk.ssk-sm .glyphicon,.ssk-sm>.ssk .glyphicon,.ssk.ssk-sm .fa,.ssk-sm>.ssk .fa{font-size:20px}.ssk.ssk-lg,.ssk-lg>.ssk{padding:9px}.ssk.ssk-lg:before,.ssk-lg>.ssk:before,.ssk.ssk-lg .glyphicon,.ssk-lg>.ssk .glyphicon,.ssk.ssk-lg .fa,.ssk-lg>.ssk .fa{font-size:28px}.ssk:last-child{margin-right:0}.ssk:hover{background-color:#424242}.ssk:hover,.ssk:focus{color:#fff;text-decoration:none}.ssk.ssk-round,.ssk-round .ssk{border-radius:50%}.ssk.ssk-round:before,.ssk-round .ssk:before{text-indent:0;margin-right:0}.ssk.ssk-rounded,.ssk-rounded .ssk{border-radius:15%}.ssk.ssk-icon{color:#757575;padding:2px;font-size:24px}.ssk.ssk-icon,.ssk.ssk-icon:hover{background-color:transparent}.ssk.ssk-icon:hover{color:#424242}.ssk.ssk-icon.ssk-xs,.ssk-xs>.ssk.ssk-icon{font-size:16px}.ssk.ssk-icon.ssk-sm,.ssk-sm>.ssk.ssk-icon{font-size:20px}.ssk.ssk-icon.ssk-lg,.ssk-lg>.ssk.ssk-icon{font-size:28px}.ssk.ssk-text{overflow:hidden;font-size:17px;line-height:normal;padding-right:10px}.ssk.ssk-text:before,.ssk.ssk-text .glyphicon,.ssk.ssk-text .fa{margin:-7px 10px -7px -7px;padding:7px;background-color:rgba(0,0,0,.15);vertical-align:bottom;text-indent:0}.ssk-block .ssk.ssk-text{display:block;margin-right:0;text-align:left}.ssk.ssk-text.ssk-xs,.ssk-xs>.ssk.ssk-text{font-size:12px;padding-right:6px}.ssk.ssk-text.ssk-xs:before,.ssk-xs>.ssk.ssk-text:before,.ssk.ssk-text.ssk-xs .glyphicon,.ssk-xs>.ssk.ssk-text .glyphicon,.ssk.ssk-text.ssk-xs .fa,.ssk-xs>.ssk.ssk-text .fa{margin:-4px 6px -4px -4px;padding:4px}.ssk.ssk-text.ssk-sm,.ssk-sm>.ssk.ssk-text{font-size:16px;padding-right:7px}.ssk.ssk-text.ssk-sm:before,.ssk-sm>.ssk.ssk-text:before,.ssk.ssk-text.ssk-sm .glyphicon,.ssk-sm>.ssk.ssk-text .glyphicon,.ssk.ssk-text.ssk-sm .fa,.ssk-sm>.ssk.ssk-text .fa{margin:-5px 7px -5px -5px;padding:5px}.ssk.ssk-text.ssk-lg,.ssk-lg>.ssk.ssk-text{font-size:22px;padding-right:13px}.ssk.ssk-text.ssk-lg:before,.ssk-lg>.ssk.ssk-text:before,.ssk.ssk-text.ssk-lg .glyphicon,.ssk-lg>.ssk.ssk-text .glyphicon,.ssk.ssk-text.ssk-lg .fa,.ssk-lg>.ssk.ssk-text .fa{margin:-9px 13px -9px -9px;padding:9px}.ssk-group,.ssk-sticky{font-size:0}.ssk-sticky{top:0;position:fixed;z-index:2000}.ssk-sticky .ssk{transition:padding .1s ease-out;-webkit-transition:padding .1s ease-out;-moz-transition:padding .1s ease-out;-ms-transition:padding .1s ease-out;-o-transition:padding .1s ease-out;margin:0}.ssk-sticky.ssk-left .ssk,.ssk-sticky.ssk-right .ssk{display:block;clear:both}.ssk-sticky.ssk-left.ssk-center,.ssk-sticky.ssk-right.ssk-center{top:50%;transform:translateY(-50%);-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%)}.ssk-sticky.ssk-left{left:0}.ssk-sticky.ssk-left .ssk{float:left}.ssk-sticky.ssk-left .ssk:hover{padding-left:15px}.ssk-sticky.ssk-right{right:0}.ssk-sticky.ssk-right .ssk{float:right}.ssk-sticky.ssk-right .ssk:hover{padding-right:15px}.ssk-sticky.ssk-bottom{font-size:0;top:auto;bottom:0}.ssk-sticky.ssk-bottom.ssk-center{left:50%;right:auto;transform:translateX(-50%);-webkit-transform:translateX(-50%);-moz-transform:translateX(-50%);-ms-transform:translateX(-50%);-o-transform:translateX(-50%)}.ssk-sticky.ssk-bottom .ssk{vertical-align:bottom}.ssk-sticky.ssk-bottom .ssk:hover{padding-bottom:15px}.ssk-sticky.ssk-round.ssk-xs .ssk:hover{padding:8px}.ssk-sticky.ssk-round.ssk-sm .ssk:hover{padding:9px}.ssk-sticky.ssk-round .ssk:hover{padding:11px}.ssk-sticky.ssk-round.ssk-lg .ssk:hover{padding:13px}@media(max-width:767px){.ssk-sticky{display:none}}.ssk-count{padding-top:20px}.ssk-count .ssk{position:relative}.ssk-count .ssk-num{border-radius:4px;color:#8f8f8f;background-color:rgba(50,50,50,.03);display:block;font-size:12px;left:0;line-height:20px;position:absolute;right:0;text-align:center;top:-20px}.ssk-count.ssk-sticky{padding-top:0}.ssk-count.ssk-sticky.ssk-left .ssk-num,.ssk-count.ssk-sticky.ssk-right .ssk-num{top:20%;background-color:transparent}.ssk-count.ssk-sticky.ssk-left .ssk-num{left:100%;margin-left:5px}.ssk-count.ssk-sticky.ssk-right .ssk-num{right:115%;margin-left:-100%;text-align:right}.ssk-facebook{background-color:#255c95}.ssk-grayscale>.ssk-facebook{background-color:#757575}.ssk-facebook:hover{background-color:#1b436c}.ssk-facebook:hover{background-color:#1b436c}.ssk-grayscale>.ssk-facebook:hover{background-color:#255c95}.ssk-facebook.ssk-icon{color:#255c95}.ssk-facebook.ssk-icon:hover{color:#1b436c}.ssk-facebook.ssk-icon:before{text-indent:0;margin-right:0}.ssk-twitter{background-color:#00b4e0}.ssk-grayscale>.ssk-twitter{background-color:#757575}.ssk-twitter:hover{background-color:#008bad}.ssk-twitter:hover{background-color:#008bad}.ssk-grayscale>.ssk-twitter:hover{background-color:#00b4e0}.ssk-twitter.ssk-icon{color:#00b4e0}.ssk-twitter.ssk-icon:hover{color:#008bad}.ssk-google-plus{background-color:#f1403a}.ssk-grayscale>.ssk-google-plus{background-color:#757575}.ssk-google-plus:hover{background-color:#e81810}.ssk-google-plus:hover{background-color:#e81810}.ssk-grayscale>.ssk-google-plus:hover{background-color:#f1403a}.ssk-google-plus.ssk-icon{color:#f1403a}.ssk-google-plus.ssk-icon:hover{color:#e81810}.ssk-pinterest{background-color:#cb2027}.ssk-grayscale>.ssk-pinterest{background-color:#757575}.ssk-pinterest:hover{background-color:#9f191f}.ssk-pinterest:hover{background-color:#9f191f}.ssk-grayscale>.ssk-pinterest:hover{background-color:#cb2027}.ssk-pinterest.ssk-icon{color:#cb2027}.ssk-pinterest.ssk-icon:hover{color:#9f191f}.ssk-tumblr{background-color:#395773}.ssk-grayscale>.ssk-tumblr{background-color:#757575}.ssk-tumblr:hover{background-color:#283d51}.ssk-tumblr:hover{background-color:#283d51}.ssk-grayscale>.ssk-tumblr:hover{background-color:#395773}.ssk-tumblr.ssk-icon{color:#395773}.ssk-tumblr.ssk-icon:hover{color:#283d51}.ssk-email{background-color:#757575}.ssk-grayscale>.ssk-email{background-color:#757575}.ssk-email:hover{background-color:#5b5b5b}.ssk-email:hover{background-color:#5b5b5b}.ssk-grayscale>.ssk-email:hover{background-color:#757575}.ssk-grayscale>.ssk-email:hover{background-color:#5b5b5b}.ssk-email.ssk-icon{color:#757575}.ssk-email.ssk-icon:hover{color:#5b5b5b}.ssk-vk{background-color:#54769a}.ssk-grayscale>.ssk-vk{background-color:#757575}.ssk-vk:hover{background-color:#425d79}.ssk-vk:hover{background-color:#425d79}.ssk-grayscale>.ssk-vk:hover{background-color:#54769a}.ssk-vk.ssk-icon{color:#54769a}.ssk-vk.ssk-icon:hover{color:#425d79}.ssk-linkedin{background-color:#1c87bd}.ssk-grayscale>.ssk-linkedin{background-color:#757575}.ssk-linkedin:hover{background-color:#156791}.ssk-linkedin:hover{background-color:#156791}.ssk-grayscale>.ssk-linkedin:hover{background-color:#1c87bd}.ssk-linkedin.ssk-icon{color:#1c87bd}.ssk-linkedin.ssk-icon:hover{color:#156791}.ssk-whatsapp{background-color:#34af23}.ssk-grayscale>.ssk-whatsapp{background-color:#757575}.ssk-whatsapp:hover{background-color:#27851a}.ssk-whatsapp:hover{background-color:#27851a}.ssk-grayscale>.ssk-whatsapp:hover{background-color:#34af23}.ssk-whatsapp.ssk-icon{color:#34af23}.ssk-whatsapp.ssk-icon:hover{color:#27851a}.ssk-reddit{background-color:#5f99cf}.ssk-grayscale>.ssk-reddit{background-color:#757575}.ssk-reddit:hover{background-color:#3a80c1}.ssk-reddit:hover{background-color:#3a80c1}.ssk-grayscale>.ssk-reddit:hover{background-color:#5f99cf}.ssk-reddit.ssk-icon{color:#5f99cf}.ssk-reddit.ssk-icon:hover{color:#3a80c1}.ssk-reddit2{background-color:#5f99cf}.ssk-grayscale>.ssk-reddit2{background-color:#757575}.ssk-reddit2:hover{background-color:#3a80c1}.ssk-reddit2:hover{background-color:#3a80c1}.ssk-grayscale>.ssk-reddit2:hover{background-color:#5f99cf}.ssk-reddit2.ssk-icon{color:#5f99cf}.ssk-reddit2.ssk-icon:hover{color:#3a80c1}.ssk-turquoise{background-color:#1abc9c}.ssk-turquoise:hover{background-color:#148f77}.ssk-emerald{background-color:#2ecc71}.ssk-emerald:hover{background-color:#25a25a}.ssk-peter-river{background-color:#3498db}.ssk-peter-river:hover{background-color:#217dbb}.ssk-belize-hole{background-color:#2980b9}.ssk-belize-hole:hover{background-color:#20638f}.ssk-amethyst{background-color:#9b59b6}.ssk-amethyst:hover{background-color:#804399}.ssk-wisteria{background-color:#8e44ad}.ssk-wisteria:hover{background-color:#703688}.ssk-wet-asphalt{background-color:#34495e}.ssk-wet-asphalt:hover{background-color:#222f3d}.ssk-midnight-blue{background-color:#2c3e50}.ssk-midnight-blue:hover{background-color:#1a242f}.ssk-green-sea{background-color:#16a085}.ssk-green-sea:hover{background-color:#107360}.ssk-nephritis{background-color:#27ae60}.ssk-nephritis:hover{background-color:#1e8449}.ssk-sunflower{background-color:#f1c40f}.ssk-sunflower:hover{background-color:#c29d0b}.ssk-orange{background-color:#f39c12}.ssk-orange:hover{background-color:#c87f0a}.ssk-carrot{background-color:#e67e22}.ssk-carrot:hover{background-color:#bf6516}.ssk-pumpkin{background-color:#d35400}.ssk-pumpkin:hover{background-color:#a04000}.ssk-alizarin{background-color:#e74c3c}.ssk-alizarin:hover{background-color:#d62c1a}.ssk-pomegranate{background-color:#c0392b}.ssk-pomegranate:hover{background-color:#962d22}.ssk-clouds{background-color:#cfd9db}.ssk-clouds:hover{background-color:#b1c2c6}.ssk-concrete{background-color:#95a5a6}.ssk-concrete:hover{background-color:#798d8f}.ssk-silver{background-color:#bdc3c7}.ssk-silver:hover{background-color:#a1aab0}.ssk-asbestos{background-color:#7f8c8d}.ssk-asbestos:hover{background-color:#667273}.ssk-dark-gray{background-color:#555}.ssk-dark-gray:hover{background-color:#3b3b3b}.ssk-black{background-color:#333}.ssk-black:hover{background-color:#1a1a1a}#article body,#instructions body,.md-preview body{color:#333;font-family:Roboto Condensed;font-size:14px;line-height:1.6;padding-top:10px;padding-bottom:10px;background-color:white;padding:30px}#article body>*:first-child,#instructions body>*:first-child,.md-preview body>*:first-child{margin-top:0!important}#article body>*:last-child,#instructions body>*:last-child,.md-preview body>*:last-child{margin-bottom:0!important}#article a,#instructions a,.md-preview a{text-decoration:none}#article a.absent,#instructions a.absent,.md-preview a.absent{color:#c00}#article h1,#instructions h1,.md-preview h1,#article h2,#instructions h2,.md-preview h2,#article h3,#instructions h3,.md-preview h3,#article h4,#instructions h4,.md-preview h4,#article h5,#instructions h5,.md-preview h5,#article h6,#instructions h6,.md-preview h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased;cursor:text;position:relative}#article img,#instructions img,.md-preview img{max-width:100%}#article h1:hover a.anchor,#instructions h1:hover a.anchor,.md-preview h1:hover a.anchor,#article h2:hover a.anchor,#instructions h2:hover a.anchor,.md-preview h2:hover a.anchor,#article h3:hover a.anchor,#instructions h3:hover a.anchor,.md-preview h3:hover a.anchor,#article h4:hover a.anchor,#instructions h4:hover a.anchor,.md-preview h4:hover a.anchor,#article h5:hover a.anchor,#instructions h5:hover a.anchor,.md-preview h5:hover a.anchor,#article h6:hover a.anchor,#instructions h6:hover a.anchor,.md-preview h6:hover a.anchor{background:url(../../images/modules/styleguide/para.png) no-repeat 10px center;text-decoration:none}#article h1 tt,#instructions h1 tt,.md-preview h1 tt,#article h1 code,#instructions h1 code,.md-preview h1 code{font-size:inherit}#article h2 tt,#instructions h2 tt,.md-preview h2 tt,#article h2 code,#instructions h2 code,.md-preview h2 code{font-size:inherit}#article h3 tt,#instructions h3 tt,.md-preview h3 tt,#article h3 code,#instructions h3 code,.md-preview h3 code{font-size:inherit}#article h4 tt,#instructions h4 tt,.md-preview h4 tt,#article h4 code,#instructions h4 code,.md-preview h4 code{font-size:inherit}#article h5 tt,#instructions h5 tt,.md-preview h5 tt,#article h5 code,#instructions h5 code,.md-preview h5 code{font-size:inherit}#article h6 tt,#instructions h6 tt,.md-preview h6 tt,#article h6 code,#instructions h6 code,.md-preview h6 code{font-size:inherit}#article h1,#instructions h1,.md-preview h1{font-size:28px;color:black}#article h2,#instructions h2,.md-preview h2{font-size:24px;border-bottom:1px solid #ccc;color:black}#article h3,#instructions h3,.md-preview h3{font-size:18px}#article h4,#instructions h4,.md-preview h4{font-size:16px}#article h5,#instructions h5,.md-preview h5{font-size:14px}#article h6,#instructions h6,.md-preview h6{color:#777;font-size:14px}#article p,#instructions p,.md-preview p,#article blockquote,#instructions blockquote,.md-preview blockquote,#article ul,#instructions ul,.md-preview ul,#article ol,#instructions ol,.md-preview ol,#article dl,#instructions dl,.md-preview dl,#article table,#instructions table,.md-preview table,#article pre,#instructions pre,.md-preview pre{margin:15px 0}#article hr,#instructions hr,.md-preview hr{background:transparent url(../../images/modules/pulls/dirty-shade.png) repeat-x 0 0;border:0 none;color:#ccc;height:4px;padding:0}#article body>h2:first-child,#instructions body>h2:first-child,.md-preview body>h2:first-child{margin-top:0;padding-top:0}#article body>h1:first-child,#instructions body>h1:first-child,.md-preview body>h1:first-child{margin-top:0;padding-top:0}#article body>h1:first-child+h2,#instructions body>h1:first-child+h2,.md-preview body>h1:first-child+h2{margin-top:0;padding-top:0}#article body>h3:first-child,#instructions body>h3:first-child,.md-preview body>h3:first-child,#article body>h4:first-child,#instructions body>h4:first-child,.md-preview body>h4:first-child,#article body>h5:first-child,#instructions body>h5:first-child,.md-preview body>h5:first-child,#article body>h6:first-child,#instructions body>h6:first-child,.md-preview body>h6:first-child{margin-top:0;padding-top:0}#article a:first-child h1,#instructions a:first-child h1,.md-preview a:first-child h1,#article a:first-child h2,#instructions a:first-child h2,.md-preview a:first-child h2,#article a:first-child h3,#instructions a:first-child h3,.md-preview a:first-child h3,#article a:first-child h4,#instructions a:first-child h4,.md-preview a:first-child h4,#article a:first-child h5,#instructions a:first-child h5,.md-preview a:first-child h5,#article a:first-child h6,#instructions a:first-child h6,.md-preview a:first-child h6{margin-top:0;padding-top:0}#article h1 p,#instructions h1 p,.md-preview h1 p,#article h2 p,#instructions h2 p,.md-preview h2 p,#article h3 p,#instructions h3 p,.md-preview h3 p,#article h4 p,#instructions h4 p,.md-preview h4 p,#article h5 p,#instructions h5 p,.md-preview h5 p,#article h6 p,#instructions h6 p,.md-preview h6 p{margin-top:0}#article li p.first,#instructions li p.first,.md-preview li p.first{display:inline-block}#article ul,#instructions ul,.md-preview ul,#article ol,#instructions ol,.md-preview ol{padding-left:30px}#article ul :first-child,#instructions ul :first-child,.md-preview ul :first-child,#article ol :first-child,#instructions ol :first-child,.md-preview ol :first-child{margin-top:0}#article ul :last-child,#instructions ul :last-child,.md-preview ul :last-child,#article ol :last-child,#instructions ol :last-child,.md-preview ol :last-child{margin-bottom:0}#article dl,#instructions dl,.md-preview dl{padding:0}#article dl dt,#instructions dl dt,.md-preview dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}#article dl dt:first-child,#instructions dl dt:first-child,.md-preview dl dt:first-child{padding:0}#article dl dt>:first-child,#instructions dl dt>:first-child,.md-preview dl dt>:first-child{margin-top:0}#article dl dt>:last-child,#instructions dl dt>:last-child,.md-preview dl dt>:last-child{margin-bottom:0}#article dl dd,#instructions dl dd,.md-preview dl dd{margin:0 0 15px;padding:0 15px}#article dl dd>:first-child,#instructions dl dd>:first-child,.md-preview dl dd>:first-child{margin-top:0}#article dl dd>:last-child,#instructions dl dd>:last-child,.md-preview dl dd>:last-child{margin-bottom:0}#article blockquote,#instructions blockquote,.md-preview blockquote{border-left:4px solid #ddd;padding:0 15px;color:#777}#article blockquote>:first-child,#instructions blockquote>:first-child,.md-preview blockquote>:first-child{margin-top:0}#article blockquote>:last-child,#instructions blockquote>:last-child,.md-preview blockquote>:last-child{margin-bottom:0}#article table,#instructions table,.md-preview table{padding:0}#article table tr,#instructions table tr,.md-preview table tr{border-top:1px solid #ccc;background-color:white;margin:0;padding:0}#article table tr:nth-child(2n),#instructions table tr:nth-child(2n),.md-preview table tr:nth-child(2n){background-color:#f8f8f8}#article table tr th,#instructions table tr th,.md-preview table tr th{font-weight:700;border:1px solid #ccc;text-align:left;margin:0;padding:6px 13px}#article table tr td,#instructions table tr td,.md-preview table tr td{border:1px solid #ccc;text-align:left;margin:0;padding:6px 13px}#article table tr th :first-child,#instructions table tr th :first-child,.md-preview table tr th :first-child,#article table tr td :first-child,#instructions table tr td :first-child,.md-preview table tr td :first-child{margin-top:0}#article table tr th :last-child,#instructions table tr th :last-child,.md-preview table tr th :last-child,#article table tr td :last-child,#instructions table tr td :last-child,.md-preview table tr td :last-child{margin-bottom:0}#article img,#instructions img,.md-preview img{max-width:100%}#article span.frame,#instructions span.frame,.md-preview span.frame{display:block;overflow:hidden}#article span.frame>span,#instructions span.frame>span,.md-preview span.frame>span{border:1px solid #ddd;display:block;float:left;overflow:hidden;margin:13px 0 0;padding:7px;width:auto}#article span.frame span img,#instructions span.frame span img,.md-preview span.frame span img{display:block;float:left}#article span.frame span span,#instructions span.frame span span,.md-preview span.frame span span{clear:both;color:#333;display:block;padding:5px 0 0}#article span.align-center,#instructions span.align-center,.md-preview span.align-center{display:block;overflow:hidden;clear:both}#article span.align-center>span,#instructions span.align-center>span,.md-preview span.align-center>span{display:block;overflow:hidden;margin:13px auto 0;text-align:center}#article span.align-center span img,#instructions span.align-center span img,.md-preview span.align-center span img{margin:0 auto;text-align:center}#article span.align-right,#instructions span.align-right,.md-preview span.align-right{display:block;overflow:hidden;clear:both}#article span.align-right>span,#instructions span.align-right>span,.md-preview span.align-right>span{display:block;overflow:hidden;margin:13px 0 0;text-align:right}#article span.align-right span img,#instructions span.align-right span img,.md-preview span.align-right span img{margin:0;text-align:right}#article span.float-left,#instructions span.float-left,.md-preview span.float-left{display:block;margin-right:13px;overflow:hidden;float:left}#article span.float-left span,#instructions span.float-left span,.md-preview span.float-left span{margin:13px 0 0}#article span.float-right,#instructions span.float-right,.md-preview span.float-right{display:block;margin-left:13px;overflow:hidden;float:right}#article span.float-right>span,#instructions span.float-right>span,.md-preview span.float-right>span{display:block;overflow:hidden;margin:13px auto 0;text-align:right}#article code,#instructions code,.md-preview code,#article tt,#instructions tt,.md-preview tt{margin:0 2px;padding:0 5px;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}#article pre code,#instructions pre code,.md-preview pre code{margin:0;padding:0;white-space:pre;border:none;background:transparent}#article .highlight pre,#instructions .highlight pre,.md-preview .highlight pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}#article pre,#instructions pre,.md-preview pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}#article pre code,#instructions pre code,.md-preview pre code,#article pre tt,#instructions pre tt,.md-preview pre tt{background-color:transparent;border:none}html{overflow-x:hidden}.bootbox-form textarea{height:300px}.placepicker-map{width:100%;height:300px}.topic .posts .content iframe,.topic .posts .content .img-responsive{max-width:85%;display:inline}.github-embed img.author-picture{float:left;width:16px;margin-right:.5em}.topic .posts h3{text-transform:uppercase;font-size:16px;font-weight:900}.github-embed h3{font-size:16px;margin:0;font-weight:700;color:#333}.mandatory{font-size:.8em;color:#999}.ir{float:right;padding-left:20px}@media print{a[href]:after{content:none!important}}.addressTag{background-image:url(/images/label.png);background-size:contain;background-repeat:no-repeat;width:400px;opacity:.7;margin-bottom:-24px;margin-top:-28px}.addressTag .address{opacity:1;color:#000;text-shadow:0 0 0 rgba(255,255,255,.3),1px 1px 0 rgba(255,255,255,.8);-webkit-transform:rotate(10.2deg);-khtml-transform:rotate(10.2deg);-moz-transform:rotate(10.2deg);transform:rotate(10.2deg);padding-left:44px;padding-top:50px;padding-bottom:20px;width:300px;height:200px;font-size:16px;font-weight:700}.orderTable th{white-space:nowrap}.orderTable .radio{padding-left:20px}.orderTable .table{margin-bottom:0}.orderTable img{max-width:100px}.orderTable .factone{margin-top:10px}.orderTable .btn-order{min-width:120px}.orderTable .fact{clear:both;float:left;margin-top:8px;margin-bottom:0}#tab-container{min-height:200px}.prices{width:100%;text-align:center}.prices ul{display:inline-block;padding-left:0;margin:0 auto;list-style-type:none}.prices li.cart{background:inherit;font-size:30px;padding:0;padding-top:10px;border:none;cursor:inherit}.prices li.cart:hover{color:#888;border:none;background:#fff}.prices li.cart p{font-size:13px;margin:0;margin-top:2px;margin-right:-5px;padding-bottom:2px;margin-bottom:-13px;clear:both}.prices .buy a:hover{text-decoration:none}.prices .buy a{color:#888}.prices li{cursor:pointer;color:#888;background:#eee;border-radius:4px;padding:10px;margin-top:5px;margin-left:6px;margin-right:6px;text-align:center;float:left;border:1px solid #fff}.prices li h4{color:#ee791d;margin-top:2px;margin-bottom:0}.prices li:hover{color:#000;background:#f4f4f4;border:1px solid #888}.prices li:hover h4{color:#ff4c08}.donate{width:20px;height:20px;margin-top:-4px;margin-left:4px;margin-right:6px;opacity:.5}.htMax200{width:200px}.htMax150{width:150px;overflow-x:hidden}.create .tab{width:110px}.pcb-dimensions{padding-left:0;padding-right:0;margin-right:-40px;line-height:32px;margin-left:-6px}.input-group .tag-editor{width:200px;background:#eee;box-shadow:0 0 9px rgba(0,153,176,.5);-webkit-box-shadow:0 0 9px rgba(0,153,176,.5);-moz-box-shadow:0 0 9px rgba(0,153,176,.5);border-top-right-radius:0;border-bottom-right-radius:0}.editProject{margin-right:15px}.selectOne{overflow-y:scroll;padding:0;height:400px}.selectOne i{display:none}.selectOne ul{width:100%;min-height:200px;list-style-type:none;margin:0;padding:10px;float:left;margin-right:10px}.selectOne li{margin:2px;padding:3px;font-size:14px;width:100%;cursor:hand;background:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.selectOne li:hover{background:#eee}@font-face{font-family:Roboto Condensed;src:url(/fonts/RobotoCondensed-Regular-webfont.eot);src:url(/fonts/RobotoCondensed-Regular-webfont.eot?#iefix) format('embedded-opentype'),url(/fonts/RobotoCondensed-Regular-webfont.woff) format('woff'),url(/fonts/RobotoCondensed-Regular-webfont.ttf) format('truetype'),url(/fonts/RobotoCondensed-Regular-webfont.svg#roboto_condensedregular) format('svg')}@font-face{font-family:Menu;src:url(/fonts/OCRAExtended.eot);src:url(/fonts/OCRAExtended.eot?#iefix) format('embedded-opentype'),url(/fonts/OCRAExtended.woff) format('woff'),url(/fonts/OCRAExtended.ttf) format('truetype'),url(/fonts/OCRAExtended.svg#OCRAExtended) format('svg')}iframe.codebender-plugin{border:0;height:510px;width:100%;margin:10px 0}@media only screen and (max-width:321px){iframe.codebender-plugin{border:0;height:180px;width:100%}}@media only screen and (max-width:320px){iframe.codebender-plugin{border:0;height:180px;width:100%}}@media only screen and (min-device-width:768px) and (max-device-width:1000px) and (orientation:landscape){iframe.codebender-plugin{border:0;height:360px;width:100%}}@media only screen and (min-device-width:768px) and (max-device-width:1024px) and (orientation:portrait){iframe.codebender-plugin{border:0;height:360px;width:100%}}.fotorama{margin-bottom:30px}.align-right{text-align:right}.align-center{text-align:center}.etabs{margin:0;padding-left:10px}.creator{white-space:nowrap}.tab{width:140px;display:inline-block;zoom:1;display:inline;background:#fff;border-bottom:none}.tab a{color:#888;text-decoration:none;text-align:center;font-size:16px;line-height:24px;display:block;padding:7px;outline:none}.tab a:hover{color:#000}.tab.active{border-top:solid 1px #888;border-left:solid 1px #888;border-right:solid 1px #888;color:#000;padding-top:1px;position:relative;top:1px;border-color:#888;border-top-left-radius:2px;border-top-right-radius:2px}.tab a.active{color:#000}.tab-container .panel-container{border-top:solid #888 1px;padding:20px}#spinner{margin:50px}.view-project .title{font-family:Roboto Condensed;font-weight:700;font-size:34px;padding-top:16px;margin-bottom:-6px;white-space:nowrap;text-overflow:ellipsis}.view-project .well{background:#fff}#donateForm{display:none}.view-project .actions{border:1px solid #eee;padding:0;border-radius:3px;width:100%;float:left}.view-project .actions-wrapper{padding-left:15px;padding-right:15px;padding-bottom:15px;margin-top:-25px;width:100%}.view-project .actionRow:hover{color:#000}.view-project .actionRowFirst{float:left;color:#888;width:100%;padding:8px;border-bottom:1px solid #eee}.view-project .actionRow{float:left;cursor:pointer;color:#888;width:100%;padding:8px;border-bottom:1px solid #eee}.view-project .actionRowShare{padding-top:7px;padding-bottom:5px;padding-right:0}.view-project .last{border-bottom:0}.view-project .actionRow .action{width:30px;padding-left:5px}.view-project .overview .left{float:left;padding-right:10px;text-align:right;width:80px}.view-project .overview .right{padding-right:5px;padding-left:5px;clear:right}.view-project .overview{overflow:hidden;font-size:14px;padding-left:16px}.profile .wip,.view-project .wip{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1;border-radius:3px;width:100%;text-align:center;padding-top:10px;padding-bottom:10px;margin-bottom:10px}.view-project .actionRow .count{float:right;text-align:right;padding-right:10px}.view-project .avatar{float:left}.goto,.like,.collect,.view{cursor:pointer}.like:hover{color:pink}.like i.active{color:red}.collect:hover{color:lightgreen}.collect i.active{color:green}.view:hover{color:lightblue}.view i.active{color:blue}.tag-editor{border:1px solid #ccc;border-radius:4px;height:36px}.tag-editor li{line-height:28px}.clip-corner{pointer-events:none;overflow:hidden;display:block;position:absolute;top:6px;height:200px;width:200px}.corner-ribbon{width:238px;text-align:center;line-height:31px;letter-spacing:1px;transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}.corner-ribbon.corner-sticky{position:absolute}.corner-ribbon.shadow{box-shadow:0 0 3px rgba(0,0,0,.3)}.corner-ribbon.top-left{top:50px;left:-46px;transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}.corner-ribbon.normal{background:rgba(0,153,176,.5);color:#fff}.corner-ribbon.black{background:#333}.corner-ribbon.grey{background:#999}.corner-ribbon.blue{background:#39d}.corner-ribbon.green{background:#2c7}.corner-ribbon.turquoise{background:#1b9}.corner-ribbon.purple{background:#95b}.corner-ribbon.red{background:#e43}.corner-ribbon.orange{background:#e82}.corner-ribbon.yellow{background:#ec0}.input-group .form-control{z-index:inherit}.filelists{display:table;width:100%}.filelist .del-file{vertical-align:middle;margin-right:2px;margin-top:3px;cursor:pointer}.filelist .list-image .del-file{margin-top:18px}.filelist-row{display:table-row}.filelist{display:table-cell;width:50%;vertical-align:top}.filelist-button{width:60px}.filelist-image-text{height:50px;line-height:50px;position:relative}.filelist-image{vertical-align:middle;position:absolute;top:0;right:20px;height:50px}.filelist-publish{display:table-cell;width:200px;padding:10px;text-align:center;vertical-align:top}.connected{border:2px dashed #ccc;width:100%;min-height:200px;list-style-type:none;margin:0;padding:10px;float:left;margin-right:10px}.connected li{margin:0;padding:3px;font-size:14px;width:100%;cursor:move;background:#fff;overflow:hidden;text-overflow:ellipsis}.clear{clear:both}.center-ad{text-align:center;padding-top:10px;padding-bottom:10px}.panel-container hr{margin-top:-8px;margin-bottom:10px}.panel-container .small{font-size:93%}.news hr{margin-top:10px;margin-bottom:10px}.news h4{margin-top:0}.dropHeader{font-size:19px}.github-fixed-branch{width:150px!important}.github-fixed-branch select[disabled]{opacity:0;hand:normal}.bootstrap-tagsinput{display:block;width:auto;margin:auto 0;height:35px}span[class^=ion-]{padding-right:6px}.menu-avatar-wrapper{margin-top:-4px;margin-left:-6px;border:2px solid rgba(60,99,174,.54);width:36px;height:36px;border-radius:22px;background:#fff}.menu-avatar-wrapper img{height:26px;position:relative;top:3px;vertical-align:top;left:3px;width:26px;border-radius:24px}.menu-avatar-wrapper i{margin-left:10px}.user-avatar-wrapper{float:left;width:66px;height:66px;border:3px solid #3c63ae;border-radius:100px;background:#fff}.user-avatar-wrapper img{height:60px;width:60px;border-radius:100px}.avatar-wrapper{position:absolute;left:10px;top:15px;border:2px solid #3c63ae;border-radius:36px;background:#fff}.avatar-container{float:left;padding-right:20px}.avatar-wrapper img{height:36px;width:36px;border-radius:36px}.form-avatar-wrapper{border-radius:60px;float:left;margin:10px;margin-top:20px;border:2px solid #3c63ae;background:#fff}.form-avatar-wrapper img{width:60px;height:60px;border-radius:60px}footer{position:absolute;bottom:0;width:100%;height:45px;line-height:45px}.embedHeader{background-color:#e4e4e4;width:100%;padding:5px;border-radius:4px 4px 0 0;border:1px solid #ccc;position:relative;white-space:nowrap;margin-bottom:-19px}.embedHeader img{float:right;height:44px;border-radius:3px}.embedHeader .embedFile{display:block;max-width:90%;overflow:hidden;text-overflow:ellipsis}pre{max-height:350px}.alert{margin-top:20px}.tn{position:relative;text-align:center;display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail{padding:0;border-radius:0;box-shadow:0 0 5px #ccc,inset 0 0 0 #000}body{font-family:Roboto Condensed,Helvetica,Arial,sans-serif;position:relative}.search-pad{margin:4px}.search-pad .input-group>.form-control{width:100%;border-radius:29px 0 0 29px;box-shadow:inherit;background:rgba(234,233,233,.56)}.search-pad .form-control:hover,.search-pad .form-control:focus,.search-pad .add-on:hover{background:#eee}.search-pad .form-control{color:inherit;border:0}.search-pad .input-group-addon{width:auto;border:0;color:inherit;border-radius:0 29px 29px 0;background:rgba(234,233,233,.56)}.promo{margin-left:10px;margin-right:10px}.promo-text{font-weight:300;font-size:30px;color:#333;white-space:nowrap}.promo-headline{font-weight:300;height:40px;padding-right:10px;padding-left:10px;font-size:30px;line-height:37px;color:#fff;border-radius:4px;white-space:nowrap}.flash{box-shadow:3px 2px 15px #888;margin-top:20px}.fixed{position:fixed;z-index:1;top:0;left:0;width:100%}.navbar{margin-bottom:0}.sticky{width:100%;background:rgba(0,153,176,.5);box-shadow:0 6px 15px rgba(59,104,111,.71);-webkit-box-shadow:0 6px 15px rgba(59,104,111,.71);-moz-box-shadow:0 6px 15px rgba(59,104,111,.71);border:1px rgba(0,153,176,.5);border-radius:0;min-height:inherit}.sticky .input-group-addon{font-size:14px}.sticky .tag-editor li{font-size:14px;line-height:20px}.sticky .tag-editor{height:28px}.sticky .tag-editor .placeholder{padding:0}.sticky .tag-editor .tag-editor-tag{padding-left:0}.sticky ul li,.sticky ul li>a,.navbar-default .navbar-nav>li>a{color:#3c63ae;padding-right:4px;font-size:14px;float:left}.sticky .navbar-nav>li>a{padding:0 9px;line-height:24px;border-radius:29px;margin-bottom:6px;margin-top:6px;background:rgba(234,233,233,.56)}.sticky .navbar-nav>li>a.active,.sticky .navbar-nav>li>a:hover,.sticky .navbar-nav>li>a:focus{background:#eee;box-shadow:0 0 9px rgba(0,153,176,.5);-webkit-box-shadow:0 0 9px rgba(0,153,176,.5);-moz-box-shadow:0 0 9px rgba(0,153,176,.5)}.input-group .bootstrap-select{z-index:inherit!important}.navbar-nav .open .dropdown-menu{position:absolute}.error{color:red}.dropper-dropzone{border:2px dashed #ccc}.well-sm{padding-left:16px;padding-right:16px}.horizontal label{font-size:16px;font-weight:400}.below{padding-bottom:10px}.profile hr{margin-top:10px;margin-bottom:10px;border:0;border-top:1px solid #ccc}hr{margin-top:13px;margin-bottom:2px;border:0;border-top:1px solid #ccc}h4,.h4{font-size:20px}.navbar-toggle{padding:inherit;line-height:44px;color:#888}.navbar-simple .brand{color:#000;padding-top:15px;padding-bottom:15px}.navbar-simple .brand a{color:#000}.navbar-simple .navbar-inner .nav li a{white-space:nowrap}.navbar-simple .navbar-inner .nav{float:right}.navbar-simple .navbar-inner .nav li a{background:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;color:#888}.navbar-simple .navbar-inner .nav li a:hover{background:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;color:#000}.page{margin-top:12px}.box-move:before,.box-move:after{background:initial!important;box-shadow:initial!important;-moz-box-shadow:initial!important;-webkit-box-shadow:initial!important}.floaters{margin-top:24px;text-align:center}.box .featured{color:yellow;opacity:.75;font-size:50px;position:absolute;top:22px;width:24px;right:33px}.box .forSale{color:#80ccd8;opacity:.75;font-size:50px;position:absolute;top:188px;width:24px;right:33px}.box .updated{pointer-events:none;position:absolute;font-size:12px;display:inline-block;top:10px;right:10px;width:200px;height:19px;text-align:right;overflow:hidden}.box .creator{pointer-events:none;position:absolute;font-size:12px;display:inline-block;top:10px;left:58px;width:200px;height:25px;text-align:left;overflow:hidden}.box .shade{pointer-events:none;position:absolute;top:6px;left:6px;background:linear-gradient(to bottom,rgba(0,0,0,.5) 0,transparent 100%);height:90px;width:300px}.profile-picture{position:absolute;top:10px;left:10px;display:inline-block;width:50px;height:50px;background:#fff;border:2px solid #3c63ae;opacity:.9;border-radius:25px;float:left;cursor:pointer;-webkit-transition:border-color .1s ease-in-out;-moz-transition:border-color .1s ease-in-out;-o-transition:border-color .1s ease-in-out;transition:border-color .1s ease-in-out}.profile-picture img{width:46px;height:46px;border-radius:25px}.box .hardware{width:100%;overflow:hidden;display:block;min-height:272px;min-width:292px;background:#fff;background-position:center;background-repeat:no-repeat;background-size:cover;border:1px solid #bababa}.box .hardware img{display:block;margin-left:auto;margin-right:auto;outline:0;max-width:100%;height:auto}.box .profile-name{width:200px;height:14px;bottom:94px;text-align:right;right:20px;position:absolute;white-space:nowrap;text-overflow:ellipsis;display:inline-block;overflow:hidden;color:#ddd;text-shadow:1px 1px #0099b0;cursor:pointer;font-size:12px}.box .buttons:hover{opacity:1.0;transition:opacity .25s ease-in-out;-moz-transition:opacity .25s ease-in-out;-webkit-transition:opacity .25s ease-in-out}.box .buttons{opacity:.3;transition:opacity .25s ease-in-out;-moz-transition:opacity .25s ease-in-out;-webkit-transition:opacity .25s ease-in-out;bottom:57px;margin:0 1px;font-size:20px;padding:8px;position:absolute;overflow:hidden;text-shadow:1px 1px #333;color:#fff;width:300px;background-color:rgba(166,116,178,.7)}.box .button{width:33%;float:left;text-align:center}.box .button:first-child{text-align:left}.box .button:last-child{text-align:right}.box a:hover{text-decoration:none;color:rgba(0,0,0,.8)}.box a{color:rgba(0,0,0,.8)}.box .title{padding:10px 0;height:52px;font-weight:700;font-family:Menu;line-height:19px;display:block;display:-webkit-box;margin:0 auto;text-align:center;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;color:rgba(0,0,0,.8);text-shadow:0 1px 0 #fff}.box{color:#fff;text-shadow:0 1px 0 #000;position:relative;display:inline-block;background:#e6e2d6;-moz-border-radius:4px;border-radius:4px;padding:5px;line-height:1;margin-right:10px;margin-left:10px;margin-bottom:25px;width:312px;vertical-align:top}.box:before,.box:after{z-index:-1;position:absolute;content:"";bottom:12px;left:10px;width:50%;top:80%;max-width:300px;background:rgba(0,0,0,.7);-webkit-box-shadow:0 15px 10px rgba(0,0,0,.7);-moz-box-shadow:0 15px 10px rgba(0,0,0,.7);box-shadow:0 15px 10px rgba(0,0,0,.7);-webkit-transform:rotate(-3deg);-moz-transform:rotate(-3deg);-o-transform:rotate(-3deg);-ms-transform:rotate(-3deg);transform:rotate(-3deg)}.box:after{-webkit-transform:rotate(3deg);-moz-transform:rotate(3deg);-o-transform:rotate(3deg);-ms-transform:rotate(3deg);transform:rotate(3deg);right:10px;left:auto}.footer{clear:both;text-align:center;padding-top:20px;padding-bottom:20px}.navbar-right{margin-right:inherit}.sticky .navbar{min-height:inherit}.menu-text{padding-top:15px}.md-editor>textarea{background:#fff;padding:10px}.md-editor>.md-preview{padding:10px}.ocr{display:inline-block;font-family:Menu!important;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.navbar-brand{background:url(/images/logo.png) no-repeat;background-size:44px 44px;background-position:6px 12px;height:68px;margin-left:-5px;line-height:28px;padding-left:60px;font-size:30px;color:#888}.navbar-brand:hover{color:#888}.navbar-nav>li>a{font-size:18px;line-height:32px}.bootstrap-select>.dropdown-toggle{z-index:inherit}.modal-backdrop{z-index:1035}.modal{z-index:999991}.md-nooverflow{position:inherit}.fotorama__html div,.fotorama__html iframe{width:100%;height:100%}.navbar-fixed-bottom{padding-left:10px;right:0;left:initial;text-align:center;border-left:1px solid #e7e7e7;border-radius:4px}.navbar-fixed-bottom button{display:none;margin-right:10px}.well{box-shadow:3px 2px 15px #888}.form-signin{max-width:330px;padding:15px;margin:0 auto}.form-signin .form-signin-heading,.form-signin .checkbox{margin-bottom:10px}.form-signin .checkbox{font-weight:400}.form-signin .form-control{position:relative;font-size:16px;height:auto;padding:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.form-signin .form-control:focus{z-index:2}.form-signin input[type=text]{margin-bottom:-1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.form-signin input[type=password]{margin-bottom:10px;border-top-left-radius:0;border-top-right-radius:0}div.toc{counter-reset:chapter}div.toc>ol{padding-left:0}div.toc>ol>ol{padding-left:30px}div.toc>ol>ol>ol{padding-left:30px}div.toc>ol>li{list-style-type:none;counter-increment:chapter;counter-reset:section;padding-top:25px;font-size:20px;padding-bottom:10px}div.toc>ol>ol>li{list-style-type:none;counter-increment:section;counter-reset:none;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;padding-top:10px;padding-bottom:5px}div.toc>ol>ol>ol>li{list-style-type:none;counter-increment:subsection;counter-reset:none;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;padding-top:10px;padding-bottom:5px}div.toc>ol li:before{content:"SECTION " counter(chapter) ": ";display:block;float:left;width:6em}div.toc>ol ol li:before{font-weight:700;content:counter(chapter) "." counter(section) ".";width:2em}div.toc>ol ol ol li:before{font-weight:700;content:counter(chapter) "." counter(section) "." counter(subsection) ".";width:3em}.affiliate{font:13px Helvetica,arial,freesans,clean,sans-serif;background-color:#fff;border:1px solid #aaa;border-radius:4px;position:relative;min-height:102px;box-shadow:5px 5px 5px #aaa;margin-top:10px}.affiliate a{color:#333;text-decoration:none}.affiliate div.price{color:white;border-radius:2px;font-size:13px;padding:3px 4px;margin-right:12px}.affiliate div.stock{font-weight:400;color:#777;padding-top:5px}.affiliate h3{padding:10px;font-size:16px;margin:0;font-weight:700;color:#333}.affiliate h3 div.ellipsis{max-height:51px;overflow:hidden}.affiliate .image{float:left;padding-bottom:8px;padding-right:17px}.affiliate .image img{float:left;height:100px;margin-right:.5em}@media(max-width:768px){.navbar-nav>li>a{padding-top:12px;padding-bottom:12px}.view-project .actionRowFirst,.view-project .actionRow,.view-project .actions{border:0}.page{margin-top:0}.view-project .title{padding-top:0}.navbar{min-height:50px}.navbar-brand{background-size:22px 22px;background-position:4px 14px}.tab{width:60px}.smalltabs .tab{width:40px}.view-project .title{font-size:28px}} \ No newline at end of file diff --git a/Logparser/logparser.html b/Logparser/logparser.html new file mode 100644 index 000000000..3596ae675 --- /dev/null +++ b/Logparser/logparser.html @@ -0,0 +1,38 @@ + + + + Log Parser | MySensors - Create your own Connected Home Experience + + + + + +
+

Paste log from gateway or node here:

+ + + + +

Human readable output:

+
+ + + + + + + + + + + + + +
Node IdChild SensorCommand TypeEcho Req/RespTypePayloadDescription
+
+
+ + + + + diff --git a/Logparser/logparser.js b/Logparser/logparser.js new file mode 100644 index 000000000..efc2e9f08 --- /dev/null +++ b/Logparser/logparser.js @@ -0,0 +1,579 @@ +function copyTextToClipboard(text) { + var textArea = document.createElement("textarea"); + + // + // *** This styling is an extra step which is likely not required. *** + // + // Why is it here? To ensure: + // 1. the element is able to have focus and selection. + // 2. if element was to flash render it has minimal visual impact. + // 3. less flakyness with selection and copying which **might** occur if + // the textarea element is not visible. + // + // The likelihood is the element won't even render, not even a flash, + // so some of these are just precautions. However in IE the element + // is visible whilst the popup box asking the user for permission for + // the web page to copy to the clipboard. + // + + // Place in top-left corner of screen regardless of scroll position. + textArea.style.position = 'fixed'; + textArea.style.top = 0; + textArea.style.left = 0; + + // Ensure it has a small width and height. Setting to 1px / 1em + // doesn't work as this gives a negative w/h on some browsers. + textArea.style.width = '2em'; + textArea.style.height = '2em'; + + // We don't need padding, reducing the size if it does flash render. + textArea.style.padding = 0; + + // Clean up any borders. + textArea.style.border = 'none'; + textArea.style.outline = 'none'; + textArea.style.boxShadow = 'none'; + + // Avoid flash of white box if rendered for any reason. + textArea.style.background = 'transparent'; + + + textArea.value = text; + + document.body.appendChild(textArea); + + textArea.select(); + + try { + var successful = document.execCommand('copy'); + var msg = successful ? 'successful' : 'unsuccessful'; + console.log('Copying text command was ' + msg); + } catch (err) { + console.log('Oops, unable to copy'); + } + + document.body.removeChild(textArea); +} + +var types = { +"presentation":[ +"S_DOOR", +"S_MOTION", +"S_SMOKE", +"S_BINARY", +"S_DIMMER", +"S_COVER", +"S_TEMP", +"S_HUM", +"S_BARO", +"S_WIND", +"S_RAIN", +"S_UV", +"S_WEIGHT", +"S_POWER", +"S_HEATER", +"S_DISTANCE", +"S_LIGHT_LEVEL", +"S_ARDUINO_NODE", +"S_ARDUINO_REPEATER_NODE", +"S_LOCK", +"S_IR", +"S_WATER", +"S_AIR_QUALITY", +"S_CUSTOM", +"S_DUST", +"S_SCENE_CONTROLLER", +"S_RGB_LIGHT", +"S_RGBW_LIGHT", +"S_COLOR_SENSOR", +"S_HVAC", +"S_MULTIMETER", +"S_SPRINKLER", +"S_WATER_LEAK", +"S_SOUND", +"S_VIBRATION", +"S_MOISTURE", +"S_INFO", +"S_GAS", +"S_GPS", +"S_WATER_QUALITY" +], +"internal": [ +"I_BATTERY_LEVEL", +"I_TIME", +"I_VERSION", +"I_ID_REQUEST", +"I_ID_RESPONSE", +"I_INCLUSION_MODE", +"I_CONFIG", +"I_FIND_PARENT_REQUEST", +"I_FIND_PARENT_RESPONSE", +"I_LOG_MESSAGE", +"I_CHILDREN", +"I_SKETCH_NAME", +"I_SKETCH_VERSION", +"I_REBOOT", +"I_GATEWAY_READY", +"I_SIGNING_PRESENTATION", +"I_NONCE_REQUEST", +"I_NONCE_RESPONSE", +"I_HEARTBEAT_REQUEST", +"I_PRESENTATION", +"I_DISCOVER_REQUEST", +"I_DISCOVER_RESPONSE", +"I_HEARTBEAT_RESPONSE", +"I_LOCKED", +"I_PING", +"I_PONG", +"I_REGISTRATION_REQUEST", +"I_REGISTRATION_RESPONSE", +"I_DEBUG", +"I_SIGNAL_REPORT_REQUEST", +"I_SIGNAL_REPORT_REVERSE", +"I_SIGNAL_REPORT_RESPONSE", +"I_PRE_SLEEP_NOTIFICATION", +"I_POST_SLEEP_NOTIFICATION" +], +"subtype":[ +"V_TEMP", +"V_HUM", +"V_STATUS", +"V_PERCENTAGE", +"V_PRESSURE", +"V_FORECAST", +"V_RAIN", +"V_RAINRATE", +"V_WIND", +"V_GUST", +"V_DIRECTION", +"V_UV", +"V_WEIGHT", +"V_DISTANCE", +"V_IMPEDANCE", +"V_ARMED", +"V_TRIPPED", +"V_WATT", +"V_KWH", +"V_SCENE_ON", +"V_SCENE_OFF", +"V_HVAC_FLOW_STATE", +"V_HVAC_SPEED", +"V_LIGHT_LEVEL", +"V_VAR1", +"V_VAR2", +"V_VAR3", +"V_VAR4", +"V_VAR5", +"V_UP", +"V_DOWN", +"V_STOP", +"V_IR_SEND", +"V_IR_RECEIVE", +"V_FLOW", +"V_VOLUME", +"V_LOCK_STATUS", +"V_LEVEL", +"V_VOLTAGE", +"V_CURRENT", +"V_RGB", +"V_RGBW", +"V_ID", +"V_UNIT_PREFIX", +"V_HVAC_SETPOINT_COOL", +"V_HVAC_SETPOINT_HEAT", +"V_HVAC_FLOW_MODE", +"V_TEXT", +"V_CUSTOM", +"V_POSITION", +"V_IR_RECORD", +"V_PH", +"V_ORP", +"V_EC", +"V_VAR", +"V_VA", +"V_POWER_FACTOR" +], +"stream":[ +"ST_FIRMWARE_CONFIG_REQUEST", +"ST_FIRMWARE_CONFIG_RESPONSE", +"ST_FIRMWARE_REQUEST", +"ST_FIRMWARE_RESPONSE", +"ST_SOUND", +"ST_IMAGE"], + command: [ +"PRESENTATION", +"SET", +"REQ", +"INTERNAL", +"STREAM" + ], +"payloadtype":[ +"P_STRING", +"P_BYTE", +"P_INT16", +"P_UINT16", +"P_LONG32", +"P_ULONG32", +"P_CUSTOM", +"P_FLOAT32" +]}; + +//mysgw: Client 0: 0;0;3;0;18;PING +var rprefix = "(?:\\d+ )?(?:mysgw: )?(?:Client 0: )?"; +var match = [ + { re: "MCO:BGN:INIT CP=([^,]+)", d: "Core initialization with capabilities $1" }, + { re: "MCO:BGN:INIT (\\w+),CP=([^,]+),VER=(.*)", d: "Core initialization of $1, with capabilities $2, library version $3" }, + { re: "MCO:BGN:INIT (\\w+),CP=([^,]+),REL=(.*),VER=(.*)", d: "Core initialization of $1, with capabilities $2, library version $4, release $3" }, + { re: "MCO:BGN:INIT (\\w+),CP=([^,]+),FQ=(\\d+),REL=(.*),VER=(.*)", d: "Core initialization of $1, with capabilities $2, CPU frequency $4 MHz, library version $5, release $4" }, + { re: "MCO:BGN:BFR", d: "Callback before()" }, + { re: "MCO:BGN:STP", d: "Callback setup()" }, + { re: "MCO:BGN:INIT OK,TSP=(.*)", d: "Core initialized, transport status $1, (1=initialized, 0=not initialized, NA=not available)" }, + { re: "MCO:BGN:NODE UNLOCKED", d: "Node successfully unlocked (see signing chapter)" }, + { re: "!MCO:BGN:TSP FAIL", d: "Transport initialization failed" }, + { re: "MCO:REG:REQ", d: "Registration request" }, + { re: "MCO:REG:NOT NEEDED", d: "No registration needed (i.e. GW)" }, + { re: "!MCO:SND:NODE NOT REG", d: "Node is not registered, cannot send message" }, + { re: "MCO:PIM:NODE REG=(\\d+)", d: "Registration response received, registration status $1" }, + { re: "MCO:PIM:ROUTE N=(\\d+),R=(\\d+)", d: "Routing table, messages to node $1 are routed via node $2" }, + { re: "MCO:SLP:MS=(\\d+),SMS=(\\d+),I1=(\\d+),M1=(\\d+),I2=(\\d+),M2=(\\d+)", d: "Sleep node, duration $1 ms, SmartSleep=$2, Int1=$3, Mode1=$4, Int2=$5, Mode2=$6" }, + { re: "MCO:SLP:MS=(\\d+)", d: "Sleep node, duration $1 ms" }, + { re: "MCO:SLP:TPD", d: "Sleep node, powerdown transport" }, + { re: "MCO:SLP:WUP=(-?\\d+)", d: "Node woke-up, reason/IRQ=$1 (-2=not possible, -1=timer, >=0 IRQ)" }, + { re: "!MCO:SLP:FWUPD", d: "Sleeping not possible, FW update ongoing" }, + { re: "!MCO:SLP:REP", d: "Sleeping not possible, repeater feature enabled" }, + { re: "!MCO:SLP:TNR", d: " Transport not ready, attempt to reconnect until timeout" }, + { re: "MCO:NLK:NODE LOCKED. UNLOCK: GND PIN (\\d+) AND RESET", d: "Node locked during booting, see signing documentation for additional information" }, + { re: "MCO:NLK:TPD", d: "Powerdown transport" }, + { re: "TSM:INIT", d: "Transition to Init state" }, + { re: "TSM:INIT:STATID=(\\d+)", d: "Init static node id $1" }, + { re: "TSM:INIT:TSP OK", d: "Transport device configured and fully operational" }, + { re: "TSM:INIT:GW MODE", d: "Node is set up as GW, thus omitting ID and findParent states" }, + { re: "!TSM:INIT:TSP FAIL", d: "Transport device initialization failed" }, + { re: "TSM:FPAR", d: "Transition to Find Parent state" }, + { re: "TSM:FPAR:STATP=(\\d+)", d: "Static parent $1 has been set, skip finding parent" }, + { re: "TSM:FPAR:OK", d: "Parent node identified" }, + { re: "!TSM:FPAR:NO REPLY", d: "No potential parents replied to find parent request" }, + { re: "!TSM:FPAR:FAIL", d: "Finding parent failed" }, + { re: "TSM:ID", d: "Transition to Request Id state" }, + { re: "TSM:ID:OK,ID=(\\d+)", d: "Node id $1 is valid" }, + { re: "TSM:ID:REQ", d: "Request node id from controller" }, + { re: "!TSM:ID:FAIL", d: "Did not receive a node id from controller. Is your controller connected and correctly configured?" }, + { re: "!TSM:ID:FAIL,ID=(\\d+)", d: "Id verification failed, $1 is invalid" }, + { re: "TSM:UPL", d: "Transition to Check Uplink state" }, + { re: "TSM:UPL:OK", d: "Uplink OK, GW returned ping" }, + { re: "!TSM:UPL:FAIL", d: "Uplink check failed, i.e. GW could not be pinged" }, + { re: "TSM:READY:NWD REQ", d: "Send transport network discovery request" }, + { re: "TSM:READY:SRT", d: "Save routing table" }, + { re: "TSM:READY:ID=(\\d+),PAR=(\\d+),DIS=(\\d+)", d: "Transport ready, node id $1, parent node id $2, distance to GW is $3" }, + { re: "!TSM:READY:UPL FAIL,SNP", d: "Too many failed uplink transmissions, search new parent" }, + { re: "!TSM:READY:FAIL,STATP", d: "Too many failed uplink transmissions, static parent enforced" }, + { re: "TSM:READY", d: "Transition to Ready state" }, + { re: "TSM:FAIL:DIS", d: "Disable transport" }, + { re: "TSM:FAIL:CNT=(\\d+)", d: "Transition to Failure state, consecutive failure counter is $1" }, + { re: "TSM:FAIL:PDT", d: "Power-down transport" }, + { re: "TSM:FAIL:RE-INIT", d: "Attempt to re-initialize transport" }, + { re: "TSF:CKU:OK,FCTRL", d: "Uplink OK, flood control prevents pinging GW in too short intervals" }, + { re: "TSF:CKU:OK", d: "Uplink OK" }, + { re: "TSF:CKU:DGWC,O=(\\d+),N=(\\d+)", d: "Uplink check revealed changed network topology, old distance $1, new distance $2" }, + { re: "TSF:CKU:FAIL", d: "No reply received when checking uplink" }, + { re: "TSF:SID:OK,ID=(\\d+)", d: "Node id $1 assigned" }, + { re: "!TSF:SID:FAIL,ID=(\\d+)", d: "Assigned id $1 is invalid" }, + { re: "TSF:PNG:SEND,TO=(\\d+)", d: "Send ping to destination $1" }, + { re: "!TSF:PNG:ACTIVE", d: "Ping active, cannot start new ping" }, + { re: "TSF:WUR:MS=(\\d+)", d: "Wait until transport ready, timeout $1" }, + { re: "TSF:MSG:ECHO REQ", d: "ECHO message requested" }, + { re: "TSF:MSG:ECHO", d: "ECHO message, do not proceed but forward to callback" }, + { re: "TSF:MSG:FPAR RES,ID=(\\d+),D=(\\d+)", d: "Response to find parent request received from node $1 with distance $2 to GW" }, + { re: "TSF:MSG:FPAR PREF FOUND", d: "Preferred parent found, i.e. parent defined via MY_PARENT_NODE_ID" }, + { re: "TSF:MSG:FPAR OK,ID=(\\d+),D=(\\d+)", d: "Find parent response from node $1 is valid, distance $2 to GW" }, + { re: "!TSF:MSG:FPAR INACTIVE", d: "Find parent response received, but no find parent request active, skip response" }, + { re: "TSF:MSG:FPAR REQ,ID=(\\d+)", d: "Find parent request from node $1" }, + { re: "TSF:MSG:PINGED,ID=(\\d+),HP=(\\d+)", d: "Node pinged by node $1 with $2 hops" }, + { re: "TSF:MSG:PONG RECV,HP=(\\d+)", d: "Pinged node replied with $1 hops" }, + { re: "!TSF:MSG:PONG RECV,INACTIVE", d: "Pong received, but !pingActive" }, + { re: "TSF:MSG:BC", d: "Broadcast message received" }, + { re: "TSF:MSG:GWL OK", d: "Link to GW ok" }, + { re: "TSF:MSG:FWD BC MSG", d: "Controlled broadcast message forwarding" }, + { re: "TSF:MSG:REL MSG", d: "Relay message" }, + { re: "TSF:MSG:REL PxNG,HP=(\\d+)", d: "Relay PING/PONG message, increment hop counter to $1" }, + { re: "!TSF:MSG:LEN,(\\d+)!=(\\d+)", d: "Invalid message length, $1 (actual) != $2 (expected)" }, + { re: "!TSF:MSG:PVER,(\\d+)!=(\\d+)", d: "Message protocol version mismatch, $1 (actual) != $2 (expected)" }, + { re: "!TSF:MSG:SIGN VERIFY FAIL", d: "Signing verification failed" }, + { re: "!TSF:MSG:REL MSG,NORP", d: "Node received a message for relaying, but node is not a repeater, message skipped" }, + { re: "!TSF:MSG:SIGN FAIL", d: "Signing message failed" }, + { re: "!TSF:MSG:GWL FAIL", d: "GW uplink failed" }, + { re: "!TSF:MSG:ID TK INVALID", d: "Token for ID request invalid" }, + { re: "TSF:SAN:OK", d: "Sanity check passed" }, + { re: "!TSF:SAN:FAIL", d: "Sanity check failed, attempt to re-initialize radio" }, + { re: "TSF:CRT:OK", d: "Clearing routing table successful" }, + { re: "TSF:LRT:OK", d: "Loading routing table successful" }, + { re: "TSF:SRT:OK", d: "Saving routing table successful" }, + { re: "!TSF:RTE:FPAR ACTIVE", d: "Finding parent active, message not sent" }, + { re: "!TSF:RTE:DST (\\d+) UNKNOWN", d: "Routing for destination $1 unknown, sending message to parent" }, + { re: "!TSF:RTE:N2N FAIL", d: "Direct node-to-node communication failed - handing over to parent" }, + { re: "TSF:RRT:ROUTE N=(\\d+),R=(\\d+)", d: "Routing table, messages to node ($1) are routed via node ($2)"}, + { re: "!TSF:SND:TNR", d: "Transport not ready, message cannot be sent" }, + { re: "TSF:TDI:TSL", d: "Set transport to sleep" }, + { re: "TSF:TDI:TPD", d: "Power down transport" }, + { re: "TSF:TRI:TRI", d: "Reinitialise transport" }, + { re: "TSF:TRI:TSB", d: "Set transport to standby" }, + { re: "TSF:TRI:TPU", d: "Power up transport" }, + { re: "TSF:SIR:CMD=(\\d+),VAL=(\\d+)", d: "Get signal report $1, value: $2" }, + { re: "TSF:MSG:READ,(\\d+)-(\\d+)-(\\d+),s=(\\d+),c=(\\d+),t=(\\d+),pt=(\\d+),l=(\\d+),sg=(\\d+):(.*)", d: "Received Message
Sender: $1
Last Node: $2
Destination: $3
Sensor Id: $4
Command: {command:$5}
Message Type: {type:$5:$6}
Payload Type: {pt:$7}
Payload Length: $8
Signing: $9
Payload: $10" }, + { re: "TSF:MSG:SEND,(\\d+)-(\\d+)-(\\d+)-(\\d+),s=(\\d+),c=(\\d+),t=(\\d+),pt=(\\d+),l=(\\d+),sg=(\\d+),ft=(\\d+),st=(\\w+):(.*)", d: "Sent Message
Sender: $1
Last Node: $2
Next Node: $3
Destination: $4
Sensor Id: $5
Command: {command:$6}
Message Type:{type:$6:$7}
Payload Type: {pt:$8}
Payload Length: $9
Signing: $10
Failed uplink counter: $11
Status: $12 (OK=success, NACK=no radio ACK received)
Payload: $13" }, + { re: "!TSF:MSG:SEND,(\\d+)-(\\d+)-(\\d+)-(\\d+),s=(\\d+),c=(\\d+),t=(\\d+),pt=(\\d+),l=(\\d+),sg=(\\d+),ft=(\\d+),st=(\\w+):(.*)", d: "Sent Message
Sender: $1
Last Node: $2
Next Node: $3
Destination: $4
Sensor Id: $5
Command: {command:$6}
Message Type:{type:$6:$7}
Payload Type: {pt:$8}
Payload Length: $9
Signing: $10
Failed uplink counter: $11
Status: $12 (OK=success, NACK=no radio ACK received)
Payload: $13" }, + { re: "\\?TSF:MSG:SEND,(\\d+)-(\\d+)-(\\d+)-(\\d+),s=(\\d+),c=(\\d+),t=(\\d+),pt=(\\d+),l=(\\d+),sg=(\\d+),ft=(\\d+),st=(\\w+):(.*)", d: "Sent Message without radio ACK
Sender: $1
Last Node: $2
Next Node: $3
Destination: $4
Sensor Id: $5
Command: {command:$6}
Message Type:{type:$6:$7}
Payload Type: {pt:$8}
Payload Length: $9
Signing: $10
Failed uplink counter: $11
Status: $12 (OK=success, NACK=no radio ACK received)
Payload: $13" }, + + // transport HAL + + { re: "THA:INIT", d: "Initialise transport HAL" }, + { re: "THA:INIT:PSK=(.*)", d: "Initialise transport HAL, PSK=$1" }, + { re: "THA:SAD:ADDR=(\\d+)", d: "Set transport address: $1" }, + { re: "THA:GAD:ADDR=(\\d+)", d: "Get transport address: $1" }, + { re: "THA:DATA:AVAIL", d: "Transport HAL received data" }, + { re: "THA:SAN:RES=(\\d+)", d: "Transport sanity check, result=$1 (0=NOK, 1=OK)" }, + { re: "THA:RCV:MSG=(.*)", d: "Message received: $1" }, + { re: "THA:RCV:DECRYPT", d: "Decrypt message" }, + { re: "THA:RCV:PLAIN=(.*)", d: "Message plaint text: $1" }, + { re: "!THA:RCV:PVER=(\\d+)", d: "Message protocol version mismatch: $1" }, + { re: "!THA:RCV:LEN=(\\d+),EXP=(\\d+)", d: "Invalid message length, actual $1, expected $2" }, + { re: "THA:RCV:MSG LEN=(\\d+)", d: "Length of received message: $1" }, + { re: "THA:SND:MSG=(.*)", d: "Send message: $1" }, + { re: "THA:SND:ENCRYPT", d: "Encrypt message" }, + { re: "THA:SND:CIP=(.*)", d: "Ciphertext of encrypted message: $1" }, + { re: "THA:SND:MSG LEN=(\\d+),RES=(\\d+)", d: "Sending message with length=$1, result=$2 (0=NOK, 1=OK)" }, + + // Signing backend + + { re: "SGN:INI:BND OK", d: "Backend has initialized ok" }, + { re: "!SGN:INI:BND FAIL", d: "Backend has not initialized ok" }, + { re: "SGN:PER:OK", d: "Personalization data is ok" }, + { re: "!SGN:PER:TAMPERED", d: "Personalization data has been tampered" }, + { re: "SGN:PRE:SGN REQ", d: "Signing required" }, + { re: "SGN:PRE:SGN REQ,TO=(\\d+)", d: "Tell node $1 that we require signing" }, + { re: "SGN:PRE:SGN REQ,FROM=(\\d+)", d: " Node $1 require signing" }, + { re: "SGN:PRE:SGN NREQ", d: "Signing not required" }, + { re: "SGN:PRE:SGN REQ,TO=(\\d+)", d: "Tell node $1 that we do not require signing" }, + { re: "SGN:PRE:SGN NREQ,FROM=(\\d+)", d: "Node $1 does not require signing" }, + { re: "!SGN:PRE:SGN NREQ,FROM=(\\d+) REJ", d: "Node $1 does not require signing but used to (requirement remain unchanged)" }, + { re: "SGN:PRE:WHI REQ", d: "Whitelisting required" }, + { re: "SGN:PRE:WHI REQ;TO=(\\d+)", d: "Tell $1 that we require whitelisting" }, + { re: "SGN:PRE:WHI REQ,FROM=(\\d+)", d: "Node $1 require whitelisting" }, + { re: "SGN:PRE:WHI NREQ", d: " Whitelisting not required" }, + { re: "SGN:PRE:WHI NREQ,TO=(\\d+)", d: "Tell node $1 that we do not require whitelisting" }, + { re: "SGN:PRE:WHI NREQ,FROM=(\\d+)", d: "Node $1 does not require whitelisting" }, + { re: "!SGN:PRE:WHI NREQ,FROM=(\\d+) REJ", d: "Node $1 does not require whitelisting but used to (requirement remain unchanged)" }, + { re: "SGN:PRE:XMT,TO=(\\d+)", d: "Presentation data transmitted to node $1" }, + { re: "!SGN:PRE:XMT,TO=(\\d+) FAIL", d: "Presentation data not properly transmitted to node $1" }, + { re: "SGN:PRE:WAIT GW", d: "Waiting for gateway presentation data" }, + { re: "!SGN:PRE:VER=(\\d+)", d: "Presentation version $1 is not supported" }, + { re: "SGN:PRE:NSUP", d: "Received signing presentation but signing is not supported" }, + { re: "SGN:PRE:NSUP,TO=(\\d+)", d: "Informing node $1 that we do not support signing" }, + { re: "SGN:SGN:NCE REQ,TO=(\\d+)", d: "Nonce request transmitted to node $1" }, + { re: "!SGN:SGN:NCE REQ,TO=(\\d+) FAIL", d: "Nonce request not properly transmitted to node $1" }, + { re: "!SGN:SGN:NCE TMO", d: "Timeout waiting for nonce" }, + { re: "SGN:SGN:SGN", d: "Message signed" }, + { re: "!SGN:SGN:SGN FAIL", d: "Message failed to be signed" }, + { re: "SGN:SGN:NREQ=(\\d+)", d: "Node $1 does not require signed messages" }, + { re: "SGN:SGN:(\\d+)!=(\\d+) NUS", d: "Will not sign because $1 is not $2 (repeater)" }, + { re: "!SGN:SGN:STATE", d: "Security system in a invalid state (personalization data tampered)" }, + { re: "!SGN:VER:NSG", d: "Message was not signed, but it should have been" }, + { re: "!SGN:VER:FAIL", d: "Verification failed" }, + { re: "SGN:VER:OK", d: "Verification succeeded" }, + { re: "SGN:VER:LEFT=(\\d+)", d: "$1 number of failed verifications left in a row before node is locked" }, + { re: "!SGN:VER:STATE", d: "Security system in a invalid state (personalization data tampered)" }, + { re: "SGN:SKP:MSG CMD=(\\d+),TYPE=(\\d+)", d: "Message with command $1 and type $2 does not need to be signed" }, + { re: "SGN:SKP:ECHO CMD=(\\d+),TYPE=(\\d+)", d: "ECHO messages do not need to be signed" }, + { re: "SGN:NCE:LEFT=(\\d+)", d: "$1 number of nonce requests between successful verifications left before node is locked" }, + { re: "SGN:NCE:XMT,TO=(\\d+)", d: "Nonce data transmitted to node $1" }, + { re: "!SGN:NCE:XMT,TO=(\\d+) FAIL", d: "Nonce data not properly transmitted to node $1" }, + { re: "!SGN:NCE:GEN", d: "Failed to generate nonce" }, + { re: "SGN:NCE:NSUP (DROPPED)", d: "Ignored nonce/request for nonce (signing not supported)" }, + { re: "SGN:NCE:FROM=(\\d+)", d: "Received nonce from node $1" }, + { re: "SGN:NCE:(\\d+)!=(\\d+) (DROPPED)", d: "Ignoring nonce as it did not come from the desgination of the message to sign" }, + { re: "!SGN:BND:INIT FAIL", d: "Failed to initialize signing backend" }, + { re: "!SGN:BND:PWD<8", d: "Signing password too short" }, + { re: "!SGN:BND:PER", d: "Backend not personalized" }, + { re: "!SGN:BND:SER", d: "Could not get device unique serial from backend" }, + { re: "!SGN:BND:TMR", d: "Backend timed out" }, + { re: "!SGN:BND:SIG,SIZE,(\\d+)>(\\d+)", d: "Refusing to sign message with length $1 because it is bigger than allowed size $2 " }, + { re: "SGN:BND:SIG WHI,ID=(\\d+)", d: "Salting message with our id $1" }, + { re: "SGN:BND:SIG WHI,SERIAL=(.*)", d: "Salting message with our serial $1" }, + { re: "!SGN:BND:VER ONGOING", d: "Verification failed, no ongoing session" }, + { re: "!SGN:BND:VER,IDENT=(\\d+)", d: "Verification failed, identifier $1 is unknown" }, + { re: "SGN:BND:VER WHI,ID=(\\d+)", d: "Id $1 found in whitelist" }, + { re: "SGN:BND:VER WHI,SERIAL=(.*)", d: "Expecting serial $1 for this sender" }, + { re: "!SGN:BND:VER WHI,ID=(\\d+) MISSING", d: "Id $1 not found in whitelist" }, + { re: "SGN:BND:NONCE=(.*)", d: "Calculating signature using nonce $1" }, + { re: "SGN:BND:HMAC=(.*)", d: "Calculated signature is $1" }, + + // NodeManager + + { re: "NM:INIT:VER=(.*)", d: "NodeManager version $1" }, + { re: "NM:INIT:INO=(.*)", d: "Sketch $1" }, + { re: "NM:INIT:LIB VER=(.+) CP=(.+)", d: "MySensors Library version $1, capabilities $2" }, + { re: "NM:INIT:RBT p=(\\d+)", d: "Configured reboot pin $1" }, + { re: "NM:BFR:INIT", d: "Connecting to the gateway..." }, + { re: "NM:BFR:OK", d: "Connection to the gateway successful" }, + { re: "NM:BFR:INT p=(\\d+) m=(\\d+)", d: "Setting up interrupt on pin $1 with mode $2" }, + { re: "NM:PRES:(\\w+)\\((\\d+)\\) p=(\\d+) t=(\\d+)", d: "Presented to the gateway child $2 for sensor $1 as {type:0:$3} with type {type:1:$4}" }, + { re: "NM:STP:ID=(\\d+) M=(\\d+)", d: "This node has id $1 and metric is set to $2" }, + { re: "NM:STP:SD T=(\\d+)", d: "Connected SD card reader, type $1" }, + { re: "NM:STP:HW V=(\\d+) F=(\\d+) M=(\\d+)", d: "CPU Vcc is $1 mV, CPU frequency $2 Mhz, free memory $3 bytes" }, + { re: "NM:LOOP:(\\w+)\\((\\d+)\\):SET t=(\\d+) v=(.+)", d: "New value for child $2 of sensor $1 with {type:1:$3} = $4" }, + { re: "NM:LOOP:INT p=(\\d+) v=(\\d+)", d: "Interrupt received on pin $1, value $2" }, + { re: "NM:LOOP:INPUT\\.\\.\\.", d: "Waiting for input from the serial port" }, + { re: "NM:LOOP:INPUT v=(.*)", d: "Received an input from the serial port: $1" }, + { re: "NM:TIME:REQ", d: "Requesting the time to the controller" }, + { re: "NM:TIME:OK ts=(\\d+)", d: "Received the time from the controller: $1" }, + { re: "NM:SLP:WKP", d: "Wakeup requested" }, + { re: "NM:SLP:SLEEP s=(\\d+)", d: "Going to sleep for $1 seconds" }, + { re: "NM:SLP:AWAKE", d: "Waking up from sleep" }, + { re: "NM:SLP:LOAD s=(\\d+)", d: "Loaded configured sleep time: $1 seconds" }, + { re: "NM:MSG:SEND\\((\\d+)\\) t=(\\d+) p=(.+)", d: "Child $1 sent {type:1:$2} = $3" }, + { re: "NM:MSG:RECV\\((\\d+)\\) c=(\\d+) t=(\\d+) p=(.+)", d: "Received a {command:$2} message for child $1 with {type:$2:$3} = $4" }, + { re: "NM:PWR:RBT", d: "Rebooting the node as requested" }, + { re: "NM:PWR:ON p=(\\d+)", d: "Powering on the sensor(s) through pin $1" }, + { re: "NM:PWR:OFF p=(\\d+)", d: "Powering off the sensor(s) through pin $1" }, + { re: "NM:OTA:REQ f=(\\d+) v=(\\d+)", d: "Over-the-air configuration change requested, function $1 value $2" }, + { re: "NM:EEPR:CLR", d: "Clearing the EEPROM as requested" }, + { re: "NM:EEPR:LOAD i=(\\d+) v=(\\d+)", d: "Read from EEPROM at position $1 the value $2" }, + { re: "NM:EEPR:SAVE i=(\\d+) v=(\\d+)", d: "Wrote to EEPROM at position $1 the value $2" }, + { re: "NM:EEPR:(\\w+)\\((\\d+)\\):LOAD", d: "Restoring from EEPROM the value of child $2 of sensor $1" }, + { re: "NM:EEPR:(\\w+)\\((\\d+)\\):SAVE", d: "Saving to EEPROM the value of child $2 of sensor $1$1: $2" }, + { re: "!NM:SENS:([^:]+):(.+)", d: "Error in sensor $1: $2" }, +]; + + + +// Init regexes +for (var i=0, len=match.length;i 16 - ldi r22, lo8(16) - ldi r23, hi8(16) - push r27 - push r26 - call uart_hexdump - pop r26 - pop r27 - adiw r26, 16 - hexdump \length-16 -.else - ldi r22, lo8(\length) - ldi r23, hi8(\length) - call uart_hexdump -.endif -.endm - -/* X points to Block */ -.macro dbg_hexdump length - precall - hexdump \length - postcall -.endm - -.section .text - -SPL = 0x3D -SPH = 0x3E -SREG = 0x3F - - -; -;sha256_ctx_t is: -; -; [h0][h1][h2][h3][h4][h5][h6][h7][length] -; hn is 32 bit large, length is 64 bit large - -;########################################################### - -.global sha256_ctx2hash -; === sha256_ctx2hash === -; this function converts a state into a normal hash (bytestring) -; param1: the 16-bit destination pointer -; given in r25,r24 (r25 is most significant) -; param2: the 16-bit pointer to sha256_ctx structure -; given in r23,r22 -sha256_ctx2hash: - movw r26, r22 - movw r30, r24 - ldi r21, 8 - sbiw r26, 4 -1: - ldi r20, 4 - adiw r26, 8 -2: - ld r0, -X - st Z+, r0 - dec r20 - brne 2b - - dec r21 - brne 1b - - ret - -;########################################################### - -.global sha256 -; === sha256 === -; this function calculates SHA-256 hashes from messages in RAM -; param1: the 16-bit hash destination pointer -; given in r25,r24 (r25 is most significant) -; param2: the 16-bit pointer to message -; given in r23,r22 -; param3: 32-bit length value (length of message in bits) -; given in r21,r20,r19,r18 -sha256: -sha256_prolog: - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r16 - push r17 - in r30, SPL - in r31, SPH - sbiw r30, 8*4+8 - in r0, SREG - cli - out SPL, r30 - out SREG, r0 - out SPH, r31 - - push r25 - push r24 - adiw r30, 1 - movw r16, r30 - movw r8, r18 /* backup of length*/ - movw r10, r20 - - movw r12, r22 /* backup pf msg-ptr */ - - movw r24, r16 - rcall sha256_init - /* if length > 0xffff */ -1: - tst r11 - brne 2f - tst r10 - breq 4f -2: - movw r24, r16 - movw r22, r12 - rcall sha256_nextBlock - ldi r19, 64 - add r12, r19 - adc r13, r1 - /* length -= 512 */ - ldi r19, 0x02 - sub r9, r19 - sbc r10, r1 - sbc r11, r1 - rjmp 1b - -4: - movw r24, r16 - movw r22, r12 - movw r20, r8 - rcall sha256_lastBlock - - pop r24 - pop r25 - movw r22, r16 - rcall sha256_ctx2hash - -sha256_epilog: - in r30, SPL - in r31, SPH - adiw r30, 8*4+8 - in r0, SREG - cli - out SPL, r30 - out SREG, r0 - out SPH, r31 - pop r17 - pop r16 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - ret - -;########################################################### - - -; block MUST NOT be larger than 64 bytes - -.global sha256_lastBlock -; === sha256_lastBlock === -; this function does padding & Co. for calculating SHA-256 hashes -; param1: the 16-bit pointer to sha256_ctx structure -; given in r25,r24 (r25 is most significant) -; param2: an 16-bit pointer to 64 byte block to hash -; given in r23,r22 -; param3: an 16-bit integer specifing length of block in bits -; given in r21,r20 -sha256_lastBlock_localSpace = (SHA256_BLOCK_BITS/8+1) - - -sha256_lastBlock: - cpi r21, 0x02 - brlo sha256_lastBlock_prolog - push r25 - push r24 - push r23 - push r22 - push r21 - push r20 - rcall sha256_nextBlock - pop r20 - pop r21 - pop r22 - pop r23 - pop r24 - pop r25 - subi r21, 0x02 - ldi r19, 64 - add r22, r19 - adc r23, r1 - rjmp sha256_lastBlock -sha256_lastBlock_prolog: - /* allocate space on stack */ - in r30, SPL - in r31, SPH - in r0, SREG - subi r30, lo8(64) - sbci r31, hi8(64) - cli - out SPL, r30 - out SREG,r0 - out SPH, r31 - - adiw r30, 1 /* SP points to next free byte on stack */ - mov r18, r20 /* r20 = LSB(length) */ - lsr r18 - lsr r18 - lsr r18 - bst r21, 0 /* may be we should explain this ... */ - bld r18, 5 /* now: r18 == length/8 (aka. length in bytes) */ - - - movw r26, r22 /* X points to begin of msg */ - tst r18 - breq sha256_lastBlock_post_copy - mov r1, r18 -sha256_lastBlock_copy_loop: - ld r0, X+ - st Z+, r0 - dec r1 - brne sha256_lastBlock_copy_loop -sha256_lastBlock_post_copy: -sha256_lastBlock_insert_stuffing_bit: - ldi r19, 0x80 - mov r0,r19 - ldi r19, 0x07 - and r19, r20 /* if we are in bitmode */ - breq 2f /* no bitmode */ -1: - lsr r0 - dec r19 - brne 1b - ld r19, X -/* maybe we should do some ANDing here, just for safety */ - or r0, r19 -2: - st Z+, r0 - inc r18 - -/* checking stuff here */ - cpi r18, 64-8+1 - brsh 0f - rjmp sha256_lastBlock_insert_zeros -0: - /* oh shit, we landed here */ - /* first we have to fill it up with zeros */ - ldi r19, 64 - sub r19, r18 - breq 2f -1: - st Z+, r1 - dec r19 - brne 1b -2: - sbiw r30, 63 - sbiw r30, 1 - movw r22, r30 - - push r31 - push r30 - push r25 - push r24 - push r21 - push r20 - rcall sha256_nextBlock - pop r20 - pop r21 - pop r24 - pop r25 - pop r30 - pop r31 - - /* now we should subtract 512 from length */ - movw r26, r24 - adiw r26, 4*8+1 /* we can skip the lowest byte */ - ld r19, X - subi r19, hi8(512) - st X+, r19 - ldi r18, 6 -1: - ld r19, X - sbci r19, 0 - st X+, r19 - dec r18 - brne 1b - -; clr r18 /* not neccessary ;-) */ - /* reset Z pointer to begin of block */ - -sha256_lastBlock_insert_zeros: - ldi r19, 64-8 - sub r19, r18 - breq sha256_lastBlock_insert_length - clr r1 -1: - st Z+, r1 /* r1 is still zero */ - dec r19 - brne 1b - -; rjmp sha256_lastBlock_epilog -sha256_lastBlock_insert_length: - movw r26, r24 /* X points to state */ - adiw r26, 8*4 /* X points to (state.length) */ - adiw r30, 8 /* Z points one after the last byte of block */ - ld r0, X+ - add r0, r20 - st -Z, r0 - ld r0, X+ - adc r0, r21 - st -Z, r0 - ldi r19, 6 -1: - ld r0, X+ - adc r0, r1 - st -Z, r0 - dec r19 - brne 1b - - sbiw r30, 64-8 - movw r22, r30 - rcall sha256_nextBlock - -sha256_lastBlock_epilog: - in r30, SPL - in r31, SPH - in r0, SREG - adiw r30, 63 ; lo8(64) - adiw r30, 1 ; hi8(64) - cli - out SPL, r30 - out SREG,r0 - out SPH, r31 - clr r1 - ret - -/**/ -;########################################################### - -.global sha256_nextBlock -; === sha256_nextBlock === -; this is the core function for calculating SHA-256 hashes -; param1: the 16-bit pointer to sha256_ctx structure -; given in r25,r24 (r25 is most significant) -; param2: an 16-bit pointer to 64 byte block to hash -; given in r23,r22 -sha256_nextBlock_localSpace = (64+8)*4 ; 64 32-bit values for w array and 8 32-bit values for a array (total 288 byte) - -Bck1 = 12 -Bck2 = 13 -Bck3 = 14 -Bck4 = 15 -Func1 = 22 -Func2 = 23 -Func3 = 24 -Func4 = 25 -Accu1 = 16 -Accu2 = 17 -Accu3 = 18 -Accu4 = 19 -XAccu1 = 8 -XAccu2 = 9 -XAccu3 = 10 -XAccu4 = 11 -T1 = 4 -T2 = 5 -T3 = 6 -T4 = 7 -LoopC = 1 -/* byteorder: high number <--> high significance */ -sha256_nextBlock: - ; initial, let's make some space ready for local vars - push r4 /* replace push & pop by mem ops? */ - push r5 - push r6 - push r7 - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 - push r16 - push r17 - push r28 - push r29 - in r20, SPL - in r21, SPH - movw r18, r20 ;backup SP -; movw r26, r20 ; X points to free space on stack - movw r30, r22 ; Z points to message - subi r20, lo8(sha256_nextBlock_localSpace) ;sbiw can do only up to 63 - sbci r21, hi8(sha256_nextBlock_localSpace) - movw r26, r20 ; X points to free space on stack - in r0, SREG - cli ; we want to be uninterrupted while updating SP - out SPL, r20 - out SREG, r0 - out SPH, r21 - push r18 - push r19 - push r24 - push r25 /* param1 will be needed later */ - ; now we fill the w array with message (think about endianess) - adiw r26, 1 ; X++ - ldi r20, 16 -sha256_nextBlock_wcpyloop: - ld r23, Z+ - ld r22, Z+ - ld r19, Z+ - ld r18, Z+ - st X+, r18 - st X+, r19 - st X+, r22 - st X+, r23 - dec r20 - brne sha256_nextBlock_wcpyloop -/* for (i=16; i<64; ++i){ - w[i] = SIGMA_b(w[i-2]) + w[i-7] + SIGMA_a(w[i-15]) + w[i-16]; - } */ - /* r25,r24,r23,r24 (r21,r20) are function values - r19,r18,r17,r16 are the accumulator - r15,r14,r13,rBck1 are backup1 - r11,r10,r9 ,r8 are xor accu - r1 is round counter */ - - ldi r20, 64-16 - mov LoopC, r20 -sha256_nextBlock_wcalcloop: - movw r30, r26 ; cp X to Z - sbiw r30, 63 - sbiw r30, 1 ; substract 64 = 16*4 - ld Accu1, Z+ - ld Accu2, Z+ - ld Accu3, Z+ - ld Accu4, Z+ /* w[i] = w[i-16] */ - ld Bck1, Z+ - ld Bck2, Z+ - ld Bck3, Z+ - ld Bck4, Z+ /* backup = w[i-15] */ - /* now sigma 0 */ - mov Func1, Bck2 - mov Func2, Bck3 - mov Func3, Bck4 - mov Func4, Bck1 /* prerotated by 8 */ - ldi r20, 1 - rcall bitrotl - movw XAccu1, Func1 - movw XAccu3, Func3 /* store ROTR(w[i-15],7) in xor accu */ - movw Func1, Bck3 - movw Func3, Bck1 /* prerotated by 16 */ - ldi r20, 2 - rcall bitrotr - eor XAccu1, Func1 /* xor ROTR(w[i-15], 18)*/ - eor XAccu2, Func2 - eor XAccu3, Func3 - eor XAccu4, Func4 - ldi Func2, 3 /* now shr3 */ /*we can destroy backup now*/ -sigma0_shr: - lsr Bck4 - ror Bck3 - ror Bck2 - ror Bck1 - dec Func2 - brne sigma0_shr - eor XAccu1, Bck1 - eor XAccu2, Bck2 - eor XAccu3, Bck3 - eor XAccu4, Bck4 /* xor SHR(w[i-15], 3)*/ /* xor accu == sigma1(w[i-15]) */ - add Accu1, XAccu1 - adc Accu2, XAccu2 - adc Accu3, XAccu3 - adc Accu4, XAccu4 /* finished with sigma0 */ - ldd Func1, Z+7*4 /* now accu += w[i-7] */ - ldd Func2, Z+7*4+1 - ldd Func3, Z+7*4+2 - ldd Func4, Z+7*4+3 - add Accu1, Func1 - adc Accu2, Func2 - adc Accu3, Func3 - adc Accu4, Func4 - ldd Bck1, Z+12*4 /* now backup = w[i-2]*/ - ldd Bck2, Z+12*4+1 - ldd Bck3, Z+12*4+2 - ldd Bck4, Z+12*4+3 - /* now sigma 1 */ - movw Func1, Bck3 - movw Func3, Bck1 /* prerotated by 16 */ - ldi r20, 1 - rcall bitrotr - movw XAccu3, Func3 - movw XAccu1, Func1 /* store in ROTR(w[i-2], 17) xor accu */ -; movw Func1, Bck3 -; movw Func3, Bck1 /* prerotated by 16 */ - ldi r20, 2 - rcall bitrotr - eor XAccu1, Func1 /* xor ROTR(w[i-2], 19)*/ - eor XAccu2, Func2 - eor XAccu3, Func3 - eor XAccu4, Func4 - ldi Func2, 2 /* now shr10 (dirty trick, skipping a byte) */ /*we can destroy backup now*/ -sigma1_shr: - lsr Bck4 - ror Bck3 - ror Bck2 - dec Func2 - brne sigma1_shr - eor XAccu1, Bck2 - eor XAccu2, Bck3 - eor XAccu3, Bck4 /* xor SHR(w[i-2], 10)*/ /* xor accu == sigma1(w[i-15]) */ - add Accu1, XAccu1 - adc Accu2, XAccu2 - adc Accu3, XAccu3 - adc Accu4, XAccu4 /* finished with sigma0 */ - /* now let's store the shit */ - st X+, Accu1 - st X+, Accu2 - st X+, Accu3 - st X+, Accu4 - dec LoopC - breq 3f ; skip if zero - rjmp sha256_nextBlock_wcalcloop -3: - /* we are finished with w array X points one byte post w */ -/* init a array */ - pop r31 - pop r30 - push r30 - push r31 - ldi r25, 8*4 /* 8 32-bit values to copy from ctx to a array */ -init_a_array: - ld r1, Z+ - st X+, r1 - dec r25 - brne init_a_array - -/* now the real fun begins */ -/* for (i=0; i<64; ++i){ - t1 = a[7] + SIGMA1(a[4]) + CH(a[4],a[5],a[6]) + k[i] + w[i]; - t2 = SIGMA0(a[0]) + MAJ(a[0],a[1],a[2]); - memmove(&(a[1]), &(a[0]), 7*4); // a[7]=a[6]; a[6]=a[5]; a[5]=a[4]; a[4]=a[3]; a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; - a[4] += t1; - a[0] = t1 + t2; - } */ - /* Y points to a[0], Z ('cause lpm wants it) points to k[i], X points to w[i] */ - sbiw r26, 8*4 /* X still points at a[7]+1*/ - movw r28, r26 - ldi r30, lo8(sha256_kv) - ldi r31, hi8(sha256_kv) - dec r27 /* X - (64*4 == 256) */ - ldi r25, 64 - mov LoopC, r25 -sha256_main_loop: - /* now calculate t1 */ - /*CH(x,y,z) = (x&y)^((~x)&z)*/ - ldd T1, Y+5*4 - ldd T2, Y+5*4+1 - ldd T3, Y+5*4+2 - ldd T4, Y+5*4+3 /* y in T */ - ldd Func1, Y+4*4 - ldd Func2, Y+4*4+1 - ldd Func3, Y+4*4+2 - ldd Func4, Y+4*4+3 /* x in Func */ - ldd Bck1, Y+6*4 - ldd Bck2, Y+6*4+1 - ldd Bck3, Y+6*4+2 - ldd Bck4, Y+6*4+3 /* z in Bck */ - and T1, Func1 - and T2, Func2 - and T3, Func3 - and T4, Func4 - com Func1 - com Func2 - com Func3 - com Func4 - and Bck1, Func1 - and Bck2, Func2 - and Bck3, Func3 - and Bck4, Func4 - eor T1, Bck1 - eor T2, Bck2 - eor T3, Bck3 - eor T4, Bck4 /* done, CH(x,y,z) is in T */ - /* now SIGMA1(a[4]) */ - ldd Bck4, Y+4*4 /* think about using it from Func reg above*/ - ldd Bck1, Y+4*4+1 - ldd Bck2, Y+4*4+2 - ldd Bck3, Y+4*4+3 /* load prerotate by 8-bit */ - movw Func1, Bck1 - movw Func3, Bck3 - ldi r20, 2 - rcall bitrotl /* rotr(x,6) */ - movw XAccu1, Func1 - movw XAccu3, Func3 - movw Func1, Bck1 - movw Func3, Bck3 - ldi r20, 3 - rcall bitrotr /* rotr(x,11) */ - eor XAccu1, Func1 - eor XAccu2, Func2 - eor XAccu3, Func3 - eor XAccu4, Func4 - movw Func1, Bck3 /* this prerotates furteh 16 bits*/ - movw Func3, Bck1 /* so we have now prerotated by 24 bits*/ - ldi r20, 1 - rcall bitrotr /* rotr(x,11) */ - eor XAccu1, Func1 - eor XAccu2, Func2 - eor XAccu3, Func3 - eor XAccu4, Func4 /* finished with SIGMA1, add it to T */ - add T1, XAccu1 - adc T2, XAccu2 - adc T3, XAccu3 - adc T4, XAccu4 - /* now we've to add a[7], w[i] and k[i] */ - ldd XAccu1, Y+4*7 - ldd XAccu2, Y+4*7+1 - ldd XAccu3, Y+4*7+2 - ldd XAccu4, Y+4*7+3 - add T1, XAccu1 - adc T2, XAccu2 - adc T3, XAccu3 - adc T4, XAccu4 /* add a[7] */ - ld XAccu1, X+ - ld XAccu2, X+ - ld XAccu3, X+ - ld XAccu4, X+ - add T1, XAccu1 - adc T2, XAccu2 - adc T3, XAccu3 - adc T4, XAccu4 /* add w[i] */ - lpm XAccu1, Z+ - lpm XAccu2, Z+ - lpm XAccu3, Z+ - lpm XAccu4, Z+ - add T1, XAccu1 - adc T2, XAccu2 - adc T3, XAccu3 - adc T4, XAccu4 /* add k[i] */ /* finished with t1 */ - /*now t2 = SIGMA0(a[0]) + MAJ(a[0],a[1],a[2]) */ /*i did to much x86 asm, i always see 4 32bit regs*/ - /* starting with MAJ(x,y,z) */ - ldd Func1, Y+4*0+0 - ldd Func2, Y+4*0+1 - ldd Func3, Y+4*0+2 - ldd Func4, Y+4*0+3 /* load x=a[0] */ - ldd XAccu1, Y+4*1+0 - ldd XAccu2, Y+4*1+1 - ldd XAccu3, Y+4*1+2 - ldd XAccu4, Y+4*1+3 /* load y=a[1] */ - and XAccu1, Func1 - and XAccu2, Func2 - and XAccu3, Func3 - and XAccu4, Func4 /* XAccu == (x & y) */ - ldd Bck1, Y+4*2+0 - ldd Bck2, Y+4*2+1 - ldd Bck3, Y+4*2+2 - ldd Bck4, Y+4*2+3 /* load z=a[2] */ - and Func1, Bck1 - and Func2, Bck2 - and Func3, Bck3 - and Func4, Bck4 - eor XAccu1, Func1 - eor XAccu2, Func2 - eor XAccu3, Func3 - eor XAccu4, Func4 /* XAccu == (x & y) ^ (x & z) */ - ldd Func1, Y+4*1+0 - ldd Func2, Y+4*1+1 - ldd Func3, Y+4*1+2 - ldd Func4, Y+4*1+3 /* load y=a[1] */ - and Func1, Bck1 - and Func2, Bck2 - and Func3, Bck3 - and Func4, Bck4 - eor XAccu1, Func1 - eor XAccu2, Func2 - eor XAccu3, Func3 - eor XAccu4, Func4 /* XAccu == Maj(x,y,z) == (x & y) ^ (x & z) ^ (y & z) */ - /* SIGMA0(a[0]) */ - ldd Bck1, Y+4*0+0 /* we should combine this with above */ - ldd Bck2, Y+4*0+1 - ldd Bck3, Y+4*0+2 - ldd Bck4, Y+4*0+3 - movw Func1, Bck1 - movw Func3, Bck3 - ldi r20, 2 - rcall bitrotr - movw Accu1, Func1 - movw Accu3, Func3 /* Accu = shr(a[0], 2) */ - movw Func1, Bck3 - movw Func3, Bck1 /* prerotate by 16 bits */ - ldi r20, 3 - rcall bitrotl - eor Accu1, Func1 - eor Accu2, Func2 - eor Accu3, Func3 - eor Accu4, Func4 /* Accu ^= shr(a[0], 13) */ - mov Func1, Bck4 - mov Func2, Bck1 - mov Func3, Bck2 - mov Func4, Bck3 /* prerotate by 24 bits */ - ldi r20, 2 - rcall bitrotl - eor Accu1, Func1 - eor Accu2, Func2 - eor Accu3, Func3 - eor Accu4, Func4 /* Accu ^= shr(a[0], 22) */ - add Accu1, XAccu1 /* add previous result (MAJ)*/ - adc Accu2, XAccu2 - adc Accu3, XAccu3 - adc Accu4, XAccu4 - /* now we are finished with the computing stuff (t1 in T, t2 in Accu)*/ - /* a[7]=a[6]; a[6]=a[5]; a[5]=a[4]; a[4]=a[3]; a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; */ - - ldi r21, 7*4 - adiw r28, 7*4 -a_shift_loop: - ld r25, -Y /* warning: this is PREdecrement */ - std Y+4, r25 - dec r21 - brne a_shift_loop - - ldd Bck1, Y+4*4+0 - ldd Bck2, Y+4*4+1 - ldd Bck3, Y+4*4+2 - ldd Bck4, Y+4*4+3 - add Bck1, T1 - adc Bck2, T2 - adc Bck3, T3 - adc Bck4, T4 - std Y+4*4+0, Bck1 - std Y+4*4+1, Bck2 - std Y+4*4+2, Bck3 - std Y+4*4+3, Bck4 - add Accu1, T1 - adc Accu2, T2 - adc Accu3, T3 - adc Accu4, T4 - std Y+4*0+0, Accu1 - std Y+4*0+1, Accu2 - std Y+4*0+2, Accu3 - std Y+4*0+3, Accu4 /* a array updated */ - - - dec LoopC - breq update_state - rjmp sha256_main_loop ;brne sha256_main_loop -update_state: - /* update state */ - /* pointers to state should still exist on the stack ;-) */ - pop r31 - pop r30 - ldi r21, 8 -update_state_loop: - ldd Accu1, Z+0 - ldd Accu2, Z+1 - ldd Accu3, Z+2 - ldd Accu4, Z+3 - ld Func1, Y+ - ld Func2, Y+ - ld Func3, Y+ - ld Func4, Y+ - add Accu1, Func1 - adc Accu2, Func2 - adc Accu3, Func3 - adc Accu4, Func4 - st Z+, Accu1 - st Z+, Accu2 - st Z+, Accu3 - st Z+, Accu4 - dec r21 - brne update_state_loop - /* now we just have to update the length */ - adiw r30, 1 /* since we add 512, we can simply skip the LSB */ - ldi r21, 2 - ldi r22, 6 - ld r20, Z - add r20, r21 - st Z+, r20 - clr r21 -sha256_nextBlock_fix_length: - brcc sha256_nextBlock_epilog - ld r20, Z - adc r20, r21 - st Z+, r20 - dec r22 - brne sha256_nextBlock_fix_length - -; EPILOG -sha256_nextBlock_epilog: -/* now we should clean up the stack */ - - pop r21 - pop r20 - in r0, SREG - cli ; we want to be uninterrupted while updating SP - out SPL, r20 - out SREG, r0 - out SPH, r21 - clr r1 - pop r29 - pop r28 - pop r17 - pop r16 - pop r15 - pop r14 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - pop r7 - pop r6 - pop r5 - pop r4 - ret - -sha256_kv: ; round-key-vector stored in ProgMem -.word 0x2f98, 0x428a, 0x4491, 0x7137, 0xfbcf, 0xb5c0, 0xdba5, 0xe9b5, 0xc25b, 0x3956, 0x11f1, 0x59f1, 0x82a4, 0x923f, 0x5ed5, 0xab1c -.word 0xaa98, 0xd807, 0x5b01, 0x1283, 0x85be, 0x2431, 0x7dc3, 0x550c, 0x5d74, 0x72be, 0xb1fe, 0x80de, 0x06a7, 0x9bdc, 0xf174, 0xc19b -.word 0x69c1, 0xe49b, 0x4786, 0xefbe, 0x9dc6, 0x0fc1, 0xa1cc, 0x240c, 0x2c6f, 0x2de9, 0x84aa, 0x4a74, 0xa9dc, 0x5cb0, 0x88da, 0x76f9 -.word 0x5152, 0x983e, 0xc66d, 0xa831, 0x27c8, 0xb003, 0x7fc7, 0xbf59, 0x0bf3, 0xc6e0, 0x9147, 0xd5a7, 0x6351, 0x06ca, 0x2967, 0x1429 -.word 0x0a85, 0x27b7, 0x2138, 0x2e1b, 0x6dfc, 0x4d2c, 0x0d13, 0x5338, 0x7354, 0x650a, 0x0abb, 0x766a, 0xc92e, 0x81c2, 0x2c85, 0x9272 -.word 0xe8a1, 0xa2bf, 0x664b, 0xa81a, 0x8b70, 0xc24b, 0x51a3, 0xc76c, 0xe819, 0xd192, 0x0624, 0xd699, 0x3585, 0xf40e, 0xa070, 0x106a -.word 0xc116, 0x19a4, 0x6c08, 0x1e37, 0x774c, 0x2748, 0xbcb5, 0x34b0, 0x0cb3, 0x391c, 0xaa4a, 0x4ed8, 0xca4f, 0x5b9c, 0x6ff3, 0x682e -.word 0x82ee, 0x748f, 0x636f, 0x78a5, 0x7814, 0x84c8, 0x0208, 0x8cc7, 0xfffa, 0x90be, 0x6ceb, 0xa450, 0xa3f7, 0xbef9, 0x78f2, 0xc671 - - -;########################################################### - -.global sha256_init -;uint32_t sha256_init_vector[]={ -; 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, -; 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; -; -;void sha256_init(sha256_ctx_t *state){ -; state->length=0; -; memcpy(state->h, sha256_init_vector, 8*4); -;} -; param1: (r23,r24) 16-bit pointer to sha256_ctx_t struct in ram -; modifys: Z(r30,r31), Func1, r22 -sha256_init: - movw r26, r24 ; (24,25) --> (26,27) load X with param1 - ldi r30, lo8((sha256_init_vector)) - ldi r31, hi8((sha256_init_vector)) - ldi r22, 32+8 -sha256_init_vloop: - lpm r23, Z+ - st X+, r23 - dec r22 - brne sha256_init_vloop - ret - -sha256_init_vector: -.word 0xE667, 0x6A09 -.word 0xAE85, 0xBB67 -.word 0xF372, 0x3C6E -.word 0xF53A, 0xA54F -.word 0x527F, 0x510E -.word 0x688C, 0x9B05 -.word 0xD9AB, 0x1F83 -.word 0xCD19, 0x5BE0 -.word 0x0000, 0x0000 -.word 0x0000, 0x0000 - -;########################################################### - -.global rotl32 -; === ROTL32 === -; function that rotates a 32 bit word to the left -; param1: the 32-bit word to rotate -; given in r25,r24,r23,r22 (r25 is most significant) -; param2: an 8-bit value telling how often to rotate -; given in r20 -; modifys: r21, r22 -rotl32: - cpi r20, 8 - brlo bitrotl - mov r21, r25 - mov r25, r24 - mov r24, r23 - mov r23, r22 - mov r22, r21 - subi r20, 8 - rjmp rotl32 -bitrotl: - clr r21 - clc -bitrotl_loop: - tst r20 - breq fixrotl -2: - rol r22 - rol r23 - rol r24 - rol r25 - rol r21 - dec r20 - brne 2b -fixrotl: - or r22, r21 - ret - - -;########################################################### - -.global rotr32 -; === ROTR32 === -; function that rotates a 32 bit word to the right -; param1: the 32-bit word to rotate -; given in r25,r24,r23,22 (r25 is most significant) -; param2: an 8-bit value telling how often to rotate -; given in r20 -; modifys: r21, r22 -rotr32: - cpi r20, 8 - brlo bitrotr - mov r21, r22 - mov r22, r23 - mov r23, r24 - mov r24, r25 - mov r25, r21 - subi r20, 8 - rjmp rotr32 -bitrotr: - clr r21 - clc -bitrotr_loop: - tst r20 - breq fixrotr -2: - ror r25 - ror r24 - ror r23 - ror r22 - ror r21 - dec r20 - brne 2b -fixrotr: - or r25, r21 - ret - - -;########################################################### -.global change_endian32 -; === change_endian32 === -; function that changes the endianess of a 32-bit word -; param1: the 32-bit word -; given in r25,r24,r23,22 (r25 is most significant) -; modifys: r21, r22 -change_endian32: - movw r20, r22 ; (r22,r23) --> (r20,r21) - mov r22, r25 - mov r23, r24 - mov r24, r21 - mov r25, r20 - ret +#include "hal/crypto/AVR/drivers/SHA256/SHA256.S" #endif \ No newline at end of file diff --git a/MyConfig.h b/MyConfig.h index e8c820109..e164f2063 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -20,15 +20,13 @@ /** * @file MyConfig.h * @ingroup MyConfigGrp - * * @brief MySensors specific configuration flags. - * + * @{ * Set these in your sketch before including MySensors.h to customize the library to your needs. * If the sketch does not define these flags, they will get default values where applicable. */ #ifndef MyConfig_h #define MyConfig_h -#include /** * @defgroup SerialDebugGrpPub Serial and debugging @@ -67,33 +65,40 @@ /** * @def MY_DEBUG_OTA - * @brief Define MY_DEBUG_OTA to redirect debug prints to given node ID + * @brief Define MY_DEBUG_OTA to (nodeID) to redirect debug prints to given node ID + * + * Example: @code #define MY_DEBUG_OTA (0) @endcode will redirect debug prints to node ID 0. * * With this option debugging messages over serial are disabled. It's not possible to send debug * messages generated by the radio. All other debug messages redirected to the given Node ID. - * The debug messages are sent unsigned. + * The debug messages are sent without signing. * * This function allocates some additional memory for radio packet preparation and buffering. * Debug messages are sent to child ID 255 (NODE_SENSOR_ID) as I_LOG_MESSAGE type. * - * You have to enable the MY_OTA_LOG_RECEIVER_FEATURE on the target node. + * You have to enable the @ref MY_OTA_LOG_RECEIVER_FEATURE on the target node. * Look into the LogOTAGateway and LogOTANode examples. * - * The output buffer can be configured via MY_SERIAL_OUTPUT_SIZE + * The output buffer can be configured via @ref MY_SERIAL_OUTPUT_SIZE * Transport related debugging is disabled when MY_DEBUG_OTA is enabled. * */ //#define MY_DEBUG_OTA (0) /** - * @def MY_DEBUG_OTA_DISABLE_ACK - * @brief Define MY_DEBUG_OTA_DISABLE_ACK to send messages with no ACK flag. + * @def MY_DEBUG_OTA_DISABLE_ECHO + * @brief Define MY_DEBUG_OTA_DISABLE_ECHO to send messages without requesting the + * destination to echo the message. * * This option reduces the latency added by OTA debug messages by sending packages * only once. You can loose debug messages. * */ -//#define MY_DEBUG_OTA_DISABLE_ACK +//#define MY_DEBUG_OTA_DISABLE_ECHO +#if defined(MY_DEBUG_OTA_DISABLE_ACK) && !defined(DOXYGEN) +#warning MY_DEBUG_OTA_DISABLE_ACK is deprecated, please use MY_DEBUG_OTA_DISABLE_ECHO instead +#define MY_DEBUG_OTA_DISABLE_ECHO +#endif /** * @def MY_OTA_LOG_RECEIVER_FEATURE @@ -193,16 +198,16 @@ /** - * @defgroup RadioSettingGrpPub Radio selection + * @defgroup TransportSettingGrpPub Transport selection * @ingroup MyConfigGrp - * @brief These options control what radio type to use and various radio specific customisations. + * @brief These options control what transport type to use and various transport specific customisations. * @{ */ /** * @defgroup RS485SettingGrpPub RS485 - * @ingroup RadioSettingGrpPub + * @ingroup TransportSettingGrpPub * @brief These options are specific to the RS485 wired transport. * @{ */ @@ -244,6 +249,12 @@ */ //#define MY_RS485_DE_PIN (2) +/** + * @def MY_RS485_DE_INVERSE + * @brief Define this if RS485 driver enable pin polarity is inverted (low-active). + */ +//#define MY_RS485_DE_INVERSE + /** * @def MY_RS485_HWSERIAL * @brief Define this if RS485 is connected to a hardware serial port. @@ -255,7 +266,7 @@ /** * @defgroup RF24SettingGrpPub RF24 - * @ingroup RadioSettingGrpPub + * @ingroup TransportSettingGrpPub * @brief These options are specific to the RF24 family of wireless transport modules. * * The following chips are supported by this driver: @@ -353,6 +364,10 @@ * * This feature is currently not supported for anything but RF24. * Require @ref MY_RF24_IRQ_PIN to be set. + * + * Note: Not supported on ESP8266, ESP32, STM32, nRF5 and sketches + * that use SoftSPI. See below issue for details + * https://github.com/mysensors/MySensors/issues/1128 */ //#define MY_RX_MESSAGE_BUFFER_FEATURE @@ -442,7 +457,7 @@ /** * @defgroup NRF5SettingGrpPub nRF5 - * @ingroup RadioSettingGrpPub + * @ingroup TransportSettingGrpPub * @brief These options are specific to the nRF5 (with Enhanced ShockBurst) family of wireless * transport modules. * @@ -573,7 +588,7 @@ /** * @defgroup RFM69SettingGrpPub RFM69 - * @ingroup RadioSettingGrpPub + * @ingroup TransportSettingGrpPub * @brief These options are specific to the %RFM69 family of wireless transport modules. * * The following chips are supported by this driver: @@ -696,8 +711,7 @@ //#define MY_RFM69_RST_PIN (9) #ifdef MY_RF69_RESET -// legacy, older board files -// not enabled now: #warning MY_RF69_RESET is depreciated, please use MY_RFM69_RST_PIN instead. +#warning MY_RF69_RESET is depreciated, please use MY_RFM69_RST_PIN instead. #define MY_RFM69_RST_PIN MY_RF69_RESET #endif @@ -713,8 +727,7 @@ */ #ifndef MY_RFM69_IRQ_PIN #ifdef MY_RF69_IRQ_PIN -// legacy, older board files -// not enabled now: #warning MY_RF69_IRQ_PIN is depreciated, please use MY_RFM69_IRQ_PIN instead. +#warning MY_RF69_IRQ_PIN is depreciated, please use MY_RFM69_IRQ_PIN instead. #define MY_RFM69_IRQ_PIN MY_RF69_IRQ_PIN #else #define MY_RFM69_IRQ_PIN DEFAULT_RFM69_IRQ_PIN @@ -727,8 +740,7 @@ */ #ifndef MY_RFM69_IRQ_NUM #ifdef MY_RF69_IRQ_NUM -// legacy, older board files -// not enabled now: #warning MY_RF69_IRQ_NUM is depreciated, please use MY_RFM69_IRQ_NUM instead. +#warning MY_RF69_IRQ_NUM is depreciated, please use MY_RFM69_IRQ_NUM instead. #define MY_RFM69_IRQ_NUM MY_RF69_IRQ_NUM #else #define MY_RFM69_IRQ_NUM digitalPinToInterrupt(MY_RFM69_IRQ_PIN) @@ -741,8 +753,7 @@ */ #ifndef MY_RFM69_CS_PIN #ifdef MY_RF69_SPI_CS -// legacy, older board files -// not enabled now: #warning MY_RF69_SPI_CS is depreciated, please use MY_RFM69_CS_PIN instead. +#warning MY_RF69_SPI_CS is depreciated, please use MY_RFM69_CS_PIN instead. #define MY_RFM69_CS_PIN MY_RF69_SPI_CS #else #define MY_RFM69_CS_PIN DEFAULT_RFM69_CS_PIN @@ -767,22 +778,6 @@ */ //#define MY_RFM69_ENABLE_ENCRYPTION -/** - * @def MY_RFM69_ENABLE_LISTENMODE - * @brief Define this if you need listenmode, or skip it to save memory - */ -//#define MY_RFM69_ENABLE_LISTENMODE - -#if defined(MY_RFM69_ENABLE_LISTENMODE) && !defined(MY_RFM69_DEFAULT_LISTEN_RX_US) -// By default, receive for 256uS in listen mode and idle for ~1s -#define MY_RFM69_DEFAULT_LISTEN_RX_US (256) -#endif - -#if defined(MY_RFM69_ENABLE_LISTENMODE) && !defined(MY_RFM69_DEFAULT_LISTEN_IDLE_US) -// By default, receive for 256uS in listen mode and idle for ~1s -#define MY_RFM69_DEFAULT_LISTEN_IDLE_US (1*1000000ul) -#endif - /** * @def MY_RFM69_MODEM_CONFIGURATION * @brief %RFM69 modem configuration, default is %RFM69_FSK_BR55_5_FD50 @@ -810,7 +805,7 @@ /** * @defgroup RFM95SettingGrpPub RFM95 - * @ingroup RadioSettingGrpPub + * @ingroup TransportSettingGrpPub * @brief These options are specific to the %RFM95 family of wireless transport modules. * * The following chips are supported by this driver: @@ -972,7 +967,7 @@ /** * @defgroup SoftSpiSettingGrpPub Soft SPI - * @ingroup RadioSettingGrpPub + * @ingroup TransportSettingGrpPub * @brief These options are specific the soft SPI driver for certain radio transport drivers. * * The following transport drivers supported by this driver: @@ -1014,7 +1009,7 @@ #endif /** @}*/ // End of SoftSpiSettingGrpPub group -/** @}*/ // End of RadioSettingGrpPub group +/** @}*/ // End of TransportSettingGrpPub group /** * @defgroup RoutingNodeSettingGrpPub Routing and node @@ -1407,7 +1402,9 @@ * @def MY_WIFI_BSSID * @brief BSSID of your WiFi network */ -//#define MY_WIFI_BSSID "MyBSSID" +#ifndef MY_WIFI_BSSID +#define MY_WIFI_BSSID NULL +#endif /** * @def MY_WIFI_PASSWORD @@ -1419,7 +1416,9 @@ * @def MY_HOSTNAME * @brief Hostname of your device */ -//#define MY_HOSTNAME "MyHostname" +#ifndef MY_HOSTNAME +#define MY_HOSTNAME "MYSENSORS_DEVICE" +#endif /** * @def MY_PORT @@ -1561,7 +1560,56 @@ /** @}*/ // End of GatewaySettingGrpPub group /** - * @defgroup lEDSettingGrpPub LED + * @defgroup GSMSettingGrpPub GSM + * @ingroup MyConfigGrp + * @brief These options control GSM specific configurations. + * @{ + */ +/** + * @def MY_GSM_APN + * @brief APN from your cell carrier / mobile provider. Example: 4g.tele2.se + */ +//#define MY_GSM_APN +/** +* @def MY_GSM_BAUDRATE +* @brief Baudrate for your GSM modem. If left undefined, TinyGSM will try to auto detect the correct rate +*/ +//#define MY_GSM_BAUDRATE (9600u) +/** +* @def MY_GSM_PIN +* @brief PIN code for your SIM card, if PIN lock is active. +*/ +//#define MY_GSM_PIN +/** +* @def MY_GSM_PSW +* @brief If using a GSM modem, this is the password supplied by your cell carrier / mobile provider. If using ESP8266 as a WiFi modem, this is your WiFi network password +*/ +//#define MY_GSM_PSW +/** +* @def MY_GSM_RX +* @brief If defined, uses softSerial using defined pins (must also define MY_GSM_TX) +*/ +//#define MY_GSM_RX +/** +* @def MY_GSM_SSID +* @brief If using ESP8266 as WiFi modem, this is your network SSID +*/ +//#define MY_GSM_SSID +/** +* @def MY_GSM_TX +* @brief If defined, uses softSerial using defined pins (must also define MY_GSM_RX) +*/ +//#define MY_GSM_TX +/** +* @def MY_GSM_USR +* @brief Supplied by your cell carrier / mobile operator. If not required, leave undefined. +*/ +//#define MY_GSM_USR + +/** @}*/ // End of GSMSettingGrpPub group + +/** + * @defgroup LEDSettingGrpPub LED * @ingroup MyConfigGrp * @brief These options control LED specific configurations. * @{ @@ -1617,7 +1665,7 @@ #ifndef MY_DEFAULT_LED_BLINK_PERIOD #define MY_DEFAULT_LED_BLINK_PERIOD 300 #endif -/** @}*/ // End of lEDSettingGrpPub group +/** @}*/ // End of LEDSettingGrpPub group /** * @defgroup SecuritySettingGrpPub Security @@ -2085,7 +2133,7 @@ #define MY_DEBUG_VERBOSE_OTA_UPDATE //!< MY_DEBUG_VERBOSE_OTA_UPDATE #endif -#if defined(MY_DEBUG) || defined(MY_DEBUG_VERBOSE_CORE) || defined(MY_DEBUG_VERBOSE_TRANSPORT) || defined(MY_DEBUG_VERBOSE_GATEWAY) || defined(MY_DEBUG_VERBOSE_SIGNING) || defined(MY_DEBUG_VERBOSE_OTA_UPDATE) || defined(MY_DEBUG_VERBOSE_RF24) || defined(MY_DEBUG_VERBOSE_NRF5_ESB) || defined(MY_DEBUG_VERBOSE_RFM69) || defined(MY_DEBUG_VERBOSE_RFM95) +#if defined(MY_DEBUG) || defined(MY_DEBUG_VERBOSE_CORE) || defined(MY_DEBUG_VERBOSE_TRANSPORT) || defined(MY_DEBUG_VERBOSE_GATEWAY) || defined(MY_DEBUG_VERBOSE_SIGNING) || defined(MY_DEBUG_VERBOSE_OTA_UPDATE) || defined(MY_DEBUG_VERBOSE_RF24) || defined(MY_DEBUG_VERBOSE_NRF5_ESB) || defined(MY_DEBUG_VERBOSE_RFM69) || defined(MY_DEBUG_VERBOSE_RFM95) || defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) #define DEBUG_OUTPUT_ENABLED //!< DEBUG_OUTPUT_ENABLED #ifndef MY_DEBUG_OTA #define DEBUG_OUTPUT(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug @@ -2093,7 +2141,7 @@ #ifndef MY_OTA_LOG_SENDER_FEATURE #define MY_OTA_LOG_SENDER_FEATURE #endif -#ifndef MY_DEBUG_OTA_DISABLE_ACK +#ifndef MY_DEBUG_OTA_DISABLE_ECHO #define DEBUG_OUTPUT(x,...) OTALog((MY_DEBUG_OTA), true, x, ##__VA_ARGS__) //!< debug #else #define DEBUG_OUTPUT(x,...) OTALog((MY_DEBUG_OTA), false, x, ##__VA_ARGS__) //!< debug @@ -2156,6 +2204,12 @@ */ #define ARDUINO_ARCH_ESP8266 +/** + * @def ARDUINO_ARCH_ESP32 + * @brief Automatically set when building for ESP32 targets + */ +#define ARDUINO_ARCH_ESP32 + /** * @def ARDUINO_ARCH_AVR * @brief Automatically set when building for AVR targets @@ -2178,7 +2232,7 @@ #define MY_DEBUG #define MY_DEBUGDEVICE #define MY_DEBUG_OTA -#define MY_DEBUG_OTA_DISABLE_ACK +#define MY_DEBUG_OTA_DISABLE_ECHO #define MY_SPECIAL_DEBUG #define MY_DISABLED_SERIAL #define MY_SPLASH_SCREEN_DISABLED @@ -2240,47 +2294,14 @@ #define MY_CONTROLLER_IP_ADDRESS #define MY_CONTROLLER_URL_ADDRESS // TinyGSM -/** - * @def MY_GSM_APN - * @brief APN from your cell carrier / mobile provider. Example: 4g.tele2.se - */ #define MY_GSM_APN -/** - * @def MY_GSM_BAUDRATE - * @brief Baudrate for your GSM modem. If left undefined, TinyGSM will try to auto detect the correct rate - */ #define MY_GSM_BAUDRATE -/** - * @def MY_GSM_PIN - * @brief PIN code for your SIM card, if PIN lock is active. - */ #define MY_GSM_PIN -/** - * @def MY_GSM_PSW - * @brief If using a GSM modem, this is the password supplied by your cell carrier / mobile provider. If using ESP8266 as a WiFi modem, this is your WiFi network password - */ #define MY_GSM_PSW -/** - * @def MY_GSM_RX - * @brief If defined, uses softSerial using defined pins (must also define MY_GSM_TX) - */ #define MY_GSM_RX -/** - * @def MY_GSM_SSID - * @brief If using ESP8266 as WiFi modem, this is your network SSID - */ #define MY_GSM_SSID -/** - * @def MY_GSM_TX - * @brief If defined, uses softSerial using defined pins (must also define MY_GSM_RX) - */ #define MY_GSM_TX -/** - * @def MY_GSM_USR - * @brief Supplied by your cell carrier / mobile operator. If not required, leave undefined. - */ #define MY_GSM_USR - // LED #define MY_DEFAULT_ERR_LED_PIN #define MY_DEFAULT_TX_LED_PIN @@ -2302,6 +2323,8 @@ #define MY_OTA_USE_I2C_EEPROM // RS485 #define MY_RS485 +#define MY_RS485_DE_PIN +#define MY_RS485_DE_INVERSE #define MY_RS485_HWSERIAL // RF24 #define MY_RADIO_RF24 @@ -2330,7 +2353,6 @@ #define MY_RFM69_RST_PIN #define MY_DEBUG_VERBOSE_RFM69 #define MY_DEBUG_VERBOSE_RFM69_REGISTERS -#define MY_RFM69_ENABLE_LISTENMODE // RFM95 #define MY_RADIO_RFM95 #define MY_DEBUG_VERBOSE_RFM95 diff --git a/MySensors.h b/MySensors.h index 79b865176..db386ba35 100644 --- a/MySensors.h +++ b/MySensors.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -38,8 +38,11 @@ #ifdef __cplusplus #include #endif +#include #include "MyConfig.h" +#include "core/MyHelperFunctions.cpp" + #include "core/MySplashScreen.h" #include "core/MySensorsCore.h" @@ -58,7 +61,6 @@ #include "hal/architecture/ESP32/MyHwESP32.cpp" #include "hal/crypto/ESP32/MyCryptoESP32.cpp" #elif defined(ARDUINO_ARCH_AVR) -#include "hal/architecture/AVR/drivers/DigitalWriteFast/digitalWriteFast.h" #include "hal/architecture/AVR/MyHwAVR.cpp" #include "hal/crypto/AVR/MyCryptoAVR.cpp" #elif defined(ARDUINO_ARCH_SAMD) @@ -69,8 +71,6 @@ #include "hal/architecture/STM32F1/MyHwSTM32F1.cpp" #include "hal/crypto/generic/MyCryptoGeneric.cpp" #elif defined(ARDUINO_ARCH_NRF5) || defined(ARDUINO_ARCH_NRF52) -#include "drivers/NVM/VirtualPage.cpp" -#include "drivers/NVM/NVRAM.cpp" #include "hal/architecture/NRF5/MyHwNRF5.cpp" #include "hal/crypto/generic/MyCryptoGeneric.cpp" #elif defined(__arm__) && defined(TEENSYDUINO) @@ -83,6 +83,8 @@ #error Hardware abstraction not defined (unsupported platform) #endif +#include "hal/architecture/MyHwHAL.cpp" + // commonly used macros, sometimes missing in arch definitions #if !defined(_BV) #define _BV(x) (1<<(x)) //!< _BV @@ -170,6 +172,22 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS for UDP #endif + + +// Set MQTT defaults if not set + +#if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX) +#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" +#endif + +#if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) +#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" +#endif + +#if !defined(MY_MQTT_CLIENT_ID) +#define MY_MQTT_CLIENT_ID "mysensors-1" +#endif + #if defined(MY_GATEWAY_MQTT_CLIENT) #if defined(MY_SENSOR_NETWORK) // We assume that a gateway having a radio also should act as repeater @@ -187,37 +205,24 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #error MY_GATEWAY_TINYGSM only works with MY_GATEWAY_MQTT_CLIENT #endif -#if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX) -#error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client -#endif - -#if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) -#error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client -#endif - -#if !defined(MY_MQTT_CLIENT_ID) -#error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client -#endif - #include "core/MyGatewayTransport.cpp" -#include "core/MyProtocolMySensors.cpp" +#include "core/MyProtocol.cpp" #if defined(MY_GATEWAY_TINYGSM) #include "drivers/TinyGSM/TinyGsmClient.h" #endif #if defined(MY_GATEWAY_LINUX) -#include "drivers/Linux/EthernetClient.h" -#include "drivers/Linux/EthernetServer.h" -#include "drivers/Linux/IPAddress.h" +#include "hal/architecture/Linux/drivers/core/EthernetClient.h" +#include "hal/architecture/Linux/drivers/core/EthernetServer.h" +#include "hal/architecture/Linux/drivers/core/IPAddress.h" #endif #include "drivers/PubSubClient/PubSubClient.cpp" #include "core/MyGatewayTransportMQTTClient.cpp" #elif defined(MY_GATEWAY_FEATURE) // GATEWAY - COMMON FUNCTIONS #include "core/MyGatewayTransport.cpp" - -#include "core/MyProtocolMySensors.cpp" +#include "core/MyProtocol.cpp" // GATEWAY - CONFIGURATION #if defined(MY_SENSOR_NETWORK) @@ -225,9 +230,6 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #define MY_REPEATER_FEATURE #endif -#if !defined(MY_PORT) -#error You must define MY_PORT (controller or gateway port to open) -#endif #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) // GATEWAY - ESP8266 / ESP32 #include "core/MyGatewayTransportEthernet.cpp" @@ -236,9 +238,9 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #if defined(MY_USE_UDP) #error UDP mode is not available for Linux #endif -#include "drivers/Linux/EthernetClient.h" -#include "drivers/Linux/EthernetServer.h" -#include "drivers/Linux/IPAddress.h" +#include "hal/architecture/Linux/drivers/core/EthernetClient.h" +#include "hal/architecture/Linux/drivers/core/EthernetServer.h" +#include "hal/architecture/Linux/drivers/core/IPAddress.h" #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_W5100) // GATEWAY - W5100 @@ -333,14 +335,6 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #define MY_RAM_ROUTING_TABLE_ENABLED #endif -// SOFTSPI -#ifdef MY_SOFTSPI -#if defined(ARDUINO_ARCH_ESP8266) -#error Soft SPI is not available on ESP8266 -#endif -#include "hal/architecture/AVR/drivers/DigitalIO/DigitalIO.h" -#endif - // SOFTSERIAL #if defined(MY_GSM_TX) != defined(MY_GSM_RX) #error Both, MY_GSM_TX and MY_GSM_RX need to be defined when using SoftSerial @@ -390,6 +384,12 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #include "hal/transport/RFM95/MyTransportRFM95.cpp" #endif +#if (defined(MY_RF24_ENABLE_ENCRYPTION) && defined(MY_RADIO_RF24)) || (defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) && defined(MY_RADIO_NRF5_ESB)) || (defined(MY_RFM69_ENABLE_ENCRYPTION) && defined(MY_RADIO_RFM69)) || (defined(MY_RFM95_ENABLE_ENCRYPTION) && defined(MY_RADIO_RFM95)) +#define MY_TRANSPORT_ENCRYPTION //!< ïnternal flag +#endif + +#include "hal/transport/MyTransportHAL.cpp" + // PASSIVE MODE #if defined(MY_PASSIVE_NODE) && !defined(DOXYGEN) #define MY_TRANSPORT_UPLINK_CHECK_DISABLED @@ -397,7 +397,7 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #undef MY_REGISTRATION_FEATURE #undef MY_SIGNING_FEATURE #undef MY_OTA_FIRMWARE_FEATURE -#if (defined(MY_GATEWAY_FEATURE) || defined(MY_REPEATER_FEATURE)) +#if defined(MY_GATEWAY_FEATURE) || defined(MY_REPEATER_FEATURE) #error This node is configured as GW/repeater, MY_PASSIVE_NODE cannot be set simultaneously #endif #if (MY_NODE_ID == AUTO) @@ -447,7 +447,7 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #include "hal/architecture/Linux/MyMainLinuxGeneric.cpp" #elif defined(ARDUINO_ARCH_STM32F1) #include "hal/architecture/STM32F1/MyMainSTM32F1.cpp" -#elif defined(TEENSYDUINO) +#elif defined(__arm__) && defined(TEENSYDUINO) #include "hal/architecture/Teensy3/MyMainTeensy3.cpp" #endif diff --git a/README.md b/README.md index 5c1e76bf8..5e36c2039 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -MySensors Library v2.3.1 +MySensors Library v2.3.2 Please visit www.mysensors.org for more information +Current version in Arduino IDE [![arduino-library-badge](https://www.ardu-badge.com/badge/MySensors.svg)](https://www.ardu-badge.com/MySensors) + Documentation ------------- [master](https://www.mysensors.org/apidocs/index.html) [development](https://www.mysensors.org/apidocs-beta/index.html) diff --git a/configure b/configure index a4e68aaba..125af49a4 100755 --- a/configure +++ b/configure @@ -4,7 +4,7 @@ # Original work: https://github.com/TMRh20/RF24/blob/master/configure function help { -cat < CPU defining/optimizing flags to be used. [configure autodetected] --extra-cflags= Extra C flags passed to C compilation. [] @@ -62,10 +62,10 @@ MySensors options: MQTT publish topic prefix. --my-mqtt-subscribe-topic-prefix= MQTT subscribe topic prefix. - --my-transport=[none|rf24|rs485|rfm95|rfm69] + --my-transport=[none|rf24|rfm69|rfm95|rs485] Set the transport to be used to communicate with other nodes. [rf24] --my-rf24-channel=<0-125> RF channel for the sensor net. [76] - --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] + --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_HIGH|RF24_PA_LOW|RF24_PA_MIN] RF24 PA level. [RF24_PA_MAX] --my-rf24-ce-pin= Pin number to use for rf24 Chip-Enable. --my-rf24-cs-pin= Pin number to use for rf24 Chip-Select. @@ -183,6 +183,10 @@ function detect_machine { soc="BCM2837" tp="rpi3" ;; + 3) + soc="BCM2711" + tp="rpi4" + ;; esac fi elif [[ $hardware == "BCM2708"* ]]; then @@ -261,6 +265,9 @@ function gcc_cpu_flags { BCM2837) flags="-march=armv8-a+crc -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard" ;; + BCM2711) + flags="-march=armv8-a+crc -mtune=cortex-a72 -mfpu=neon-fp-armv8 -mfloat-abi=hard" + ;; AM33XX) flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard" ;; @@ -318,7 +325,7 @@ for opt do CFLAGS="$optarg" ;; --extra-cxxflags=*) - CXXFLAGS="$optarg" + CXXFLAGS="$optarg $CXXFLAGS" ;; --extra-ldflags=*) LDFLAGS="$optarg" @@ -542,6 +549,7 @@ BINDIR=${BINDIR:-bin} GATEWAY_DIR=${GATEWAY_DIR:-${PREFIX}/bin} CC=${CC:-gcc} CXX=${CXX:-g++} +CXXFLAGS="$CXXFLAGS -std=c++11" if [ -z "${SOC}" ]; then printf "${SECTION} Detecting target machine.\n" @@ -556,7 +564,7 @@ if [ -z "${CPUFLAGS}" ]; then CPUFLAGS=$(gcc_cpu_flags $SOC) fi -if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" ]]; then +if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" || $SOC == "BCM2711" ]]; then CPPFLAGS="-DLINUX_ARCH_RASPBERRYPI $CPPFLAGS" else printf "${SECTION} Checking GPIO Sysfs.\n" @@ -569,7 +577,7 @@ fi if [ -z "${SPI_DRIVER}" ]; then printf "${SECTION} Detecting SPI driver.\n" - if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" ]]; then + if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" || $SOC == "BCM2711" ]]; then SPI_DRIVER=BCM elif [[ $(eval 'ls /dev/spidev* 2>/dev/null') ]]; then SPI_DRIVER=SPIDEV @@ -584,8 +592,8 @@ fi if [ -n "${SPI_DRIVER}" ]; then case ${SPI_DRIVER} in BCM) - if [[ $SOC != "BCM2835" && $SOC != "BCM2836" && $SOC != "BCM2837" ]]; then - die "BCM SPI driver is only supported for SOCs BCM2835, BCM2836 or BCM2837" 5 + if [[ $SOC != "BCM2835" && $SOC != "BCM2836" && $SOC != "BCM2837" && $SOC != "BCM2711" ]]; then + die "BCM SPI driver is only supported for SOCs BCM2835, BCM2836, BCM2837 or BCM2711" 5 fi CPPFLAGS="-DLINUX_SPI_BCM $CPPFLAGS" ;; @@ -625,10 +633,10 @@ elif [[ ${transport_type} == "rf24" ]]; then CPPFLAGS="-DMY_RADIO_RF24 $CPPFLAGS" elif [[ ${transport_type} == "rfm69" ]]; then CPPFLAGS="-DMY_RADIO_RFM69 -DMY_RFM69_NEW_DRIVER $CPPFLAGS" -elif [[ ${transport_type} == "rs485" ]]; then - CPPFLAGS="-DMY_RS485 $CPPFLAGS" elif [[ ${transport_type} == "rfm95" ]]; then CPPFLAGS="-DMY_RADIO_RFM95 $CPPFLAGS" +elif [[ ${transport_type} == "rs485" ]]; then + CPPFLAGS="-DMY_RS485 $CPPFLAGS" else die "Invalid transport type." 3 fi @@ -665,11 +673,12 @@ else printf " ${OK} Encryption: Disabled.\n" fi -printf " ${OK} CPPFLAGS: $CPPFLAGS\n" - LDFLAGS="-pthread $LDFLAGS" CPPFLAGS="$CPUFLAGS $CPPFLAGS" +printf " ${OK} CPPFLAGS: $CPPFLAGS\n" +printf " ${OK} CXXFLAGS: $CXXFLAGS\n" + printf "${SECTION} Detecting init system.\n" if [ "${NO_INIT}" ]; then printf " ${OK} No init system chosen.\n" diff --git a/core/MyCapabilities.h b/core/MyCapabilities.h index 3be1c5f41..9c276c5e9 100644 --- a/core/MyCapabilities.h +++ b/core/MyCapabilities.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyEepromAddresses.h b/core/MyEepromAddresses.h index ac710de3f..8e735e06d 100644 --- a/core/MyEepromAddresses.h +++ b/core/MyEepromAddresses.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyGatewayTransport.cpp b/core/MyGatewayTransport.cpp index 042631f6e..37aa71328 100644 --- a/core/MyGatewayTransport.cpp +++ b/core/MyGatewayTransport.cpp @@ -29,25 +29,25 @@ inline void gatewayTransportProcess(void) { if (gatewayTransportAvailable()) { _msg = gatewayTransportReceive(); - if (_msg.destination == GATEWAY_ADDRESS) { + if (_msg.getDestination() == GATEWAY_ADDRESS) { - // Check if sender requests an ack back. - if (mGetRequestAck(_msg)) { + // Check if sender requests an echo + if (_msg.getRequestEcho()) { // Copy message _msgTmp = _msg; - mSetRequestAck(_msgTmp, - false); // Reply without ack flag (otherwise we would end up in an eternal loop) - mSetAck(_msgTmp, true); - _msgTmp.sender = getNodeId(); - _msgTmp.destination = _msg.sender; + // Reply without echo flag, otherwise we would end up in an eternal loop + _msgTmp.setRequestEcho(false); + _msgTmp.setEcho(true); + _msgTmp.setSender(getNodeId()); + _msgTmp.setDestination(_msg.getSender()); gatewayTransportSend(_msgTmp); } - if (mGetCommand(_msg) == C_INTERNAL) { - if (_msg.type == I_VERSION) { + if (_msg.getCommand() == C_INTERNAL) { + if (_msg.getType() == I_VERSION) { // Request for version. Create the response gatewayTransportSend(buildGw(_msgTmp, I_VERSION).set(MYSENSORS_LIBRARY_VERSION)); #ifdef MY_INCLUSION_MODE_FEATURE - } else if (_msg.type == I_INCLUSION_MODE) { + } else if (_msg.getType() == I_INCLUSION_MODE) { // Request to change inclusion mode inclusionModeSet(atoi(_msg.data) == 1); #endif diff --git a/core/MyGatewayTransport.h b/core/MyGatewayTransport.h index 7fcd9bef4..cfb34b48a 100644 --- a/core/MyGatewayTransport.h +++ b/core/MyGatewayTransport.h @@ -51,9 +51,10 @@ * | | GWT | TPS | ETH OK | Connected to network * |!| GWT | TPS | ETH FAIL | Connection failed * | | GWT | IMQ | TOPIC=%%s,MSG RECEIVE | MQTT message received on topic [%%s] -* | | GWT | RMQ | MQTT RECONNECT | Reconnecting to MQTT broker -* | | GWT | RMQ | MQTT CONNECTED | Connected to MQTT broker -* | | GWT | TPC | CONNECTING... | Connecting to MQTT broker +* | | GWT | RMQ | CONNECTING... | Connecting to MQTT broker +* | | GWT | RMQ | OK | Connected to MQTT broker +* |!| GWT | RMQ | FAIL | Connection to MQTT broker failed +* | | GWT | TPC | CONNECTING... | Obtaining IP address * | | GWT | TPC | IP=%%s | IP address [%%s] obtained * |!| GWT | TPC | DHCP FAIL | DHCP request failed * | | GWT | RFC | C=%%d,MSG=%%s | Received message [%%s] from client [%%d] diff --git a/core/MyGatewayTransportEthernet.cpp b/core/MyGatewayTransportEthernet.cpp index 7ba465007..dde3a69e8 100644 --- a/core/MyGatewayTransportEthernet.cpp +++ b/core/MyGatewayTransportEthernet.cpp @@ -47,27 +47,29 @@ extern MyMessage _msgTmp; #undef MY_ESP8266_HOSTNAME // cleanup #endif -#ifndef MY_WIFI_BSSID -#define MY_WIFI_BSSID NULL +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#if !defined(MY_WIFI_SSID) +#error ESP8266/ESP32 gateway: MY_WIFI_SSID not defined! +#endif #endif #if defined(MY_CONTROLLER_IP_ADDRESS) -IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS); +#define _ethernetControllerIP IPAddress(MY_CONTROLLER_IP_ADDRESS) #endif #if defined(MY_IP_ADDRESS) -IPAddress _ethernetGatewayIP(MY_IP_ADDRESS); +#define _ethernetGatewayIP IPAddress(MY_IP_ADDRESS) #if defined(MY_IP_GATEWAY_ADDRESS) -IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); +#define _gatewayIp IPAddress(MY_IP_GATEWAY_ADDRESS) #elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) // Assume the gateway will be the machine on the same network as the local IP // but with last octet being '1' -IPAddress _gatewayIp(_ethernetGatewayIP[0], _ethernetGatewayIP[1], _ethernetGatewayIP[2], 1); +#define _gatewayIp IPAddress(_ethernetGatewayIP[0], _ethernetGatewayIP[1], _ethernetGatewayIP[2], 1) #endif /* End of MY_IP_GATEWAY_ADDRESS */ #if defined(MY_IP_SUBNET_ADDRESS) -IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); +#define _subnetIp IPAddress(MY_IP_SUBNET_ADDRESS) #elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) -IPAddress _subnetIp(255, 255, 255, 0); +#define _subnetIp IPAddress(255, 255, 255, 0) #endif /* End of MY_IP_SUBNET_ADDRESS */ #endif /* End of MY_IP_ADDRESS */ @@ -119,15 +121,11 @@ static EthernetClient client = EthernetClient(); static inputBuffer inputString; #endif /* End of MY_GATEWAY_CLIENT_MODE */ -#ifndef MY_IP_ADDRESS -void gatewayTransportRenewIP(); -#endif - // On W5100 boards with SPI_EN exposed we can use the real SPI bus together with radio // (if we enable it during usage) -#if defined(MY_W5100_SPI_EN) -void _w5100_spi_en(bool enable) +void _w5100_spi_en(const bool enable) { +#if defined(MY_W5100_SPI_EN) if (enable) { // Pull up pin hwPinMode(MY_W5100_SPI_EN, INPUT); @@ -137,9 +135,35 @@ void _w5100_spi_en(bool enable) hwPinMode(MY_W5100_SPI_EN, OUTPUT); hwDigitalWrite(MY_W5100_SPI_EN, LOW); } -} #else -#define _w5100_spi_en(x) + (void)enable; +#endif +} + +#if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100) +void gatewayTransportRenewIP(void) +{ + /* renew/rebind IP address + 0 - nothing happened + 1 - renew failed + 2 - renew success + 3 - rebind failed + 4 - rebind success + */ + static uint32_t _nextIPRenewal = hwMillis() + MY_IP_RENEWAL_INTERVAL_MS; + const uint32_t now = hwMillis(); + + // http://playground.arduino.cc/Code/TimingRollover + if ((int32_t)(now - _nextIPRenewal) < 0) { + return; + } + if (Ethernet.maintain() & ~(0x06)) { + GATEWAY_DEBUG(PSTR("!GWT:TRC:IP RENEW FAIL\n")); + return; + } + _w5100_spi_en(false); + _nextIPRenewal = now + MY_IP_RENEWAL_INTERVAL_MS; +} #endif bool gatewayTransportInit(void) @@ -147,26 +171,22 @@ bool gatewayTransportInit(void) _w5100_spi_en(true); #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) -#if defined(MY_WIFI_SSID) // Turn off access point WiFi.mode(WIFI_STA); -#if defined(MY_HOSTNAME) #if defined(MY_GATEWAY_ESP8266) WiFi.hostname(MY_HOSTNAME); #elif defined(MY_GATEWAY_ESP32) WiFi.setHostname(MY_HOSTNAME); #endif -#endif #ifdef MY_IP_ADDRESS WiFi.config(_ethernetGatewayIP, _gatewayIp, _subnetIp); #endif (void)WiFi.begin(MY_WIFI_SSID, MY_WIFI_PASSWORD, 0, MY_WIFI_BSSID); while (WiFi.status() != WL_CONNECTED) { - wait(500); + delay(1000); GATEWAY_DEBUG(PSTR("GWT:TIN:CONNECTING...\n")); } GATEWAY_DEBUG(PSTR("GWT:TIN:IP: %s\n"), WiFi.localIP().toString().c_str()); -#endif #elif defined(MY_GATEWAY_LINUX) // Nothing to do here #else @@ -204,7 +224,7 @@ bool gatewayTransportInit(void) #endif /* End of MY_CONTROLLER_URL_ADDRESS */ GATEWAY_DEBUG(PSTR("GWT:TIN:ETH OK\n")); _w5100_spi_en(false); - gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE)); + gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(F(MSG_GW_STARTUP_COMPLETE))); _w5100_spi_en(true); presentNode(); } else { @@ -228,7 +248,7 @@ bool gatewayTransportInit(void) bool gatewayTransportSend(MyMessage &message) { int nbytes = 0; - char *_ethernetMsg = protocolFormat(message); + char *_ethernetMessage = protocolMyMessage2Serial(message); setIndication(INDICATION_GW_TX); @@ -240,7 +260,7 @@ bool gatewayTransportSend(MyMessage &message) #else _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT); #endif /* End of MY_CONTROLLER_URL_ADDRESS */ - _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg)); + _ethernetServer.write((uint8_t *)_ethernetMessage, strlen(_ethernetMessage)); // returns 1 if the packet was sent successfully nbytes = _ethernetServer.endPacket(); #else /* Else part of MY_USE_UDP */ @@ -263,19 +283,19 @@ bool gatewayTransportSend(MyMessage &message) return false; } } - nbytes = client.write((const uint8_t*)_ethernetMsg, strlen(_ethernetMsg)); + nbytes = client.write((const uint8_t *)_ethernetMessage, strlen(_ethernetMessage)); #endif /* End of MY_USE_UDP */ #else /* Else part of MY_GATEWAY_CLIENT_MODE */ // Send message to connected clients #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) { if (clients[i] && clients[i].connected()) { - nbytes += clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg)); + nbytes += clients[i].write((uint8_t *)_ethernetMessage, strlen(_ethernetMessage)); } } -#else /* Else part of MY_GATEWAY_ESP8266 */ - nbytes = _ethernetServer.write(_ethernetMsg); -#endif /* End of MY_GATEWAY_ESP8266 */ +#else /* Else part of MY_GATEWAY_ESPxx*/ + nbytes = _ethernetServer.write(_ethernetMessage); +#endif /* End of MY_GATEWAY_ESPxx */ #endif /* End of MY_GATEWAY_CLIENT_MODE */ _w5100_spi_en(false); return (nbytes > 0); @@ -296,10 +316,9 @@ bool _readFromClient(uint8_t i) inputString[i].string[inputString[i].idx] = 0; GATEWAY_DEBUG(PSTR("GWT:RFC:C=%" PRIu8 ",MSG=%s\n"), i, inputString[i].string); inputString[i].idx = 0; - if (protocolParse(_ethernetMsg, inputString[i].string)) { + if (protocolSerial2MyMessage(_ethernetMsg, inputString[i].string)) { return true; } - } else { // add it to the inputString: inputString[i].string[inputString[i].idx++] = inChar; @@ -326,7 +345,7 @@ bool _readFromClient(void) inputString.string[inputString.idx] = 0; GATEWAY_DEBUG(PSTR("GWT:RFC:MSG=%s\n"), inputString.string); inputString.idx = 0; - if (protocolParse(_ethernetMsg, inputString.string)) { + if (protocolSerial2MyMessage(_ethernetMsg, inputString.string)) { return true; } @@ -360,12 +379,11 @@ bool gatewayTransportAvailable(void) int packet_size = _ethernetServer.parsePacket(); if (packet_size) { - //GATEWAY_DEBUG(PSTR("UDP packet available. Size:%" PRIu8 "\n"), packet_size); _ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH); inputString.string[packet_size] = 0; GATEWAY_DEBUG(PSTR("GWT:TSA:UDP MSG=%s\n"), inputString.string); _w5100_spi_en(false); - const bool ok = protocolParse(_ethernetMsg, inputString.string); + const bool ok = protocolSerial2MyMessage(_ethernetMsg, inputString.string); if (ok) { setIndication(INDICATION_GW_RX); } @@ -381,7 +399,7 @@ bool gatewayTransportAvailable(void) #endif /* End of MY_CONTROLLER_URL_ADDRESS */ GATEWAY_DEBUG(PSTR("GWT:TSA:ETH OK\n")); _w5100_spi_en(false); - gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE)); + gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(F(MSG_GW_STARTUP_COMPLETE))); _w5100_spi_en(true); presentNode(); } else { @@ -398,7 +416,7 @@ bool gatewayTransportAvailable(void) #endif /* End of MY_USE_UDP */ #else /* Else part of MY_GATEWAY_CLIENT_MODE */ #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) || defined(MY_GATEWAY_LINUX) - // ESP8266: Go over list of clients and stop any that are no longer connected. + // ESP8266/ESP32: 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++) { @@ -474,29 +492,4 @@ MyMessage& gatewayTransportReceive(void) return _ethernetMsg; } -#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_ESP32) && !defined(MY_GATEWAY_LINUX) -void gatewayTransportRenewIP(void) -{ - /* renew/rebind IP address - 0 - nothing happened - 1 - renew failed - 2 - renew success - 3 - rebind failed - 4 - rebind success - */ - static unsigned long next_time = hwMillis() + MY_IP_RENEWAL_INTERVAL_MS; - unsigned long now = hwMillis(); - // http://playground.arduino.cc/Code/TimingRollover - if ((long)(now - next_time) < 0) { - return; - } - if (Ethernet.maintain() & ~(0x06)) { - GATEWAY_DEBUG(PSTR("!GWT:TRC:IP RENEW FAIL\n")); - /* Error occurred -> IP was not renewed */ - return; - } - _w5100_spi_en(false); - next_time = now + MY_IP_RENEWAL_INTERVAL_MS; -} -#endif diff --git a/core/MyGatewayTransportMQTTClient.cpp b/core/MyGatewayTransportMQTTClient.cpp index dce532a9a..12cfd67e1 100644 --- a/core/MyGatewayTransportMQTTClient.cpp +++ b/core/MyGatewayTransportMQTTClient.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -47,46 +47,55 @@ #undef MY_ESP8266_HOSTNAME // cleanup #endif -#ifndef MY_WIFI_BSSID -#define MY_WIFI_BSSID NULL +#ifndef MY_MQTT_USER +#define MY_MQTT_USER NULL +#endif + +#ifndef MY_MQTT_PASSWORD +#define MY_MQTT_PASSWORD NULL +#endif + +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#if !defined(MY_WIFI_SSID) +#error ESP8266/ESP32 MQTT gateway: MY_WIFI_SSID not defined! +#endif #endif #if defined MY_CONTROLLER_IP_ADDRESS -IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS); +#define _brokerIp IPAddress(MY_CONTROLLER_IP_ADDRESS) #endif #if defined(MY_IP_ADDRESS) -IPAddress _MQTT_clientIp(MY_IP_ADDRESS); +#define _MQTT_clientIp IPAddress(MY_IP_ADDRESS) #if defined(MY_IP_GATEWAY_ADDRESS) -IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); +#define _gatewayIp IPAddress(MY_IP_GATEWAY_ADDRESS) #elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) // Assume the gateway will be the machine on the same network as the local IP // but with last octet being '1' -IPAddress _gatewayIp(_MQTT_clientIp[0], _MQTT_clientIp[1], _MQTT_clientIp[2], 1); +#define _gatewayIp IPAddress(_MQTT_clientIp[0], _MQTT_clientIp[1], _MQTT_clientIp[2], 1) #endif /* End of MY_IP_GATEWAY_ADDRESS */ #if defined(MY_IP_SUBNET_ADDRESS) -IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); +#define _subnetIp IPAddress(MY_IP_SUBNET_ADDRESS) #elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) -IPAddress _subnetIp(255, 255, 255, 0); +#define _subnetIp IPAddress(255, 255, 255, 0) #endif /* End of MY_IP_SUBNET_ADDRESS */ #endif /* End of MY_IP_ADDRESS */ -#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) #define EthernetClient WiFiClient #elif defined(MY_GATEWAY_LINUX) // Nothing to do here #else uint8_t _MQTT_clientMAC[] = { MY_MAC_ADDRESS }; -#endif /* End of MY_GATEWAY_ESP8266 */ +#endif /* End of MY_GATEWAY_ESPxy */ #if defined(MY_GATEWAY_TINYGSM) #if defined(MY_GSM_RX) && defined(MY_GSM_TX) SoftwareSerial SerialAT(MY_GSM_RX, MY_GSM_TX); #endif static TinyGsm modem(SerialAT); -static TinyGsmClient _MQTT_gsmClient(modem); -static PubSubClient _MQTT_client(_MQTT_gsmClient); +static TinyGsmClient _MQTT_ethClient(modem); #if defined(MY_GSM_BAUDRATE) uint32_t rate = MY_GSM_BAUDRATE; #else /* Else part of MY_GSM_BAUDRATE */ @@ -94,10 +103,9 @@ uint32_t rate = 0; #endif /* End of MY_GSM_BAUDRATE */ #else /* Else part of MY_GATEWAY_TINYGSM */ static EthernetClient _MQTT_ethClient; -static PubSubClient _MQTT_client(_MQTT_ethClient); #endif /* End of MY_GATEWAY_TINYGSM */ - +static PubSubClient _MQTT_client(_MQTT_ethClient); static bool _MQTT_connecting = true; static bool _MQTT_available = false; static MyMessage _MQTT_msg; @@ -108,13 +116,13 @@ bool gatewayTransportSend(MyMessage &message) return false; } setIndication(INDICATION_GW_TX); - char *topic = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message); + char *topic = protocolMyMessage2MQTT(MY_MQTT_PUBLISH_TOPIC_PREFIX, message); GATEWAY_DEBUG(PSTR("GWT:TPS:TOPIC=%s,MSG SENT\n"), topic); #if defined(MY_MQTT_CLIENT_PUBLISH_RETAIN) - bool retain = mGetCommand(message) == C_SET || - (mGetCommand(message) == C_INTERNAL && message.type == I_BATTERY_LEVEL); + const bool retain = message.getCommand() == C_SET || + (message.getCommand() == C_INTERNAL && message.getType() == I_BATTERY_LEVEL); #else - bool retain = false; + const bool retain = false; #endif /* End of MY_MQTT_CLIENT_PUBLISH_RETAIN */ return _MQTT_client.publish(topic, message.getString(_convBuffer), retain); } @@ -122,41 +130,45 @@ bool gatewayTransportSend(MyMessage &message) void incomingMQTT(char *topic, uint8_t *payload, unsigned int length) { GATEWAY_DEBUG(PSTR("GWT:IMQ:TOPIC=%s, MSG RECEIVED\n"), topic); - _MQTT_available = protocolMQTTParse(_MQTT_msg, topic, payload, length); + _MQTT_available = protocolMQTT2MyMessage(_MQTT_msg, topic, payload, length); setIndication(INDICATION_GW_RX); } bool reconnectMQTT(void) { - GATEWAY_DEBUG(PSTR("GWT:RMQ:MQTT RECONNECT\n")); + GATEWAY_DEBUG(PSTR("GWT:RMQ:CONNECTING...\n")); // Attempt to connect - if (_MQTT_client.connect(MY_MQTT_CLIENT_ID -#if defined(MY_MQTT_USER) && defined(MY_MQTT_PASSWORD) - , MY_MQTT_USER, MY_MQTT_PASSWORD -#endif - )) { - GATEWAY_DEBUG(PSTR("GWT:RMQ:MQTT CONNECTED\n")); - + if (_MQTT_client.connect(MY_MQTT_CLIENT_ID, MY_MQTT_USER, MY_MQTT_PASSWORD)) { + GATEWAY_DEBUG(PSTR("GWT:RMQ:OK\n")); // Send presentation of locally attached sensors (and node if applicable) presentNode(); + // Once connected, publish subscribe + if (__builtin_constant_p(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX)) { + // to save some memory + _MQTT_client.subscribe(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "/+/+/+/+/+"); + } else { + char inTopic[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + strlen("/+/+/+/+/+")]; + (void)strncpy(inTopic, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX, strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1); + (void)strcat(inTopic, "/+/+/+/+/+"); + _MQTT_client.subscribe(inTopic); + } - // Once connected, publish an announcement... - //_MQTT_client.publish("outTopic","hello world"); - // ... and resubscribe - _MQTT_client.subscribe(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "/+/+/+/+/+"); return true; } + delay(1000); + GATEWAY_DEBUG(PSTR("!GWT:RMQ:FAIL\n")); return false; } bool gatewayTransportConnect(void) { #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) - while (WiFi.status() != WL_CONNECTED) { - wait(500); + if (WiFi.status() != WL_CONNECTED) { GATEWAY_DEBUG(PSTR("GWT:TPC:CONNECTING...\n")); + delay(1000); + return false; } - GATEWAY_DEBUG(PSTR("GWT:TPC:IP=%s\n"),WiFi.localIP().toString().c_str()); + GATEWAY_DEBUG(PSTR("GWT:TPC:IP=%s\n"), WiFi.localIP().toString().c_str()); #elif defined(MY_GATEWAY_LINUX) #if defined(MY_IP_ADDRESS) _MQTT_ethClient.bind(_MQTT_clientIp); @@ -234,31 +246,17 @@ bool gatewayTransportInit(void) _MQTT_client.setCallback(incomingMQTT); -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) // Turn off access point WiFi.mode(WIFI_STA); -#if defined(MY_HOSTNAME) +#if defined(MY_GATEWAY_ESP8266) WiFi.hostname(MY_HOSTNAME); -#endif /* End of MY_ESP8266_HOSTNAME */ -#if defined(MY_IP_ADDRESS) - WiFi.config(_MQTT_clientIp, _gatewayIp, _subnetIp); -#endif /* End of MY_IP_ADDRESS */ -#ifndef MY_WIFI_BSSID -#define MY_WIFI_BSSID NULL -#endif - (void)WiFi.begin(MY_WIFI_SSID, MY_WIFI_PASSWORD, 0, MY_WIFI_BSSID); #elif defined(MY_GATEWAY_ESP32) - // Turn off access point - WiFi.mode(WIFI_STA); -#if defined(MY_HOSTNAME) WiFi.setHostname(MY_HOSTNAME); -#endif /* End of MY_HOSTNAME */ +#endif #if defined(MY_IP_ADDRESS) WiFi.config(_MQTT_clientIp, _gatewayIp, _subnetIp); #endif /* End of MY_IP_ADDRESS */ -#ifndef MY_WIFI_BSSID -#define MY_WIFI_BSSID NULL -#endif (void)WiFi.begin(MY_WIFI_SSID, MY_WIFI_PASSWORD, 0, MY_WIFI_BSSID); #endif @@ -273,8 +271,14 @@ bool gatewayTransportAvailable(void) if (_MQTT_connecting) { return false; } - //keep lease on dhcp address - //Ethernet.maintain(); +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) + if (WiFi.status() != WL_CONNECTED) { +#if defined(MY_GATEWAY_ESP32) + (void)gatewayTransportInit(); +#endif + return false; + } +#endif if (!_MQTT_client.connected()) { //reinitialise client if (gatewayTransportConnect()) { diff --git a/core/MyGatewayTransportSerial.cpp b/core/MyGatewayTransportSerial.cpp index 9bab20e06..dec57b514 100644 --- a/core/MyGatewayTransportSerial.cpp +++ b/core/MyGatewayTransportSerial.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -33,7 +33,7 @@ MyMessage _serialMsg; bool gatewayTransportSend(MyMessage &message) { setIndication(INDICATION_GW_TX); - MY_SERIALDEVICE.print(protocolFormat(message)); + MY_SERIALDEVICE.print(protocolMyMessage2Serial(message)); // Serial print is always successful return true; } @@ -56,7 +56,7 @@ bool gatewayTransportAvailable(void) if (_serialInputPos < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) { if (inChar == '\n') { _serialInputString[_serialInputPos] = 0; - const bool ok = protocolParse(_serialMsg, _serialInputString); + const bool ok = protocolSerial2MyMessage(_serialMsg, _serialInputString); if (ok) { setIndication(INDICATION_GW_RX); } diff --git a/core/MyHelperFunctions.cpp b/core/MyHelperFunctions.cpp new file mode 100644 index 000000000..7ed22d405 --- /dev/null +++ b/core/MyHelperFunctions.cpp @@ -0,0 +1,41 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 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 "MyHelperFunctions.h" + +static uint8_t convertH2I(const char c) +{ + if (c <= '9') { + return c - '0'; + } else if (c >= 'a') { + return c - 'a' + 10; + } else { + return c - 'A' + 10; + } +} + +static char convertI2H(const uint8_t i) +{ + const uint8_t k = i & 0x0F; + if (k <= 9) { + return '0' + k; + } else { + return 'A' + k - 10; + } +} diff --git a/core/MyHelperFunctions.h b/core/MyHelperFunctions.h new file mode 100644 index 000000000..ea287da0e --- /dev/null +++ b/core/MyHelperFunctions.h @@ -0,0 +1,38 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 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 MyHelperFunctions_h +#define MyHelperFunctions_h + +/** +* Single character hex conversion +* @param c hex char +* @return byte representation of the paramter +*/ +static uint8_t convertH2I(const char c) __attribute__((unused)); + +/** +* Lower nibble byte to hex conversion +* @param i byte +* @return hex char representation of the parameter +*/ +static char convertI2H(const uint8_t i) __attribute__((unused)); + + +#endif diff --git a/core/MyInclusionMode.cpp b/core/MyInclusionMode.cpp index 365dfbaa9..8a27e0122 100644 --- a/core/MyInclusionMode.cpp +++ b/core/MyInclusionMode.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyInclusionMode.h b/core/MyInclusionMode.h index 8f2ed0e19..4b18e453c 100644 --- a/core/MyInclusionMode.h +++ b/core/MyInclusionMode.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyIndication.cpp b/core/MyIndication.cpp index 47f753bb0..271f93d8c 100644 --- a/core/MyIndication.cpp +++ b/core/MyIndication.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyIndication.h b/core/MyIndication.h index 310d1557c..04551642d 100644 --- a/core/MyIndication.h +++ b/core/MyIndication.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyLeds.cpp b/core/MyLeds.cpp index 739c8ea3b..b3a4e14be 100644 --- a/core/MyLeds.cpp +++ b/core/MyLeds.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyLeds.h b/core/MyLeds.h index 1b318e95a..b74c820c8 100644 --- a/core/MyLeds.h +++ b/core/MyLeds.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyMessage.cpp b/core/MyMessage.cpp index b749b182b..b67a0aa5a 100644 --- a/core/MyMessage.cpp +++ b/core/MyMessage.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,88 +19,258 @@ #include "MyMessage.h" +#include "MyHelperFunctions.h" #include #include #include MyMessage::MyMessage(void) { - clear(); + this->clear(); } -MyMessage::MyMessage(const uint8_t _sensor, const uint8_t _type) +MyMessage::MyMessage(const uint8_t _sensorId, const mysensors_data_t _dataType) { - clear(); - sensor = _sensor; - type = _type; + this->clear(); + (void)this->setSensor(_sensorId); + (void)this->setType(static_cast(_dataType)); } void MyMessage::clear(void) { - 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)); + this->last = 0u; + this->sender = 0u; + this->destination = GATEWAY_ADDRESS; // Gateway is default destination + this->version_length = 0u; + this->command_echo_payload = 0u; + this->type = 0u; + this->sensor = 0u; + // clear data buffer + (void)memset((void *)this->data, 0u, sizeof(this->data)); // set message protocol version - miSetVersion(PROTOCOL_VERSION); + (void)this->setVersion(); } +uint8_t MyMessage::getHeaderSize(void) const +{ + return (uint8_t)HEADER_SIZE; +} + +uint8_t MyMessage::getMaxPayloadSize(void) const +{ + return (uint8_t)MAX_PAYLOAD_SIZE; +} + +uint8_t MyMessage::getExpectedMessageSize(void) const +{ + return this->getHeaderSize() + (this->getSigned() ? this->getMaxPayloadSize() : this->getLength()); +} + +bool MyMessage::isProtocolVersionValid(void) const +{ + return (this->getVersion() == V2_MYS_HEADER_PROTOCOL_VERSION); +} + +uint8_t MyMessage::getType(void) const +{ + return this->type; +} + +MyMessage& MyMessage::setType(const uint8_t messageType) +{ + this->type = messageType; + return *this; +} + +uint8_t MyMessage::getLast(void) const +{ + return this->last; +} + +MyMessage& MyMessage::setLast(const uint8_t lastId) +{ + this->last = lastId; + return *this; +} + +uint8_t MyMessage::getSender(void) const +{ + return this->sender; +} + +MyMessage& MyMessage::setSender(const uint8_t senderId) +{ + this->sender = senderId; + return *this; +} + +uint8_t MyMessage::getSensor(void) const +{ + return this->sensor; +} + +MyMessage& MyMessage::setSensor(const uint8_t sensorId) +{ + this->sensor = sensorId; + return *this; +} + +uint8_t MyMessage::getDestination(void) const +{ + return this->destination; +} + +MyMessage& MyMessage::setDestination(const uint8_t destinationId) +{ + this->destination = destinationId; + return *this; +} + +// TODO: Remove before v3 is released, use isEcho instead bool MyMessage::isAck(void) const { - return miGetAck(); + return this->isEcho(); } -uint8_t MyMessage::getCommand(void) const +bool MyMessage::isEcho(void) const { - return miGetCommand(); + return (bool)BF_GET(this->command_echo_payload, V2_MYS_HEADER_CEP_ECHO_POS, + V2_MYS_HEADER_CEP_ECHO_SIZE); } -/* Getters for payload converted to desired form */ -void* MyMessage::getCustom(void) const +MyMessage& MyMessage::setEcho(const bool echo) { - return (void *)data; + BF_SET(this->command_echo_payload, echo, V2_MYS_HEADER_CEP_ECHO_POS, + V2_MYS_HEADER_CEP_ECHO_SIZE); + return *this; } -const char* MyMessage::getString(void) const +bool MyMessage::getRequestEcho(void) const { - uint8_t payloadType = miGetPayloadType(); - if (payloadType == P_STRING) { - return data; - } else { - return NULL; + return (bool)BF_GET(this->command_echo_payload, V2_MYS_HEADER_CEP_ECHOREQUEST_POS, + V2_MYS_HEADER_CEP_ECHOREQUEST_SIZE); +} + +MyMessage& MyMessage::setRequestEcho(const bool requestEcho) +{ + BF_SET(this->command_echo_payload, requestEcho, V2_MYS_HEADER_CEP_ECHOREQUEST_POS, + V2_MYS_HEADER_CEP_ECHOREQUEST_SIZE); + return *this; +} + +uint8_t MyMessage::getVersion(void) const +{ + return (uint8_t)BF_GET(this->version_length, V2_MYS_HEADER_VSL_VERSION_POS, + V2_MYS_HEADER_VSL_VERSION_SIZE); +} + +MyMessage& MyMessage::setVersion(void) +{ + BF_SET(this->version_length, V2_MYS_HEADER_PROTOCOL_VERSION, V2_MYS_HEADER_VSL_VERSION_POS, + V2_MYS_HEADER_VSL_VERSION_SIZE); + return *this; +} + +mysensors_command_t MyMessage::getCommand(void) const +{ + return static_cast(BF_GET(this->command_echo_payload, + V2_MYS_HEADER_CEP_COMMAND_POS, V2_MYS_HEADER_CEP_COMMAND_SIZE)); +} + +MyMessage& MyMessage::setCommand(const mysensors_command_t command) +{ + BF_SET(this->command_echo_payload, static_cast(command), V2_MYS_HEADER_CEP_COMMAND_POS, + V2_MYS_HEADER_CEP_COMMAND_SIZE); + return *this; +} + +mysensors_payload_t MyMessage::getPayloadType(void) const +{ + return static_cast(BF_GET(this->command_echo_payload, + V2_MYS_HEADER_CEP_PAYLOADTYPE_POS, V2_MYS_HEADER_CEP_PAYLOADTYPE_SIZE)); +} + +MyMessage& MyMessage::setPayloadType(const mysensors_payload_t payloadType) +{ + BF_SET(this->command_echo_payload, static_cast(payloadType), + V2_MYS_HEADER_CEP_PAYLOADTYPE_POS, V2_MYS_HEADER_CEP_PAYLOADTYPE_SIZE); + return *this; +} + +bool MyMessage::getSigned(void) const +{ + return (bool)BF_GET(this->version_length, V2_MYS_HEADER_VSL_SIGNED_POS, + V2_MYS_HEADER_VSL_SIGNED_SIZE); +} + +MyMessage& MyMessage::setSigned(const bool signedFlag) +{ + BF_SET(this->version_length, signedFlag, V2_MYS_HEADER_VSL_SIGNED_POS, + V2_MYS_HEADER_VSL_SIGNED_SIZE); + return *this; +} + +uint8_t MyMessage::getLength(void) const +{ + uint8_t length = BF_GET(this->version_length, V2_MYS_HEADER_VSL_LENGTH_POS, + V2_MYS_HEADER_VSL_LENGTH_SIZE); + // limit length + if (length > MAX_PAYLOAD_SIZE) { + length = MAX_PAYLOAD_SIZE; } + return length; +} + +MyMessage& MyMessage::setLength(const uint8_t length) +{ + uint8_t finalLength = length; + // limit length + if (finalLength > MAX_PAYLOAD_SIZE) { + finalLength = MAX_PAYLOAD_SIZE; + } + + BF_SET(this->version_length, finalLength, V2_MYS_HEADER_VSL_LENGTH_POS, + V2_MYS_HEADER_VSL_LENGTH_SIZE); + return *this; } -char MyMessage::i2h(const uint8_t i) const +/* Getters for payload converted to desired form */ +void* MyMessage::getCustom(void) const +{ + return (void *)this->data; +} + +const char* MyMessage::getString(void) const { - uint8_t k = i & 0x0F; - if (k <= 9) { - return '0' + k; + if (this->getPayloadType() == P_STRING) { + return this->data; } else { - return 'A' + k - 10; + return NULL; } } char* MyMessage::getCustomString(char *buffer) const { - for (uint8_t i = 0; i < miGetLength(); i++) { - buffer[i * 2] = i2h(data[i] >> 4); - buffer[(i * 2) + 1] = i2h(data[i]); + if (buffer != NULL) { + for (uint8_t i = 0; i < this->getLength(); i++) { + buffer[i * 2] = convertI2H(this->data[i] >> 4); + buffer[(i * 2) + 1] = convertI2H(this->data[i]); + } + buffer[this->getLength() * 2] = '\0'; + return buffer; + } else { + return NULL; } - buffer[miGetLength() * 2] = '\0'; - return buffer; } char* MyMessage::getStream(char *buffer) const { - uint8_t cmd = miGetCommand(); - if ((cmd == C_STREAM) && (buffer != NULL)) { - return getCustomString(buffer); + if (buffer != NULL) { + if (this->getCommand() == C_STREAM) { + return this->getCustomString(buffer); + } + return buffer; } else { return NULL; } @@ -108,11 +278,11 @@ char* MyMessage::getStream(char *buffer) const char* MyMessage::getString(char *buffer) const { - uint8_t payloadType = miGetPayloadType(); if (buffer != NULL) { + const uint8_t payloadType = this->getPayloadType(); if (payloadType == P_STRING) { - (void)strncpy(buffer, data, miGetLength()); - buffer[miGetLength()] = 0; + (void)strncpy(buffer, this->data, this->getLength()); + buffer[this->getLength()] = 0; } else if (payloadType == P_BYTE) { (void)itoa(bValue, buffer, 10); } else if (payloadType == P_INT16) { @@ -124,7 +294,7 @@ char* MyMessage::getString(char *buffer) const } else if (payloadType == P_ULONG32) { (void)ultoa(ulValue, buffer, 10); } else if (payloadType == P_FLOAT32) { - (void)dtostrf(fValue, 2, min(fPrecision, (uint8_t)8), buffer); + (void)dtostrf(fValue, 2, min(fPrecision, (uint8_t)8u), buffer); } else if (payloadType == P_CUSTOM) { return getCustomString(buffer); } @@ -136,15 +306,15 @@ char* MyMessage::getString(char *buffer) const bool MyMessage::getBool(void) const { - return getByte(); + return (bool)this->getByte(); } uint8_t MyMessage::getByte(void) const { - if (miGetPayloadType() == P_BYTE) { - return data[0]; - } else if (miGetPayloadType() == P_STRING) { - return atoi(data); + if (this->getPayloadType() == P_BYTE) { + return (uint8_t)this->data[0]; + } else if (this->getPayloadType() == P_STRING) { + return (uint8_t)atoi(this->data); } else { return 0; } @@ -153,10 +323,10 @@ uint8_t MyMessage::getByte(void) const float MyMessage::getFloat(void) const { - if (miGetPayloadType() == P_FLOAT32) { - return fValue; - } else if (miGetPayloadType() == P_STRING) { - return atof(data); + if (this->getPayloadType() == P_FLOAT32) { + return this->fValue; + } else if (this->getPayloadType() == P_STRING) { + return (float)atof(this->data); } else { return 0; } @@ -164,10 +334,10 @@ float MyMessage::getFloat(void) const int32_t MyMessage::getLong(void) const { - if (miGetPayloadType() == P_LONG32) { - return lValue; - } else if (miGetPayloadType() == P_STRING) { - return atol(data); + if (this->getPayloadType() == P_LONG32) { + return this->lValue; + } else if (this->getPayloadType() == P_STRING) { + return (int32_t)atol(this->data); } else { return 0; } @@ -175,10 +345,10 @@ int32_t MyMessage::getLong(void) const uint32_t MyMessage::getULong(void) const { - if (miGetPayloadType() == P_ULONG32) { - return ulValue; - } else if (miGetPayloadType() == P_STRING) { - return atol(data); + if (this->getPayloadType() == P_ULONG32) { + return this->ulValue; + } else if (this->getPayloadType() == P_STRING) { + return (uint32_t)atol(this->data); } else { return 0; } @@ -186,10 +356,10 @@ uint32_t MyMessage::getULong(void) const int16_t MyMessage::getInt(void) const { - if (miGetPayloadType() == P_INT16) { - return iValue; - } else if (miGetPayloadType() == P_STRING) { - return atoi(data); + if (this->getPayloadType() == P_INT16) { + return this->iValue; + } else if (this->getPayloadType() == P_STRING) { + return (int16_t)atoi(this->data); } else { return 0; } @@ -197,69 +367,41 @@ int16_t MyMessage::getInt(void) const uint16_t MyMessage::getUInt(void) const { - if (miGetPayloadType() == P_UINT16) { - return uiValue; - } else if (miGetPayloadType() == P_STRING) { - return atoi(data); + if (this->getPayloadType() == P_UINT16) { + return this->uiValue; + } else if (this->getPayloadType() == P_STRING) { + return (uint16_t)atoi(this->data); } else { return 0; } - } -MyMessage& MyMessage::setType(const uint8_t _type) +MyMessage& MyMessage::set(const void* value, const size_t _length) { - type = _type; - return *this; -} - -MyMessage& MyMessage::setSensor(const uint8_t _sensor) -{ - sensor = _sensor; - return *this; -} - -MyMessage& MyMessage::setDestination(const uint8_t _destination) -{ - destination = _destination; - return *this; -} - -// Set payload -MyMessage& MyMessage::set(const void* value, const uint8_t length) -{ - uint8_t payloadLength = value == NULL ? 0 : min(length, (uint8_t)MAX_PAYLOAD); - miSetLength(payloadLength); - miSetPayloadType(P_CUSTOM); - memcpy(data, value, payloadLength); + (void)this->setLength((value != NULL) ? _length : 0); + (void)this->setPayloadType(P_CUSTOM); + (void)memcpy((void *)this->data, value, this->getLength()); return *this; } MyMessage& MyMessage::set(const char* value) { - uint8_t length = value == NULL ? 0 : min(strlen(value), (size_t)MAX_PAYLOAD); - miSetLength(length); - miSetPayloadType(P_STRING); - if (length) { - strncpy(data, value, length); - } + (void)this->setLength((value != NULL) ? strlen(value) : 0); + (void)this->setPayloadType(P_STRING); + (void)strncpy(this->data, value, this->getLength()); // null terminate string - data[length] = 0; + this->data[this->getLength()] = 0; return *this; } #if !defined(__linux__) MyMessage& MyMessage::set(const __FlashStringHelper* value) { - uint8_t length = value == NULL ? 0 - : min(strlen_P(reinterpret_cast(value)), (size_t)MAX_PAYLOAD); - miSetLength(length); - miSetPayloadType(P_STRING); - if (length) { - strncpy_P(data, reinterpret_cast(value), length); - } + (void)this->setLength((value != NULL) ? strlen_P(reinterpret_cast(value)) : 0); + (void)this->setPayloadType(P_STRING); + (void)strncpy_P(this->data, reinterpret_cast(value), this->getLength()); // null terminate string - data[length] = 0; + this->data[this->getLength()] = 0; return *this; } #endif @@ -267,57 +409,54 @@ MyMessage& MyMessage::set(const __FlashStringHelper* value) MyMessage& MyMessage::set(const bool value) { - miSetLength(1); - miSetPayloadType(P_BYTE); - data[0] = value; - return *this; + return this->set((uint8_t)value); } MyMessage& MyMessage::set(const uint8_t value) { - miSetLength(1); - miSetPayloadType(P_BYTE); - data[0] = value; + (void)this->setLength(1u); + (void)this->setPayloadType(P_BYTE); + this->bValue = value; return *this; } MyMessage& MyMessage::set(const float value, const uint8_t decimals) { - miSetLength(5); // 32 bit float + persi - miSetPayloadType(P_FLOAT32); - fValue=value; - fPrecision = decimals; + (void)this->setLength(5u); // 32 bit float + persi + (void)this->setPayloadType(P_FLOAT32); + this->fValue = value; + this->fPrecision = decimals; return *this; } MyMessage& MyMessage::set(const uint32_t value) { - miSetPayloadType(P_ULONG32); - miSetLength(4); - ulValue = value; + (void)this->setLength(4u); + (void)this->setPayloadType(P_ULONG32); + this->ulValue = value; return *this; } MyMessage& MyMessage::set(const int32_t value) { - miSetPayloadType(P_LONG32); - miSetLength(4); - lValue = value; + (void)this->setLength(4u); + (void)this->setPayloadType(P_LONG32); + this->lValue = value; return *this; } MyMessage& MyMessage::set(const uint16_t value) { - miSetPayloadType(P_UINT16); - miSetLength(2); - uiValue = value; + (void)this->setLength(2u); + (void)this->setPayloadType(P_UINT16); + this->uiValue = value; return *this; } MyMessage& MyMessage::set(const int16_t value) { - miSetPayloadType(P_INT16); - miSetLength(2); - iValue = value; + (void)this->setLength(2u); + (void)this->setPayloadType(P_INT16); + this->iValue = value; return *this; } diff --git a/core/MyMessage.h b/core/MyMessage.h index 83824008e..db3627a37 100644 --- a/core/MyMessage.h +++ b/core/MyMessage.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -36,10 +36,32 @@ #include #endif -#define PROTOCOL_VERSION (2u) //!< The version of the protocol -#define MAX_MESSAGE_LENGTH (32u) //!< The maximum size of a message (including header) -#define HEADER_SIZE (7u) //!< The size of the header -#define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) //!< The maximum size of a payload depends on #MAX_MESSAGE_LENGTH and #HEADER_SIZE +#define V2_MYS_HEADER_PROTOCOL_VERSION (2u) //!< Protocol version +#define V2_MYS_HEADER_SIZE (7u) //!< Header size +#define V2_MYS_HEADER_MAX_MESSAGE_SIZE (32u) //!< Max payload size + +#define V2_MYS_HEADER_VSL_VERSION_POS (0) //!< bitfield position version +#define V2_MYS_HEADER_VSL_VERSION_SIZE (2u) //!< size version field +#define V2_MYS_HEADER_VSL_SIGNED_POS (2u) //!< bitfield position signed field +#define V2_MYS_HEADER_VSL_SIGNED_SIZE (1u) //!< size signed field +#define V2_MYS_HEADER_VSL_LENGTH_POS (3u) //!< bitfield position length field +#define V2_MYS_HEADER_VSL_LENGTH_SIZE (5u) //!< size length field + +#define V2_MYS_HEADER_CEP_COMMAND_POS (0) //!< bitfield position command field +#define V2_MYS_HEADER_CEP_COMMAND_SIZE (3u) //!< size command field +#define V2_MYS_HEADER_CEP_ECHOREQUEST_POS (3u) //!< bitfield position echo request field +#define V2_MYS_HEADER_CEP_ECHOREQUEST_SIZE (1u) //!< size echo request field +#define V2_MYS_HEADER_CEP_ECHO_POS (4u) //!< bitfield position echo field +#define V2_MYS_HEADER_CEP_ECHO_SIZE (1u) //!< size echo field +#define V2_MYS_HEADER_CEP_PAYLOADTYPE_POS (5u) //!< bitfield position payload type field +#define V2_MYS_HEADER_CEP_PAYLOADTYPE_SIZE (3u) //!< size payload type field + +#define MAX_MESSAGE_SIZE V2_MYS_HEADER_MAX_MESSAGE_SIZE //!< The maximum size of a message (including header) +#define HEADER_SIZE V2_MYS_HEADER_SIZE //!< The size of the header +#define MAX_PAYLOAD_SIZE (MAX_MESSAGE_SIZE - HEADER_SIZE) //!< The maximum size of a payload depends on #MAX_MESSAGE_SIZE and #HEADER_SIZE + +// deprecated in 3.0.0 +#define MAX_PAYLOAD MAX_PAYLOAD_SIZE //!< \deprecated in 3.0.0 The maximum size of a payload depends on #MAX_MESSAGE_SIZE and #HEADER_SIZE /// @brief The command field (message-type) defines the overall properties of a message typedef enum { @@ -47,7 +69,10 @@ typedef enum { C_SET = 1, //!< This message is sent from or to a sensor when a sensor value should be updated. C_REQ = 2, //!< Requests a variable value (usually from an actuator destined for controller). C_INTERNAL = 3, //!< Internal MySensors messages (also include common messages provided/generated by the library). - C_STREAM = 4 //!< For firmware and other larger chunks of data that need to be divided into pieces. + C_STREAM = 4, //!< For firmware and other larger chunks of data that need to be divided into pieces. + C_RESERVED_5 = 5, //!< C_RESERVED_5 + C_RESERVED_6 = 6, //!< C_RESERVED_6 + C_INVALID_7 = 7 //!< C_INVALID_7 } mysensors_command_t; #if !DOXYGEN // Hide until we migrate @@ -96,7 +121,7 @@ typedef enum { S_WATER_QUALITY = 39 //!< V_TEMP, V_PH, V_ORP, V_EC, V_STATUS } mysensors_sensor_t; -/// @brief Type of sensor data (for set/req/ack messages) +/// @brief Type of sensor data (for set/req/echo messages) typedef enum { V_TEMP = 0, //!< S_TEMP. Temperature S_TEMP, S_HEATER, S_HVAC V_HUM = 1, //!< S_HUM. Humidity @@ -161,7 +186,6 @@ typedef enum { } mysensors_data_t; #endif - /// @brief Type of internal messages (for internal messages) typedef enum { I_BATTERY_LEVEL = 0, //!< Battery level @@ -200,8 +224,7 @@ typedef enum { I_POST_SLEEP_NOTIFICATION = 33 //!< Message sent after node woke up (if enabled) } mysensors_internal_t; - -/// @brief Type of data stream (for streamed message) +/// @brief Type of data stream (for streamed message) typedef enum { ST_FIRMWARE_CONFIG_REQUEST = 0, //!< Request new FW, payload contains current FW details ST_FIRMWARE_CONFIG_RESPONSE = 1, //!< New FW details to initiate OTA FW update @@ -226,7 +249,6 @@ typedef enum { } mysensors_payload_t; - #ifndef BIT #define BIT(n) ( 1<<(n) ) //!< Bit indexing macro #endif @@ -238,46 +260,27 @@ typedef enum { #define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) //!< Insert a new bitfield value 'x' into 'y' // Getters/setters for special bit fields in header -#define mSetVersion(_message,_version) BF_SET(_message.version_length, _version, 0, 2) //!< Set version field -#define mGetVersion(_message) ((uint8_t)BF_GET(_message.version_length, 0, 2)) //!< Get version field - -#define mSetSigned(_message,_signed) BF_SET(_message.version_length, _signed, 2, 1) //!< Set signed field -#define mGetSigned(_message) ((bool)BF_GET(_message.version_length, 2, 1)) //!< Get signed field - -#define mSetLength(_message,_length) BF_SET(_message.version_length, _length, 3, 5) //!< Set length field -#define mGetLength(_message) ((uint8_t)BF_GET(_message.version_length, 3, 5)) //!< Get length field - -#define mSetCommand(_message,_command) BF_SET(_message.command_ack_payload, _command, 0, 3) //!< Set command field -#define mGetCommand(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 0, 3)) //!< Get command field - -#define mSetRequestAck(_message,_rack) BF_SET(_message.command_ack_payload, _rack, 3, 1) //!< Set ack-request field -#define mGetRequestAck(_message) ((bool)BF_GET(_message.command_ack_payload, 3, 1)) //!< Get ack-request field - -#define mSetAck(_message,_ackMsg) BF_SET(_message.command_ack_payload, _ackMsg, 4, 1) //!< Set ack field -#define mGetAck(_message) ((bool)BF_GET(_message.command_ack_payload, 4, 1)) //!< Get ack field - -#define mSetPayloadType(_message, _pt) BF_SET(_message.command_ack_payload, _pt, 5, 3) //!< Set payload type field -#define mGetPayloadType(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 5, 3)) //!< Get payload type field - +// deprecated in 3.0.0 +#define mSetVersion(_message, _version) _message.setVersion(_version) //!< \deprecated Set version field +#define mGetVersion(_message) _message.getVersion() //!< \deprecated Get version field -// internal access for special fields -#define miGetCommand() ((uint8_t)BF_GET(command_ack_payload, 0, 3)) //!< Internal getter for command field +#define mSetSigned(_message, _signed) _message.setSigned(_signed) //!< \deprecated Set signed field +#define mGetSigned(_message) _message.getSigned() //!< \deprecated Get signed 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 mSetLength(_message,_length) _message.setLength(_length) //!< \deprecated Set length field +#define mGetLength(_message) _message.getLength() //!< \deprecated Get 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 mSetCommand(_message, _command) _message.setCommand(_command) //!< \deprecated Set command field +#define mGetCommand(_message) _message.getCommand() //!< \deprecated Get command 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 mSetRequestEcho(_message, _requestEcho) _message.setRequestEcho(_requestEcho) //!< \deprecated Set echo request field +#define mGetRequestEcho(_message) _message.getRequestEcho() //!< \deprecated Get echo request field -#define miSetAck(_ack) BF_SET(command_ack_payload, _ack, 4, 1) //!< Internal setter for ack field -#define miGetAck() ((bool)BF_GET(command_ack_payload, 4, 1)) //!< Internal getter for ack field - -#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 5, 3) //!< Internal setter for payload type field -#define miGetPayloadType() (uint8_t)BF_GET(command_ack_payload, 5, 3) //!< Internal getter for payload type field +#define mSetEcho(_message, _echo) _message.setEcho(_echo) //!< \deprecated Set echo field +#define mGetEcho(_message) _message.getEcho() //!< \deprecated Get echo field +#define mSetPayloadType(_message, _payloadType) _message.setPayloadType(_payloadType) //!< \deprecated Set payload type field +#define mGetPayloadType(_message) _message.getPayloadType() //!< \deprecated Get payload type field #if defined(__cplusplus) || defined(DOXYGEN) /** @@ -289,6 +292,7 @@ class MyMessage char* getCustomString(char *buffer) const; public: + /** * Default constructor */ @@ -296,17 +300,10 @@ class MyMessage /** * Constructor - * @param sensor id of the child sensor for this message - * @param type see http://korturl.nu/stupidurl - */ - MyMessage(const uint8_t sensor, const uint8_t type); - - /** - * Single character hex (0 - 15) conversion - * @param i byte (only lower 4 bits will be considered) - * @return single char with the hex representation (0 to F) of the parameter + * @param sensorId id of the child sensor for this message + * @param dataType */ - char i2h(const uint8_t i) const; + MyMessage(const uint8_t sensorId, const mysensors_data_t dataType); /** * @brief Clear message contents. @@ -316,8 +313,8 @@ class MyMessage /** * 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 - * 2*MAX_PAYLOAD+1. This is to be able to fit hex-conversion of a full binary payload. - * @param buffer pointer to a buffer that's at least 2*MAX_PAYLOAD+1 bytes large + * 2 * MAX_PAYLOAD_SIZE + 1. This is to be able to fit hex-conversion of a full binary payload. + * @param buffer pointer to a buffer that's at least 2 * MAX_PAYLOAD_SIZE + 1 bytes large */ char* getStream(char *buffer) const; @@ -381,41 +378,185 @@ class MyMessage uint32_t getULong(void) const; /** - * @brief Getter for command type - * @return #mysensors_command_t + * @brief getHeaderSize + * @return the size of the header + */ + uint8_t getHeaderSize(void) const; + + /** + * @brief getMaxPayloadSize + * @return the max. size of the payload + */ + uint8_t getMaxPayloadSize(void) const; + + /** + * @brief getExpectedMessageSize + * @return the expected message size based on header information + */ + uint8_t getExpectedMessageSize(void) const; + + /** + * @brief isProtocolVersionValid + * @return true if the protocol version is valid + */ + bool isProtocolVersionValid(void) const; + + /** + * @brief Getter for echo request + * @return echo request + */ + bool getRequestEcho(void) const; + + /** + * @brief Setter for echo request + * @param requestEcho */ - uint8_t getCommand(void) const; + MyMessage& setRequestEcho(const bool requestEcho); + + /** + * @brief Getter for version + * @return version + */ + uint8_t getVersion(void) const; + + /** + * @brief Setter for version + */ + MyMessage& setVersion(void); + + /** + * @brief Getter for length + * @return length + */ + uint8_t getLength(void) const; + + /** + * @brief Setter for length + * @param length + */ + MyMessage& setLength(const uint8_t length); + + /** + * @brief Getter for command type + * @return #mysensors_command_t + */ + mysensors_command_t getCommand(void) const; /** - * @brief Getter for ack-flag. - * @return true if this is an ack message + * @brief Setter for command type + * @param command + */ + MyMessage& setCommand(const mysensors_command_t command); + + /** + * @brief Getter for payload type + * @return payload type + */ + mysensors_payload_t getPayloadType(void) const; + + /** + * @brief Setter for payload type + * @param payloadType + */ + MyMessage& setPayloadType(const mysensors_payload_t payloadType); + + /** + * @brief Getter for sign field + * @return sign field + */ + bool getSigned(void) const; + + /** + * @brief Setter for sign field + * @param signedFlag + */ + MyMessage& setSigned(const bool signedFlag); + + /** + * \deprecated use isEcho() + * @brief Getter for echo-flag. + * @return true if this is an echoed message */ bool isAck(void) const; + /** + * @brief Getter for echo-flag. + * @return true if this is an echoed message + */ + bool isEcho(void) const; + + /** + * @brief Setter for echo-flag. + * @param echo true if this an echo message + */ + MyMessage& setEcho(const bool echo); + + /** + * @brief Get message type + * @return messageType + */ + uint8_t getType(void) const; + /** * @brief Set message type - * @param type see http://korturl.nu/stupidurl + * @param messageType + */ + MyMessage& setType(const uint8_t messageType); + + /** + * @brief Get last ID + * @return lastId + */ + uint8_t getLast(void) const; + + /** + * @brief Set last ID + * @param lastId */ - MyMessage& setType(const uint8_t type); + MyMessage& setLast(const uint8_t lastId); + + /** + * @brief Get sender ID + * @return sender + */ + uint8_t getSender(void) const; + + /** + * @brief Set sender ID + * @param senderId + */ + MyMessage& setSender(const uint8_t senderId); + + /** + * @brief Get sensor ID of message + * @return sensorId + */ + uint8_t getSensor(void) const; /** * @brief Set which child sensor this message belongs to - * @param sensor + * @param sensorId */ - MyMessage& setSensor(const uint8_t sensor); + MyMessage& setSensor(const uint8_t sensorId); + + /** + * @brief Get destination + * @return destinationId + */ + uint8_t getDestination(void) const; /** * @brief Set final destination node id for this message - * @param destination + * @param destinationId */ - MyMessage& setDestination(const uint8_t destination); + MyMessage& setDestination(const uint8_t destinationId); /** * @brief Set entire payload * @param payload pointer to the buffer where the payload is stored * @param length of the payload */ - MyMessage& set(const void* payload, const uint8_t length); + MyMessage& set(const void* payload, const size_t length); /** * @brief Set payload to character array @@ -492,11 +633,11 @@ typedef union { /** * 3 bit - Command type
- * 1 bit - Request an ack - Indicator that receiver should send an ack back
- * 1 bit - Is ack message - Indicator that this is the actual ack message
+ * 1 bit - Request an echo - Indicator that receiver should echo the message back to the sender
+ * 1 bit - Is echo message - Indicator that this is the echoed message
* 3 bit - Payload data type */ - uint8_t command_ack_payload; + uint8_t command_echo_payload; uint8_t type; //!< 8 bit - Type varies depending on command uint8_t sensor; //!< 8 bit - Id of sensor that this message concerns. @@ -516,17 +657,13 @@ typedef union { float fValue; uint8_t fPrecision; //!< Number of decimals when serializing }; - struct { //!< Presentation messages - uint8_t version; //!< Library version - uint8_t sensorType; //!< Sensor type hint for controller, see table above - }; - char data[MAX_PAYLOAD + 1]; //!< Buffer for raw payload data + char data[MAX_PAYLOAD_SIZE + 1]; //!< Buffer for raw payload data } __attribute__((packed)); //!< Doxygen will complain without this comment #if defined(__cplusplus) || defined(DOXYGEN) } __attribute__((packed)); #else }; -uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1]; //!< buffer for entire message +uint8_t array[HEADER_SIZE + MAX_PAYLOAD_SIZE + 1]; //!< buffer for entire message } __attribute__((packed)) MyMessage; #endif diff --git a/core/MyOTAFirmwareUpdate.cpp b/core/MyOTAFirmwareUpdate.cpp index 25bdbd75a..b1dbccc65 100644 --- a/core/MyOTAFirmwareUpdate.cpp +++ b/core/MyOTAFirmwareUpdate.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -85,7 +85,7 @@ LOCAL void firmwareOTAUpdateRequest(void) LOCAL bool firmwareOTAUpdateProcess(void) { - if (_msg.type == ST_FIRMWARE_CONFIG_RESPONSE) { + if (_msg.getType() == ST_FIRMWARE_CONFIG_RESPONSE) { if(_firmwareUpdateOngoing) { OTA_DEBUG(PSTR("!OTA:FWP:UPDO\n")); // FW config response received, FW update already ongoing return true; @@ -116,13 +116,13 @@ LOCAL bool firmwareOTAUpdateProcess(void) return true; } 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.getType() == ST_FIRMWARE_RESPONSE) { // extract FW block replyFirmwareBlock_t *firmwareResponse = (replyFirmwareBlock_t *)_msg.data; // Proceed firmware data return _firmwareResponse(firmwareResponse->block, firmwareResponse->data); #ifdef FIRMWARE_PROTOCOL_31 - } else if (_msg.type == ST_FIRMWARE_RESPONSE_RLE) { + } else if (_msg.getType() == ST_FIRMWARE_RESPONSE_RLE) { // RLE encoded block // extract FW block replyFirmwareBlockRLE_t *firmwareResponse = (replyFirmwareBlockRLE_t *)_msg.data; @@ -139,15 +139,15 @@ LOCAL bool firmwareOTAUpdateProcess(void) #endif } else { #ifdef MCUBOOT_PRESENT - if (_msg.type == ST_FIRMWARE_CONFIRM) { + if (_msg.getType() == ST_FIRMWARE_CONFIRM) { if (*(uint16_t *)MCUBOOT_IMAGE_0_MAGIC_ADDR == ((uint16_t)MCUBOOT_IMAGE_MAGIC)) { - if (*(uint8_t*)(MCUBOOT_IMAGE_0_IMG_OK_ADDR)!=MCUBOOT_IMAGE_0_IMG_OK_BYTE) { + if (*(uint8_t *)(MCUBOOT_IMAGE_0_IMG_OK_ADDR) != MCUBOOT_IMAGE_0_IMG_OK_BYTE) { // Calculate data word to write - uint32_t *img_ok_base_addr = (uint32_t*)(MCUBOOT_IMAGE_0_IMG_OK_ADDR & ~3); // align word wise + uint32_t *img_ok_base_addr = (uint32_t *)(MCUBOOT_IMAGE_0_IMG_OK_ADDR & ~3); // align word wise uint32_t img_ok_data = *img_ok_base_addr; // Set copy of MCUBOOT_IMAGE_0_IMG_OK_ADDR to MCUBOOT_IMAGE_0_IMG_OK_BYTE (0x01) - uint8_t * img_ok_array = (uint8_t*)(&img_ok_data); - img_ok_array[MCUBOOT_IMAGE_0_IMG_OK_ADDR % 4] = MCUBOOT_IMAGE_0_IMG_OK_BYTE; + uint8_t *img_ok_array = (uint8_t *)&img_ok_data; + *(img_ok_array + (MCUBOOT_IMAGE_0_IMG_OK_ADDR % 4)) = MCUBOOT_IMAGE_0_IMG_OK_BYTE; // Write word back Flash.write(img_ok_base_addr, img_ok_data); } @@ -164,9 +164,9 @@ LOCAL bool firmwareOTAUpdateProcess(void) LOCAL void presentBootloaderInformation(void) { requestFirmwareConfig_t *requestFirmwareConfig = (requestFirmwareConfig_t *)_msgTmp.data; - mSetLength(_msgTmp, sizeof(requestFirmwareConfig_t)); - mSetCommand(_msgTmp, C_STREAM); - mSetPayloadType(_msgTmp, P_CUSTOM); + _msgTmp.setLength(sizeof(requestFirmwareConfig_t)); + _msgTmp.setCommand(C_STREAM); + _msgTmp.setPayloadType(P_CUSTOM); // copy node settings to reqFWConfig (void)memcpy(requestFirmwareConfig, &_nodeFirmwareConfig, sizeof(nodeFirmwareConfig_t)); // add bootloader information diff --git a/core/MyOTAFirmwareUpdate.h b/core/MyOTAFirmwareUpdate.h index 9295766c9..880291b58 100644 --- a/core/MyOTAFirmwareUpdate.h +++ b/core/MyOTAFirmwareUpdate.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -65,7 +65,7 @@ #define LOCAL static //!< static -#if MAX_PAYLOAD >= 22 +#if MAX_PAYLOAD_SIZE >= 22 #define FIRMWARE_BLOCK_SIZE (16u) //!< Size of each firmware block #else #define FIRMWARE_BLOCK_SIZE (8u) //!< Size of each firmware block @@ -73,9 +73,12 @@ #define FIRMWARE_PROTOCOL_31 #endif #endif -#define FIRMWARE_MAX_REQUESTS (5u) //!< Number of times a firmware block should be requested before giving up +#ifndef MY_OTA_RETRY #define MY_OTA_RETRY (5u) //!< Number of times to request a fw block before giving up +#endif +#ifndef MY_OTA_RETRY_DELAY #define MY_OTA_RETRY_DELAY (500u) //!< Number of milliseconds before re-requesting a FW block +#endif #ifndef MCUBOOT_PRESENT #define FIRMWARE_START_OFFSET (10u) //!< Start offset for firmware in flash (DualOptiboot wants to keeps a signature first) #else @@ -103,7 +106,7 @@ * * Supports smaller FIRMWARE_BLOCK_SIZE, RLE and NVM for nRF5 with mcuboot. The * extension is enabled per default when mcuboot is present or full FIRMWARE_BLOCK_SIZE - * exeeds MAX_PAYLOAD. + * exeeds MAX_PAYLOAD_SIZE. */ #define FIRMWARE_PROTOCOL_31 #endif diff --git a/core/MyOTALogging.cpp b/core/MyOTALogging.cpp index 7e027543d..381969d46 100644 --- a/core/MyOTALogging.cpp +++ b/core/MyOTALogging.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -22,7 +22,7 @@ // global variables static bool inOTALog = false; -void OTALog(uint8_t logNode, bool enableAck, const char *fmt, ... ) +void OTALog(uint8_t logNode, const bool requestEcho, const char *fmt, ... ) { // Avoid recursion if (inOTALog==true) { @@ -30,7 +30,7 @@ void OTALog(uint8_t logNode, bool enableAck, const char *fmt, ... ) } inOTALog = true; - MyMessage msg(NODE_SENSOR_ID, I_LOG_MESSAGE); + MyMessage msg(NODE_SENSOR_ID, V_CUSTOM); char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; va_list args; @@ -59,16 +59,17 @@ void OTALog(uint8_t logNode, bool enableAck, const char *fmt, ... ) } // Configure message - msg.sender = getNodeId(); + msg.setSender(getNodeId()); msg.setDestination(logNode); - mSetCommand(msg, C_INTERNAL); - mSetRequestAck(msg, enableAck); + msg.setCommand(C_INTERNAL); + msg.setType(I_LOG_MESSAGE); + msg.setRequestEcho(requestEcho); // Send package - for (int pos = 0; posMAX_PAYLOAD) { - length = MAX_PAYLOAD; + if (length > MAX_PAYLOAD_SIZE) { + length = MAX_PAYLOAD_SIZE; } (void)_sendRoute(msg.set((char*)&fmtBuffer[pos])); } @@ -109,8 +110,8 @@ inline void OTALogPrint(const MyMessage &message) } // FLush buffer, when node id changes - if ((OTALogBufferNode!=BROADCAST_ADDRESS) && ((OTALogBufferNode != message.sender) || - (OTALogBufferSensor != message.sensor))) { + if ((OTALogBufferNode!=BROADCAST_ADDRESS) && ((OTALogBufferNode != message.getSender()) || + (OTALogBufferSensor != message.getSensor()))) { OTALogPrintPrefix(); MY_SERIALDEVICE.print(OTALogfmtBuffer); MY_SERIALDEVICE.println("..."); @@ -124,8 +125,8 @@ inline void OTALogPrint(const MyMessage &message) OTALogfmtBufferPos += strlen(str); // Store node ID and sensor ID - OTALogBufferNode = message.sender; - OTALogBufferSensor = message.sensor; + OTALogBufferNode = message.getSender(); + OTALogBufferSensor = message.getSensor(); // Print out buffered lines ending with \n char *EOLpos; diff --git a/core/MyOTALogging.h b/core/MyOTALogging.h index 168628908..2bb4fe34a 100644 --- a/core/MyOTALogging.h +++ b/core/MyOTALogging.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -47,11 +47,11 @@ * new debug message line starts. Incomplete messages are ending with '...' * * @param logNode Destination node ID - * @param enableAck Enable or disable ACK flag + * @param echo Enable or disable echo flag * @param fmt printf format string * @param ... arguments */ -void OTALog(uint8_t logNode, bool enableAck, const char *fmt, ... ); +void OTALog(uint8_t logNode, bool echo, const char *fmt, ... ); /** * @brief Handles output of OTA log or debug messages diff --git a/core/MyPrivateConfig.h.sample b/core/MyPrivateConfig.h.sample index 3d77bd6de..173517717 100644 --- a/core/MyPrivateConfig.h.sample +++ b/core/MyPrivateConfig.h.sample @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyProtocol.cpp b/core/MyProtocol.cpp new file mode 100644 index 000000000..86805e1e4 --- /dev/null +++ b/core/MyProtocol.cpp @@ -0,0 +1,167 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 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 "MyConfig.h" +#include "MyTransport.h" +#include "MyProtocol.h" +#include "MyHelperFunctions.h" +#include + +char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH]; +char _convBuffer[MAX_PAYLOAD_SIZE * 2 + 1]; + +bool protocolSerial2MyMessage(MyMessage &message, char *inputString) +{ + char *str, *p; + uint8_t index = 0; + mysensors_command_t command = C_INVALID_7; + message.setSender(GATEWAY_ADDRESS); + message.setLast(GATEWAY_ADDRESS); + message.setEcho(false); + + // Extract command data coming on serial line + for (str = strtok_r(inputString, ";", &p); // split using semicolon + str && index < 5; // loop while str is not null an max 4 times + str = strtok_r(NULL, ";", &p), index++ // get subsequent tokens + ) { + switch (index) { + case 0: // Radio id (destination) + message.setDestination(atoi(str)); + break; + case 1: // Child id + message.setSensor(atoi(str)); + break; + case 2: // Message type + command = static_cast(atoi(str)); + message.setCommand(command); + break; + case 3: // Should we request echo from destination? + message.setRequestEcho(atoi(str) ? 1 : 0); + break; + case 4: // Data type + message.setType(atoi(str)); + break; + } + } + // payload + if (str == NULL) { + // no payload, set default value + message.set((uint8_t)0); + } else if (command == C_STREAM) { + // stream payload + uint8_t bvalue[MAX_PAYLOAD_SIZE]; + uint8_t blen = 0; + while (*str) { + uint8_t val; + val = convertH2I(*str++) << 4; + val += convertH2I(*str++); + bvalue[blen] = val; + blen++; + } + message.set(bvalue, blen); + } else { + // regular payload + char *value = str; + // Remove trailing carriage return and newline character (if it exists) + const uint8_t lastCharacter = strlen(value) - 1; + if (value[lastCharacter] == '\r' || value[lastCharacter] == '\n') { + value[lastCharacter] = '\0'; + } + message.set(value); + } + return (index == 5); +} + +char *protocolMyMessage2Serial(MyMessage &message) +{ + (void)snprintf_P(_fmtBuffer, (uint8_t)MY_GATEWAY_MAX_SEND_LENGTH, + PSTR("%" PRIu8 ";%" PRIu8 ";%" PRIu8 ";%" PRIu8 ";%" PRIu8 ";%s\n"), message.getSender(), + message.getSensor(), message.getCommand(), message.isEcho(), message.getType(), + message.getString(_convBuffer)); + return _fmtBuffer; +} + +char *protocolMyMessage2MQTT(const char *prefix, MyMessage &message) +{ + (void)snprintf_P(_fmtBuffer, (uint8_t)MY_GATEWAY_MAX_SEND_LENGTH, + PSTR("%s/%" PRIu8 "/%" PRIu8 "/%" PRIu8 "/%" PRIu8 "/%" PRIu8 ""), prefix, + message.getSender(), message.getSensor(), message.getCommand(), message.isEcho(), + message.getType()); + return _fmtBuffer; +} + + +bool protocolMQTT2MyMessage(MyMessage &message, char *topic, uint8_t *payload, + const unsigned int length) +{ + char *str, *p; + uint8_t index = 0; + message.setSender(GATEWAY_ADDRESS); + message.setLast(GATEWAY_ADDRESS); + message.setEcho(false); + for (str = strtok_r(topic + strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1, "/", &p); + str && index < 5; + str = strtok_r(NULL, "/", &p), index++ + ) { + switch (index) { + case 0: + // Node id + message.setDestination(atoi(str)); + break; + case 1: + // Sensor id + message.setSensor(atoi(str)); + break; + case 2: { + // Command type + const mysensors_command_t command = static_cast(atoi(str)); + message.setCommand(command); + // Add payload + if (command == C_STREAM) { + uint8_t bvalue[MAX_PAYLOAD_SIZE]; + uint8_t blen = 0; + while (*payload) { + uint8_t val; + val = convertH2I(*payload++) << 4; + val += convertH2I(*payload++); + bvalue[blen] = val; + blen++; + } + message.set(bvalue, blen); + } else { + // terminate string + char *value = (char *)payload; + value[length] = '\0'; + message.set((const char*)payload); + } + break; + } + case 3: + // Echo flag + message.setRequestEcho(atoi(str) ? 1 : 0); + break; + case 4: + // Sub type + message.setType(atoi(str)); + break; + } + } + // Return true if input valid + return (index == 5); +} diff --git a/core/MyProtocol.h b/core/MyProtocol.h index cf127cb88..6d50faf4d 100644 --- a/core/MyProtocol.h +++ b/core/MyProtocol.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -22,13 +22,12 @@ #include "MySensorsCore.h" - // parse(message, inputString) // parse a string into a message element // returns true if successfully parsed the input string -bool protocolParse(MyMessage &message, char *inputString); +bool protocolSerial2MyMessage(MyMessage &message, char *inputString); // Format MyMessage to the protocol representation -char *protocolFormat(MyMessage &message); +char *protocolMyMessage2Serial(MyMessage &message); #endif diff --git a/core/MyProtocolMySensors.cpp b/core/MyProtocolMySensors.cpp deleted file mode 100755 index c874504bd..000000000 --- a/core/MyProtocolMySensors.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Henrik Ekblad - * Copyright (C) 2013-2018 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 "MyConfig.h" -#include "MyTransport.h" -#include "MyProtocol.h" -#include - -uint8_t protocolH2i(char c); - -char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH]; -char _convBuffer[MAX_PAYLOAD*2+1]; - -bool protocolParse(MyMessage &message, char *inputString) -{ - char *str, *p, *value=NULL; - uint8_t bvalue[MAX_PAYLOAD]; - uint8_t blen = 0; - int i = 0; - uint8_t command = 0; - - // Extract command data coming on serial line - for (str = strtok_r(inputString, ";", &p); // split using semicolon - str && i < 6; // loop while str is not null an max 5 times - str = strtok_r(NULL, ";", &p) // get subsequent tokens - ) { - switch (i) { - case 0: // Radio id (destination) - message.destination = atoi(str); - break; - case 1: // Child id - message.sensor = atoi(str); - break; - case 2: // Message type - command = atoi(str); - mSetCommand(message, command); - break; - case 3: // Should we request ack from destination? - mSetRequestAck(message, atoi(str)?1:0); - break; - case 4: // Data type - message.type = atoi(str); - break; - case 5: // Variable value - if (command == C_STREAM) { - while (*str) { - uint8_t val; - val = protocolH2i(*str++) << 4; - val += protocolH2i(*str++); - bvalue[blen] = val; - blen++; - } - } else { - value = str; - // Remove trailing carriage return and newline character (if it exists) - uint8_t lastCharacter = strlen(value)-1; - if (value[lastCharacter] == '\r') { - value[lastCharacter] = 0; - } - if (value[lastCharacter] == '\n') { - value[lastCharacter] = 0; - } - } - break; - } - i++; - } - // Check for invalid input - if (i < 5) { - return false; - } - message.sender = GATEWAY_ADDRESS; - message.last = GATEWAY_ADDRESS; - mSetAck(message, false); - if (command == C_STREAM) { - message.set(bvalue, blen); - } else { - message.set(value); - } - return true; -} - -char * protocolFormat(MyMessage &message) -{ - snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, - PSTR("%" PRIu8 ";%" PRIu8 ";%" PRIu8 ";%" PRIu8 ";%" PRIu8 ";%s\n"), message.sender, - message.sensor, (uint8_t)mGetCommand(message), (uint8_t)mGetAck(message), message.type, - message.getString(_convBuffer)); - return _fmtBuffer; -} - -char * protocolFormatMQTTTopic(const char* prefix, MyMessage &message) -{ - snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, - PSTR("%s/%" PRIu8 "/%" PRIu8 "/%" PRIu8 "/%" PRIu8 "/%" PRIu8 ""), prefix, - message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type); - return _fmtBuffer; -} - -char * protocolFormatMQTTSubscribe(const char* prefix) -{ - snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/+/+/+/+/+"), prefix); - return _fmtBuffer; -} - -#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 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) { - uint8_t bvalue[MAX_PAYLOAD]; - uint8_t blen = 0; - while (*payload) { - uint8_t val; - 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; -} diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index 88d293d5b..22bbf91a9 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,28 +19,27 @@ #include "MySensorsCore.h" -#if defined(__linux__) -#include -#include -#endif - // debug output #if defined(MY_DEBUG_VERBOSE_CORE) #define CORE_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug #else -#define CORE_DEBUG(x,...) //!< debug NULL +#define CORE_DEBUG(x,...) //!< debug NULL #endif // message buffers - MyMessage _msg; // Buffer for incoming messages MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others) // core configuration static coreConfig_t _coreConfig; +#if defined(MY_DEBUG_VERBOSE_CORE) +static uint8_t waitLock = 0; +static uint8_t processLock = 0; +#endif + #if defined(DEBUG_OUTPUT_ENABLED) -char _convBuf[MAX_PAYLOAD*2+1]; +char _convBuf[MAX_PAYLOAD_SIZE * 2 + 1]; #endif // Callback for transport=ok transition @@ -57,6 +56,12 @@ void _callbackTransportReady(void) void _process(void) { +#if defined(MY_DEBUG_VERBOSE_CORE) + if (processLock) { + CORE_DEBUG(PSTR("!MCO:PRO:RC=%" PRIu8 "\n"), processLock); // recursive call detected + } + processLock++; +#endif doYield(); #if defined(MY_INCLUSION_MODE_FEATURE) @@ -75,16 +80,20 @@ void _process(void) // To avoid high cpu usage usleep(10000); // 10ms #endif +#if defined(MY_DEBUG_VERBOSE_CORE) + processLock--; +#endif } void _infiniteLoop(void) { +#if defined(__linux__) + exit(1); +#else while(1) { doYield(); -#if defined(__linux__) - exit(1); -#endif } +#endif } void _begin(void) @@ -110,8 +119,16 @@ void _begin(void) displaySplashScreen(); #endif - CORE_DEBUG(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",REL=%" PRIu8 ",VER=" +#if defined(F_CPU) + CORE_DEBUG(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",FQ=%" PRIu16 ",REL=%" + PRIu8 ",VER=" + MYSENSORS_LIBRARY_VERSION "\n"), (uint16_t)(F_CPU/1000000UL), + MYSENSORS_LIBRARY_VERSION_PRERELEASE_NUMBER); +#else + CORE_DEBUG(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",FQ=NA,REL=%" + PRIu8 ",VER=" MYSENSORS_LIBRARY_VERSION "\n"), MYSENSORS_LIBRARY_VERSION_PRERELEASE_NUMBER); +#endif if (!hwInitResult) { CORE_DEBUG(PSTR("!MCO:BGN:HW ERR\n")); setIndication(INDICATION_ERR_HW_INIT); @@ -135,7 +152,7 @@ void _begin(void) // Read latest received controller configuration from EEPROM // Note: _coreConfig.isMetric is bool, hence empty EEPROM (=0xFF) evaluates to true (default) - hwReadConfigBlock((void*)&_coreConfig.controllerConfig, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, + hwReadConfigBlock((void *)&_coreConfig.controllerConfig, (void *)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(controllerConfig_t)); #if defined(MY_OTA_FIRMWARE_FEATURE) @@ -176,7 +193,8 @@ void _begin(void) setup(); } #if defined(MY_SENSOR_NETWORK) - CORE_DEBUG(PSTR("MCO:BGN:INIT OK,TSP=%" PRIu8 "\n"), isTransportReady() && transportSanityCheck()); + CORE_DEBUG(PSTR("MCO:BGN:INIT OK,TSP=%" PRIu8 "\n"), isTransportReady() && + transportHALSanityCheck()); #else // no sensor network defined, call presentation & registration _callbackTransportReady(); @@ -299,7 +317,7 @@ bool _sendRoute(MyMessage &message) (void)message; #endif #if defined(MY_GATEWAY_FEATURE) - if (message.destination == getNodeId()) { + if (message.getDestination() == getNodeId()) { // This is a message sent from a sensor attached on the gateway node. // Pass it directly to the gateway transport layer. return gatewayTransportSend(message); @@ -312,11 +330,11 @@ bool _sendRoute(MyMessage &message) #endif } -bool send(MyMessage &message, const bool enableAck) +bool send(MyMessage &message, const bool requestEcho) { - message.sender = getNodeId(); - mSetCommand(message, C_SET); - mSetRequestAck(message, enableAck); + message.setSender(getNodeId()); + message.setCommand(C_SET); + message.setRequestEcho(requestEcho); #if defined(MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE) if (_coreConfig.nodeRegistered) { @@ -330,70 +348,77 @@ bool send(MyMessage &message, const bool enableAck) #endif } -bool sendBatteryLevel(const uint8_t value, const bool ack) +bool sendBatteryLevel(const uint8_t value, const bool requestEcho) { return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, - ack).set(value)); + requestEcho).set(value)); } -bool sendHeartbeat(const bool ack) +bool sendHeartbeat(const bool requestEcho) { #if defined(MY_SENSOR_NETWORK) const uint32_t heartbeat = transportGetHeartbeat(); return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT_RESPONSE, - ack).set(heartbeat)); + requestEcho).set(heartbeat)); +#elif defined(MY_GATEWAY_FEATURE) + const uint32_t heartbeat = hwMillis(); + return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT_RESPONSE, + requestEcho).set(heartbeat)); #else - (void)ack; + (void)requestEcho; return false; #endif } -bool present(const uint8_t childSensorId, const uint8_t sensorType, const char *description, - const bool ack) +bool present(const uint8_t childSensorId, const mysensors_sensor_t sensorType, + const char *description, + const bool requestEcho) { - return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, - ack).set(childSensorId == NODE_SENSOR_ID ? MYSENSORS_LIBRARY_VERSION : description)); + return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, + static_cast(sensorType), + requestEcho).set(childSensorId == NODE_SENSOR_ID ? MYSENSORS_LIBRARY_VERSION : description)); } #if !defined(__linux__) -bool present(const uint8_t childSensorId, const uint8_t sensorType, +bool present(const uint8_t childSensorId, const mysensors_sensor_t sensorType, const __FlashStringHelper *description, - const bool ack) + const bool requestEcho) { - return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, - ack).set(childSensorId == NODE_SENSOR_ID ? F(" MYSENSORS_LIBRARY_VERSION "): description)); + return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, + static_cast(sensorType), + requestEcho).set(childSensorId == NODE_SENSOR_ID ? F(" MYSENSORS_LIBRARY_VERSION "): description)); } #endif -bool sendSketchInfo(const char *name, const char *version, const bool ack) +bool sendSketchInfo(const char *name, const char *version, const bool requestEcho) { bool result = true; if (name) { result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, - ack).set(name)); + requestEcho).set(name)); } if (version) { result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, - ack).set(version)); + requestEcho).set(version)); } return result; } #if !defined(__linux__) bool sendSketchInfo(const __FlashStringHelper *name, const __FlashStringHelper *version, - const bool ack) + const bool requestEcho) { bool result = true; if (name) { result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, - ack).set(name)); + requestEcho).set(name)); } if (version) { result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, - ack).set(version)); + requestEcho).set(version)); } return result; } @@ -404,16 +429,17 @@ bool request(const uint8_t childSensorId, const uint8_t variableType, const uint return _sendRoute(build(_msgTmp, destination, childSensorId, C_REQ, variableType).set("")); } -bool requestTime(const bool ack) +bool requestTime(const bool requestEcho) { - return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, ack).set("")); + return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, + requestEcho).set("")); } // Message delivered through _msg bool _processInternalCoreMessage(void) { - const uint8_t type = _msg.type; - if (_msg.sender == GATEWAY_ADDRESS) { + const uint8_t type = _msg.getType(); + if (_msg.getSender() == GATEWAY_ADDRESS) { if (type == I_REBOOT) { #if !defined(MY_DISABLE_REMOTE_RESET) setIndication(INDICATION_REBOOT); @@ -437,7 +463,7 @@ bool _processInternalCoreMessage(void) presentNode(); } else if (type == I_HEARTBEAT_REQUEST) { (void)sendHeartbeat(); - } else if (_msg.type == I_VERSION) { + } else if (type == I_VERSION) { #if !defined(MY_GATEWAY_FEATURE) (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_VERSION).set(MYSENSORS_LIBRARY_VERSION_INT)); @@ -499,11 +525,11 @@ bool _processInternalCoreMessage(void) approveRegistration = true; #endif -#if (F_CPU>16000000) +#if (F_CPU>16*1000000ul) // delay for fast GW and slow nodes delay(5); #endif - (void)_sendRoute(build(_msgTmp, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, + (void)_sendRoute(build(_msgTmp, _msg.getSender(), NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_RESPONSE).set(approveRegistration)); #else return false; // processing of this request via controller @@ -529,31 +555,71 @@ uint8_t loadState(const uint8_t pos) void wait(const uint32_t waitingMS) { +#if defined(MY_DEBUG_VERBOSE_CORE) + if (waitLock) { + CORE_DEBUG(PSTR("!MCO:WAI:RC=%" PRIu8 "\n"), waitLock); // recursive call detected + } + waitLock++; +#endif const uint32_t enteringMS = hwMillis(); while (hwMillis() - enteringMS < waitingMS) { _process(); } +#if defined(MY_DEBUG_VERBOSE_CORE) + waitLock--; +#endif } -bool wait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType) +bool wait(const uint32_t waitingMS, const mysensors_command_t cmd) { +#if defined(MY_DEBUG_VERBOSE_CORE) + if (waitLock) { + CORE_DEBUG(PSTR("!MCO:WAI:RC=%" PRIu8 "\n"), waitLock); // recursive call detected + } + waitLock++; +#endif const uint32_t enteringMS = hwMillis(); - // invalidate msg type - _msg.type = !msgType; + // invalidate cmd + //_msg.setCommand(!cmd); + _msg.setCommand(C_INVALID_7); + bool expectedResponse = false; + while ((hwMillis() - enteringMS < waitingMS) && !expectedResponse) { + _process(); + expectedResponse = (_msg.getCommand() == cmd); + } +#if defined(MY_DEBUG_VERBOSE_CORE) + waitLock--; +#endif + return expectedResponse; +} + +bool wait(const uint32_t waitingMS, const mysensors_command_t cmd, const uint8_t msgType) +{ +#if defined(MY_DEBUG_VERBOSE_CORE) + if (waitLock) { + CORE_DEBUG(PSTR("!MCO:WAI:RC=%" PRIu8 "\n"), waitLock); // recursive call detected + } + waitLock++; +#endif + const uint32_t enteringMS = hwMillis(); + // invalidate cmd + //_msg.setCommand(!cmd); + _msg.setCommand(C_INVALID_7); bool expectedResponse = false; while ( (hwMillis() - enteringMS < waitingMS) && !expectedResponse ) { _process(); - expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType); + expectedResponse = (_msg.getCommand() == cmd && _msg.getType() == msgType); } +#if defined(MY_DEBUG_VERBOSE_CORE) + waitLock--; +#endif return expectedResponse; } void doYield(void) { hwWatchdogReset(); - yield(); - #if defined (MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN) ledsProcess(); #endif @@ -704,9 +770,13 @@ int8_t smartSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t i return _sleep(sleepingMS, true, interrupt1, mode1, interrupt2, mode2); } +uint32_t getSleepRemaining(void) +{ + return hwGetSleepRemaining(); +} -void _nodeLock(const char* str) +void _nodeLock(const char *str) { #ifdef MY_NODE_LOCK_FEATURE // Make sure EEPROM is updated to locked status diff --git a/core/MySensorsCore.h b/core/MySensorsCore.h index 7cd5295e3..cb685a154 100644 --- a/core/MySensorsCore.h +++ b/core/MySensorsCore.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -38,9 +38,9 @@ * MySensorsCore debug log messages: * * |E| SYS | SUB | Message | Comment -* |-|-----|-----|---------------------------------------------|-------------------------------------------------------------------------------------------------- +* |-|-----|-----|---------------------------------------------|----------------------------------------------------------------------------------------------------------------- * |!| MCO | BGN | HW ERR | Error HW initialization (e.g. ext. EEPROM) -* | | MCO | BGN | INIT %%s,CP=%%s,REL=%%d,VER=%%s (%%d) | Core initialization, capabilities (CP), release number (REL), library version (VER) +* | | MCO | BGN | INIT %%s,CP=%%s,FQ=%%d,REL=%%d,VER=%%s | Core initialization, capabilities (CP), CPU frequency [Mhz] (FQ), release number (REL), library version (VER) * | | MCO | BGN | BFR | Callback before() * | | MCO | BGN | STP | Callback setup() * | | MCO | BGN | INIT OK,TSP=%%d | Core initialised, transport status (TSP): 0=not initialised, 1=initialised, NA=not available @@ -50,16 +50,16 @@ * | | 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 | SLP | MS=%%lu,SMS=%%d,I1=%%d,M1=%%d,I2=%%d,M2=%%d | Sleep node, time (MS), smartSleep (SMS), Int1/M1, Int2/M2 +* |!| MCO | WAI | RC=%%d | Recursive call detected in wait(), level (RC) +* | | MCO | SLP | MS=%%lu,SMS=%%d,I1=%%d,M1=%%d,I2=%%d,M2=%%d | Sleep node, time (MS), smartSleep (SMS), Int1 (I1), Mode1 (M1), Int2 (I2), Mode2 (M2) * | | MCO | SLP | WUP=%%d | Node woke-up, reason/IRQ (WUP) * |!| MCO | SLP | NTL | Sleeping not possible, no time left * |!| MCO | SLP | FWUPD | Sleeping not possible, FW update ongoing * |!| MCO | SLP | REP | Sleeping not possible, repeater feature enabled -* |!| MCO | SLP | TNR | Transport not ready, attempt to reconnect until timeout (MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS) +* |!| MCO | SLP | TNR | Transport not ready, attempt to reconnect until timeout (@ref MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS) * | | MCO | NLK | NODE LOCKED. UNLOCK: GND PIN %%d AND RESET | Node locked during booting, see signing chapter for additional information * | | MCO | NLK | TSL | Set transport to sleep * -* * @brief API declaration for MySensorsCore */ @@ -67,7 +67,7 @@ #ifndef MySensorsCore_h #define MySensorsCore_h -#include "Version.h" // Auto generated by bot +#include "Version.h" #include "MyConfig.h" #include "MyEepromAddresses.h" #include "MyMessage.h" @@ -125,75 +125,96 @@ uint8_t getParentNodeId(void); void presentNode(void); /** -* Each node must present all attached sensors before any values can be handled correctly by the controller. -* It is usually good to present all attached sensors after power-up in setup(). -* -* @param sensorId Select a unique sensor id for this sensor. Choose a number between 0-254. -* @param sensorType The sensor type. See sensor typedef in MyMessage.h. -* @param description A textual description of the sensor. -* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. -* @param description A textual description of the sensor. -* @return true Returns true if message reached the first stop on its way to destination. -*/ -bool present(const uint8_t sensorId, const uint8_t sensorType, const char *description="", - const bool ack = false); + * Each node must present all attached sensors before any values can be handled correctly by the controller. + * It is usually good to present all attached sensors after power-up in setup(). + * + * @param sensorId Select a unique sensor id for this sensor. Choose a number between 0-254. + * @param sensorType The sensor type. See sensor typedef in MyMessage.h. + * @param description A textual description of the sensor. + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. + * @return true Returns true if message reached the first stop on its way to destination. + */ +bool present(const uint8_t sensorId, const mysensors_sensor_t sensorType, + const char *description = "", + const bool requestEcho = false); #if !defined(__linux__) -bool present(const uint8_t childSensorId, const uint8_t sensorType, +bool present(const uint8_t childSensorId, const mysensors_sensor_t sensorType, const __FlashStringHelper *description, - const bool ack = false); + const bool requestEcho = false); #endif /** * Sends sketch meta information to the gateway. Not mandatory but a nice thing to do. * @param name String containing a short Sketch name or NULL if not applicable * @param version String containing a short Sketch version or NULL if not applicable - * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. * @return true Returns true if message reached the first stop on its way to destination. */ -bool sendSketchInfo(const char *name, const char *version, const bool ack = false); +bool sendSketchInfo(const char *name, const char *version, const bool requestEcho = false); #if !defined(__linux__) bool sendSketchInfo(const __FlashStringHelper *name, const __FlashStringHelper *version, - const bool ack = false); + const bool requestEcho = false); #endif /** -* Sends a message to gateway or one of the other nodes in the radio network -* @param msg Message to send -* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. -* @return true Returns true if message reached the first stop on its way to destination. -*/ -bool send(MyMessage &msg, const bool ack = false); + * Sends a message to gateway or one of the other nodes in the radio network + * @param msg Message to send + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. + * @return true Returns true if message reached the first stop on its way to destination. + */ +bool send(MyMessage &msg, const bool requestEcho = false); /** * Send this nodes battery level to gateway. * @param level Level between 0-100(%) - * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. * @return true Returns true if message reached the first stop on its way to destination. */ -bool sendBatteryLevel(const uint8_t level, const bool ack = false); +bool sendBatteryLevel(const uint8_t level, const bool requestEcho = false); /** * Send a heartbeat message (I'm alive!) to the gateway/controller. * The payload will be an incremental 16 bit integer value starting at 1 when sensor is powered on. - * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. * @return true Returns true if message reached the first stop on its way to destination. */ -bool sendHeartbeat(const bool ack = false); +bool sendHeartbeat(const bool requestEcho = false); /** -* Send this nodes signal strength to gateway. -* @param level Signal strength can be RSSI if the radio provide it, or another kind of calculation -* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. -* @return true Returns true if message reached the first stop on its way to destination. -*/ -bool sendSignalStrength(const int16_t level, const bool ack = false); + * Send this nodes signal strength to gateway. + * @param level Signal strength can be RSSI if the radio provide it, or another kind of calculation + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. + * @return true Returns true if message reached the first stop on its way to destination. + */ +bool sendSignalStrength(const int16_t level, const bool requestEcho = false); /** -* Send this nodes TX power level to gateway. -* @param level For instance, can be TX power level in dbm -* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. -* @return true Returns true if message reached the first stop on its way to destination. -*/ -bool sendTXPowerLevel(const uint8_t level, const bool ack = false); + * Send this nodes TX power level to gateway. + * @param level For instance, can be TX power level in dbm + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. + * @return true Returns true if message reached the first stop on its way to destination. + */ +bool sendTXPowerLevel(const uint8_t level, const bool requestEcho = false); /** * Requests a value from gateway or some other sensor in the radio network. @@ -209,10 +230,13 @@ bool request(const uint8_t childSensorId, const uint8_t variableType, /** * 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. + * @param requestEcho Set this to true if you want destination node to echo the message back to this node. + * Default is not to request echo. If set to true, the final destination will echo back the + * contents of the message, triggering the receive() function on the original node with a copy of + * the message, with message.isEcho() set to true and sender/destination switched. * @return true Returns true if message reached the first stop on its way to destination. */ -bool requestTime(const bool ack = false); +bool requestTime(const bool requestEcho = false); /** * Returns the most recent node configuration received from controller @@ -248,6 +272,17 @@ uint8_t loadState(const uint8_t pos); */ void wait(const uint32_t waitingMS); +/** + * Wait for a specified amount of time to pass or until specified message received. Keeps process()ing. + * This does not power-down the radio nor the Arduino. + * Because this calls process() in a loop, it is a good way to wait + * in your loop() on a repeater node or sensor that listens to messages. + * @param waitingMS Number of milliseconds to wait. + * @param cmd Command of incoming message. + * @return True if specified message received + */ +bool wait(const uint32_t waitingMS, const mysensors_command_t cmd); + /** * 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. @@ -258,7 +293,7 @@ void wait(const uint32_t waitingMS); * @param msgtype Message type. * @return True if specified message received */ -bool wait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgtype); +bool wait(const uint32_t waitingMS, const mysensors_command_t cmd, const uint8_t msgtype); /** * Function to allow scheduler to do some work. @@ -306,7 +341,7 @@ int8_t sleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interr /** * \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). +* Specify the time to wait for incoming messages by defining @ref MY_SMART_SLEEP_WAIT_DURATION_MS 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) */ @@ -315,7 +350,7 @@ 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). +* Specify the time to wait for incoming messages by defining @ref MY_SMART_SLEEP_WAIT_DURATION_MS 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 @@ -326,7 +361,7 @@ int8_t smartSleep(const uint8_t interrupt, const uint8_t mode, const uint32_t sl /** * \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). +* Specify the time to wait for incoming messages by defining @ref MY_SMART_SLEEP_WAIT_DURATION_MS 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 @@ -353,6 +388,13 @@ 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); +/** + * Return the sleep time remaining after waking up from sleep. + * Depending on the CPU architecture, the remaining time can be seconds off (e.g. upto roughly 8 seconds on AVR). + * @return Time remaining, in ms, when wake from sleep by an interrupt, 0 by timer (@ref MY_WAKE_UP_BY_TIMER), undefined otherwise. +*/ +uint32_t getSleepRemaining(void); + // **** private functions ******** @@ -371,7 +413,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep = false, * * @param str The string to transmit. */ -void _nodeLock(const char* str); +void _nodeLock(const char *str); /** * @brief Check node lock status and prevent node execution if locked. @@ -406,26 +448,24 @@ void _registerNode(void); * @return true Returns true if message reached the first stop on its way to destination. */ bool _sendRoute(MyMessage &message); - /** * @brief Callback for incoming messages -* @param message */ -void receive(const MyMessage &message) __attribute__((weak)); +void receive(const MyMessage&) __attribute__((weak)); /** * @brief Callback for incoming time messages */ -void receiveTime(uint32_t) __attribute__((weak)); +void receiveTime(uint32_t) __attribute__((weak)); /** * @brief Node presentation */ -void presentation(void) __attribute__((weak)); +void presentation(void) __attribute__((weak)); /** * @brief Called before node initialises */ void before(void) __attribute__((weak)); /** -* @brief Called before any hwInitialization is done +* @brief Called before any hardware initialisation is done */ void preHwInit(void) __attribute__((weak)); /** @@ -440,27 +480,27 @@ void loop(void) __attribute__((weak)); // Inline function and macros static inline MyMessage& build(MyMessage &msg, const uint8_t destination, const uint8_t sensor, - const uint8_t command, const uint8_t type, const bool ack = false) + const mysensors_command_t command, const uint8_t type, const bool requestEcho = false) { - msg.sender = getNodeId(); - msg.destination = destination; - msg.sensor = sensor; - msg.type = type; - mSetCommand(msg,command); - mSetRequestAck(msg,ack); - mSetAck(msg,false); + msg.setSender(getNodeId()); + msg.setDestination(destination); + msg.setSensor(sensor); + msg.setType(type); + msg.setCommand(command); + msg.setRequestEcho(requestEcho); + msg.setEcho(false); return msg; } static inline MyMessage& buildGw(MyMessage &msg, const uint8_t type) { - msg.sender = GATEWAY_ADDRESS; - msg.destination = GATEWAY_ADDRESS; - msg.sensor = NODE_SENSOR_ID; - msg.type = type; - mSetCommand(msg, C_INTERNAL); - mSetRequestAck(msg, false); - mSetAck(msg, false); + msg.setSender(GATEWAY_ADDRESS); + msg.setDestination(GATEWAY_ADDRESS); + msg.setSensor(NODE_SENSOR_ID); + msg.setType(type); + msg.setCommand(C_INTERNAL); + msg.setRequestEcho(false); + msg.setEcho(false); return msg; } diff --git a/core/MySigning.cpp b/core/MySigning.cpp index 75da2f674..dddf91a8e 100644 --- a/core/MySigning.cpp +++ b/core/MySigning.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -171,7 +171,7 @@ void signerPresentation(MyMessage &msg, uint8_t destination) bool signerProcessInternal(MyMessage &msg) { bool ret; - switch (msg.type) { + switch (msg.getType()) { case I_SIGNING_PRESENTATION: ret = signerInternalProcessPresentation(msg); break; @@ -195,11 +195,11 @@ bool signerCheckTimer(void) bool signerSignMsg(MyMessage &msg) { - bool ret; #if defined(MY_SIGNING_FEATURE) + bool ret; // If destination is known to require signed messages and we are the sender, // sign this message unless it is identified as an exception - if (DO_SIGN(msg.destination) && msg.sender == getNodeId()) { + if (DO_SIGN(msg.getDestination()) && msg.getSender() == getNodeId()) { if (skipSign(msg)) { ret = true; } else { @@ -210,13 +210,13 @@ bool signerSignMsg(MyMessage &msg) } else { // Send nonce-request _signingNonceStatus=SIGN_WAITING_FOR_NONCE; - if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL, + if (!_sendRoute(build(_msgSign, msg.getDestination(), msg.getSensor(), C_INTERNAL, I_NONCE_REQUEST).set(""))) { SIGN_DEBUG(PSTR("!SGN:SGN:NCE REQ,TO=%" PRIu8 " FAIL\n"), - msg.destination); // Failed to transmit nonce request! + msg.getDestination()); // Failed to transmit nonce request! ret = false; } else { - SIGN_DEBUG(PSTR("SGN:SGN:NCE REQ,TO=%" PRIu8 "\n"), msg.destination); // Nonce requested + SIGN_DEBUG(PSTR("SGN:SGN:NCE REQ,TO=%" PRIu8 "\n"), msg.getDestination()); // Nonce requested // 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(); @@ -245,21 +245,22 @@ bool signerSignMsg(MyMessage &msg) } } } - } else if (getNodeId() == msg.sender) { - mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned + } else if (getNodeId() == msg.getSender()) { + msg.setSigned(false); // Message is not supposed to be signed, make sure it is marked unsigned SIGN_DEBUG(PSTR("SGN:SGN:NREQ=%" PRIu8 "\n"), - msg.destination); // Do not sign message as it is not req + msg.getDestination()); // Do not sign message as it is not req ret = true; } else { - SIGN_DEBUG(PSTR("SGN:SGN:%" PRIu8 "!=%" PRIu8 " NUS\n"), msg.sender, + SIGN_DEBUG(PSTR("SGN:SGN:%" PRIu8 "!=%" PRIu8 " NUS\n"), msg.getSender(), getNodeId()); // Will not sign message since it was from someone else ret = true; } + return ret; #else (void)msg; - ret = true; + return true; #endif // MY_SIGNING_FEATURE - return ret; + } bool signerVerifyMsg(MyMessage &msg) @@ -272,14 +273,14 @@ bool signerVerifyMsg(MyMessage &msg) // If we are a node, or we are a gateway and the sender require signatures (or just a strict gw) // and we are the destination... #if defined(MY_SIGNING_WEAK_SECURITY) - if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == getNodeId()) { + if ((!MY_IS_GATEWAY || DO_SIGN(msg.getSender())) && msg.getDestination() == getNodeId()) { #else - if (msg.destination == getNodeId()) { + if (msg.getDestination() == getNodeId()) { #endif // Internal messages of certain types are not verified if (skipSign(msg)) { verificationResult = true; - } else if (!mGetSigned(msg)) { + } else if (!msg.getSigned()) { // Got unsigned message that should have been signed SIGN_DEBUG(PSTR("!SGN:VER:NSG\n")); // Message is not signed, but it should have been! verificationResult = false; @@ -309,7 +310,7 @@ bool signerVerifyMsg(MyMessage &msg) } } #endif - mSetSigned(msg,0); // Clear the sign-flag now as verification is completed + msg.setSigned(false); // Clear the sign-flag now as verification is completed } } #else @@ -353,29 +354,29 @@ int signerMemcmp(const void* a, const void* b, size_t sz) static bool skipSign(MyMessage &msg) { bool ret = false; - if (mGetAck(msg)) { + if (msg.isEcho()) { ret = true; - } else if (mGetCommand(msg) == C_INTERNAL && - (msg.type == I_SIGNING_PRESENTATION || - msg.type == I_REGISTRATION_REQUEST || - msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE || - 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_DISCOVER_REQUEST || msg.type == I_DISCOVER_RESPONSE || - msg.type == I_LOG_MESSAGE)) { + } else if (msg.getCommand() == C_INTERNAL && + (msg.getType() == I_SIGNING_PRESENTATION || + msg.getType() == I_REGISTRATION_REQUEST || + msg.getType() == I_NONCE_REQUEST || msg.getType() == I_NONCE_RESPONSE || + msg.getType() == I_ID_REQUEST || msg.getType() == I_ID_RESPONSE || + msg.getType() == I_FIND_PARENT_REQUEST || msg.getType() == I_FIND_PARENT_RESPONSE || + msg.getType() == I_HEARTBEAT_REQUEST || msg.getType() == I_HEARTBEAT_RESPONSE || + msg.getType() == I_PING || msg.getType() == I_PONG || + msg.getType() == I_DISCOVER_REQUEST || msg.getType() == I_DISCOVER_RESPONSE || + msg.getType() == I_LOG_MESSAGE)) { ret = true; - } else if (mGetCommand(msg) == C_STREAM && - (msg.type == ST_SOUND || - msg.type == ST_IMAGE || - msg.type == ST_FIRMWARE_REQUEST || msg.type == ST_FIRMWARE_RESPONSE )) { + } else if (msg.getCommand() == C_STREAM && + (msg.getType() == ST_SOUND || + msg.getType() == ST_IMAGE || + msg.getType() == ST_FIRMWARE_REQUEST || msg.getType() == ST_FIRMWARE_RESPONSE )) { ret = true; } if (ret) { - SIGN_DEBUG(PSTR("SGN:SKP:%s CMD=%" PRIu8 ",TYPE=%" PRIu8 "\n"), mGetAck(msg) ? "ACK" : "MSG", - mGetCommand(msg), - msg.type); //Skip signing/verification of this message + SIGN_DEBUG(PSTR("SGN:SKP:%s CMD=%" PRIu8 ",TYPE=%" PRIu8 "\n"), msg.isEcho() ? "ECHO" : "MSG", + msg.getCommand(), + msg.getType()); //Skip signing/verification of this message } return ret; } @@ -386,8 +387,8 @@ 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.setLength(2); + msg.setPayloadType(P_CUSTOM); // displayed as hex msg.data[0] = SIGNING_PRESENTATION_VERSION_1; msg.data[1] = 0; } @@ -395,7 +396,7 @@ static void prepareSigningPresentation(MyMessage &msg, uint8_t destination) // Helper to process presentation messages static bool signerInternalProcessPresentation(MyMessage &msg) { - const uint8_t sender = msg.sender; + const uint8_t sender = msg.getSender(); #if defined(MY_SIGNING_FEATURE) if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) { SIGN_DEBUG(PSTR("!SGN:PRE:VER=%" PRIu8 "\n"), @@ -519,10 +520,10 @@ static bool signerInternalProcessNonceRequest(MyMessage &msg) } #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("!SGN:NCE:XMT,TO=%" PRIu8 " FAIL\n"), msg.sender); // Failed to transmit nonce! + if (!_sendRoute(build(msg, msg.getSender(), NODE_SENSOR_ID, C_INTERNAL, I_NONCE_RESPONSE))) { + SIGN_DEBUG(PSTR("!SGN:NCE:XMT,TO=%" PRIu8 " FAIL\n"), msg.getSender()); // Failed to transmit nonce! } else { - SIGN_DEBUG(PSTR("SGN:NCE:XMT,TO=%" PRIu8 "\n"), msg.sender); + SIGN_DEBUG(PSTR("SGN:NCE:XMT,TO=%" PRIu8 "\n"), msg.getSender()); } } else { SIGN_DEBUG(PSTR("!SGN:NCE:GEN\n")); // Failed to generate nonce! @@ -540,9 +541,10 @@ static bool signerInternalProcessNonceResponse(MyMessage &msg) { #if defined(MY_SIGNING_FEATURE) // Proceed with signing if nonce has been received - SIGN_DEBUG(PSTR("SGN:NCE:FROM=%" PRIu8 "\n"), msg.sender); - if (msg.sender != _msgSign.destination) { - SIGN_DEBUG(PSTR("SGN:NCE:%" PRIu8 "!=%" PRIu8 " (DROPPED)\n"), _msgSign.destination, msg.sender); + SIGN_DEBUG(PSTR("SGN:NCE:FROM=%" PRIu8 "\n"), msg.getSender()); + if (msg.getSender() != _msgSign.getDestination()) { + SIGN_DEBUG(PSTR("SGN:NCE:%" PRIu8 "!=%" PRIu8 " (DROPPED)\n"), _msgSign.getDestination(), + msg.getSender()); } else { signerBackendPutNonce(msg); if (signerBackendSignMsg(_msgSign)) { diff --git a/core/MySigning.h b/core/MySigning.h index e305b0ec3..614d78bb7 100644 --- a/core/MySigning.h +++ b/core/MySigning.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -765,7 +765,7 @@ int signerMemcmp(const void* a, const void* b, size_t sz); * | | SGN | PRE | SGN NREQ,FROM='node' | Node 'node' does not require signing * |!| SGN | PRE | SGN NREQ,FROM='node' REJ | Node 'node' does not require signing but used to (requirement remain unchanged) * | | SGN | PRE | WHI REQ | Whitelisting required - * | | SGN | PRE | WHI REQ;TO='node' | Tell 'node' that we require whitelisting + * | | SGN | PRE | WHI REQ,TO='node' | Tell 'node' that we require whitelisting * | | SGN | PRE | WHI REQ,FROM='node' | Node 'node' require whitelisting * | | SGN | PRE | WHI NREQ | Whitelisting not required * | | SGN | PRE | WHI NREQ,TO='node' | Tell 'node' that we do not require whitelisting @@ -791,7 +791,7 @@ int signerMemcmp(const void* a, const void* b, size_t sz); * | | SGN | VER | LEFT='number' | 'number' of failed verifications left in a row before node is locked * |!| SGN | VER | STATE | Security system in a invalid state (personalization data tampered) * | | SGN | SKP | MSG CMD='cmd',TYPE='type'| Message with command 'cmd' and type 'type' does not need to be signed - * | | SGN | SKP | ACK CMD='cmd',TYPE='type'| ACK messages does not need to be signed + * | | SGN | SKP | ECHO CMD='cmd',TYPE='type'| ECHO messages does not need to be signed * | | SGN | NCE | LEFT='number' | 'number' of nonce requests between successful verifications left before node is locked * | | SGN | NCE | XMT,TO='node' | Nonce data transmitted to 'node' * |!| SGN | NCE | XMT,TO='node' FAIL | Nonce data not properly transmitted to 'node' @@ -892,5 +892,16 @@ int signerMemcmp(const void* a, const void* b, size_t sz); * Also, if whitelisting is used, make sure the proper serial is paired with the proper node ID at the destination. * Whitelisting preferences are communicated with the signing presentation (done automatically from nodes to gateway but * has to be explicitly done by sketch for node to node communication). @see signerPresentation + * + * @subsection MySigningTroubleshootingSymptomStTampered Signing backend reports tampered even after personalization + * + * The signing backend validates that the secure elements in EEPROM remain unmodified after personalization using a checksum. If the check fails, + * the backend reports + * @code + * !SGN:PER:Tampered + * @endcode + * This usually indicate that the sketch has modified the secure elements in EEPROM, but if you experience this even after a node is freshly + * personalized on a atmega device, it could be that the EESAVE fuse bit is not set which mean that the EEPROM is erased when a new firmware is flashed. + * You will need to enable the EESAVE bit in order to have the security personalization persist in the node. */ /** @}*/ diff --git a/core/MySigningAtsha204.cpp b/core/MySigningAtsha204.cpp index 73fd36e5e..a82075ac8 100644 --- a/core/MySigningAtsha204.cpp +++ b/core/MySigningAtsha204.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -26,35 +26,13 @@ */ #include "MySigning.h" +#include "MyHelperFunctions.h" #ifdef MY_SIGNING_ATSHA204 #define SIGNING_IDENTIFIER (1) //HMAC-SHA256 #if defined(MY_DEBUG_VERBOSE_SIGNING) #define SIGN_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) -static char printStr[65]; -static char i2h(uint8_t i) -{ - uint8_t k = i & 0x0F; - if (k <= 9) { - return '0' + k; - } else { - return 'A' + k - 10; - } -} - -static void buf2str(const uint8_t* buf, size_t sz) -{ - uint8_t i; - if (sz > 32) { - sz = 32; //clamp to 32 bytes - } - for (i = 0; i < sz; i++) { - printStr[i * 2] = i2h(buf[i] >> 4); - printStr[(i * 2) + 1] = i2h(buf[i]); - } - printStr[sz * 2] = '\0'; -} #else #define SIGN_DEBUG(x,...) #endif @@ -147,19 +125,20 @@ bool signerAtsha204GetNonce(MyMessage &msg) for (int i = 0; i < 32; i++) { _signing_verifying_nonce[i] = _signing_rx_buffer[SHA204_BUFFER_POS_DATA+i] ^ (hwMillis()&0xFF); } - memcpy(_signing_verifying_nonce, signerSha256(_signing_verifying_nonce, 32), - min(MAX_PAYLOAD, 32)); + (void)memcpy((void *)_signing_verifying_nonce, (const void *)signerSha256(_signing_verifying_nonce, + 32), + min((uint8_t)MAX_PAYLOAD_SIZE, 32u)); // We just idle the chip now since we expect to use it soon when the signed message arrives atsha204_idle(); - if (MAX_PAYLOAD < 32) { + if (MAX_PAYLOAD_SIZE < 32) { // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD); + (void)memset((void *)&_signing_verifying_nonce[MAX_PAYLOAD_SIZE], 0xAA, 32u - MAX_PAYLOAD_SIZE); } // Transfer the first part of the nonce to the message - msg.set(_signing_verifying_nonce, min(MAX_PAYLOAD, 32)); + msg.set(_signing_verifying_nonce, min((uint8_t)MAX_PAYLOAD_SIZE, 32u)); _signing_verification_ongoing = true; _signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce return true; @@ -171,40 +150,43 @@ void signerAtsha204PutNonce(MyMessage &msg) return; } - memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), min(MAX_PAYLOAD, 32)); - if (MAX_PAYLOAD < 32) { + (void)memcpy((void *)_signing_signing_nonce, (const void *)msg.getCustom(), + min((uint8_t)MAX_PAYLOAD_SIZE, 32u)); + if (MAX_PAYLOAD_SIZE < 32u) { // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD); + (void)memset((void *)&_signing_signing_nonce[MAX_PAYLOAD_SIZE], 0xAA, 32u - MAX_PAYLOAD_SIZE); } } bool signerAtsha204SignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it - if (mGetLength(msg) > MAX_PAYLOAD-2) { - SIGN_DEBUG(PSTR("!SGN:BND:SIG,SIZE,%" PRIu8 ">%" PRIu8 "\n"), mGetLength(msg), - MAX_PAYLOAD-2); //Message too large + if (msg.getLength() > MAX_PAYLOAD_SIZE - 2) { + SIGN_DEBUG(PSTR("!SGN:BND:SIG,SIZE,%" PRIu8 ">%" PRIu8 "\n"), msg.getLength(), + MAX_PAYLOAD_SIZE - 2); //Message too large return false; } // Calculate signature of message - mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated + msg.setSigned(true); // make sure signing flag is set before signature is calculated signerCalculateSignature(msg, true); +#if defined(MY_SIGNING_NODE_WHITELISTING) if (DO_WHITELIST(msg.destination)) { // Salt the signature with the senders nodeId and the unique serial of the ATSHA device // We can reuse the nonce buffer now since it is no longer needed memcpy(_signing_signing_nonce, _signing_hmac, 32); - _signing_signing_nonce[32] = msg.sender; + _signing_signing_nonce[32] = msg.getSender(); memcpy(&_signing_signing_nonce[33], _signing_node_serial_info, 9); // We can 'void' sha256 because the hash is already put in the correct place (void)signerSha256(_signing_signing_nonce, 32+1+9); - SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,ID=%" PRIu8 "\n"), msg.sender); + SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,ID=%" PRIu8 "\n"), msg.getSender()); #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(_signing_node_serial_info, 9); - SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,SERIAL=%s\n"), printStr); + hwDebugBuf2Str(_signing_node_serial_info, 9); + SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,SERIAL=%s\n"), hwDebugPrintStr); #endif } +#endif // Put device back to sleep atsha204_sleep(); @@ -213,7 +195,8 @@ bool signerAtsha204SignMsg(MyMessage &msg) _signing_hmac[0] = SIGNING_IDENTIFIER; // Transfer as much signature data as the remaining space in the message permits - memcpy(&msg.data[mGetLength(msg)], _signing_hmac, min(MAX_PAYLOAD-mGetLength(msg), 32)); + (void)memcpy((void *)&msg.data[msg.getLength()], (const void *)_signing_hmac, + min(MAX_PAYLOAD_SIZE - msg.getLength(), 32)); return true; } @@ -231,8 +214,8 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) _signing_verification_ongoing = false; - if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) { - SIGN_DEBUG(PSTR("!SGN:BND:VER,IDENT=%" PRIu8 "\n"), msg.data[mGetLength(msg)]); + if (msg.data[msg.getLength()] != SIGNING_IDENTIFIER) { + SIGN_DEBUG(PSTR("!SGN:BND:VER,IDENT=%" PRIu8 "\n"), msg.data[msg.getLength()]); return false; } @@ -242,23 +225,23 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) // Look up the senders nodeId in our whitelist and salt the signature with that data size_t j; for (j=0; j < NUM_OF(_signing_whitelist); j++) { - if (_signing_whitelist[j].nodeId == msg.sender) { + if (_signing_whitelist[j].nodeId == msg.getSender()) { // We can reuse the nonce buffer now since it is no longer needed memcpy(_signing_verifying_nonce, _signing_hmac, 32); - _signing_verifying_nonce[32] = msg.sender; + _signing_verifying_nonce[32] = msg.getSender(); memcpy(&_signing_verifying_nonce[33], _signing_whitelist[j].serial, 9); // We can 'void' sha256 because the hash is already put in the correct place (void)signerSha256(_signing_verifying_nonce, 32+1+9); - SIGN_DEBUG(PSTR("SGN:BND:VER WHI,ID=%" PRIu8 "\n"), msg.sender); + SIGN_DEBUG(PSTR("SGN:BND:VER WHI,ID=%" PRIu8 "\n"), msg.getSender()); #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(_signing_whitelist[j].serial, 9); - SIGN_DEBUG(PSTR("SGN:BND:VER WHI,SERIAL=%s\n"), printStr); + hwDebugBuf2Str(_signing_whitelist[j].serial, 9); + SIGN_DEBUG(PSTR("SGN:BND:VER WHI,SERIAL=%s\n"), hwDebugPrintStr); #endif break; } } if (j == NUM_OF(_signing_whitelist)) { - SIGN_DEBUG(PSTR("!SGN:BND:VER WHI,ID=%" PRIu8 " MISSING\n"), msg.sender); + SIGN_DEBUG(PSTR("!SGN:BND:VER WHI,ID=%" PRIu8 " MISSING\n"), msg.getSender()); // Put device back to sleep atsha204_sleep(); return false; @@ -272,8 +255,8 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) _signing_hmac[0] = SIGNING_IDENTIFIER; // Compare the calculated signature with the provided signature - if (signerMemcmp(&msg.data[mGetLength(msg)], _signing_hmac, - min(MAX_PAYLOAD-mGetLength(msg), 32))) { + if (signerMemcmp(&msg.data[msg.getLength()], _signing_hmac, + min(MAX_PAYLOAD_SIZE - msg.getLength(), 32))) { return false; } else { return true; @@ -286,13 +269,13 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) static void signerCalculateSignature(MyMessage &msg, bool signing) { // Signature is calculated on everything expect the first byte in the header - uint16_t bytes_left = mGetLength(msg)+HEADER_SIZE-1; + uint16_t bytes_left = msg.getLength()+HEADER_SIZE-1; int16_t current_pos = 1-(int16_t)HEADER_SIZE; // Start at the second byte in the header uint8_t* nonce = signing ? _signing_signing_nonce : _signing_verifying_nonce; #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(nonce, 32); - SIGN_DEBUG(PSTR("SGN:BND:NONCE=%s\n"), printStr); + hwDebugBuf2Str(nonce, 32); + SIGN_DEBUG(PSTR("SGN:BND:NONCE=%s\n"), hwDebugPrintStr); #endif while (bytes_left) { @@ -317,8 +300,8 @@ static void signerCalculateSignature(MyMessage &msg, bool signing) } } #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(_signing_hmac, 32); - SIGN_DEBUG(PSTR("SGN:BND:HMAC=%s\n"), printStr); + hwDebugBuf2Str(_signing_hmac, 32); + SIGN_DEBUG(PSTR("SGN:BND:HMAC=%s\n"), hwDebugPrintStr); #endif } diff --git a/core/MySigningAtsha204Soft.cpp b/core/MySigningAtsha204Soft.cpp index 1ee1919c0..7515ba6d8 100644 --- a/core/MySigningAtsha204Soft.cpp +++ b/core/MySigningAtsha204Soft.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -29,34 +29,13 @@ */ #include "MySigning.h" +#include "MyHelperFunctions.h" #ifdef MY_SIGNING_SOFT #define SIGNING_IDENTIFIER (1) //HMAC-SHA256 #if defined(MY_DEBUG_VERBOSE_SIGNING) #define SIGN_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) -static char printStr[65]; -static char i2h(const uint8_t i) -{ - uint8_t k = i & 0x0F; - if (k <= 9) { - return '0' + k; - } else { - return 'A' + k - 10; - } -} - -static void buf2str(const uint8_t *buf, size_t sz) -{ - if (sz > 32) { - sz = 32; //clamp to 32 bytes - } - for (uint8_t i = 0; i < sz; i++) { - printStr[i * 2] = i2h(buf[i] >> 4); - printStr[(i * 2) + 1] = i2h(buf[i]); - } - printStr[sz * 2] = '\0'; -} #else #define SIGN_DEBUG(x,...) #endif @@ -152,8 +131,8 @@ bool signerAtsha204SoftGetNonce(MyMessage &msg) } #ifdef MY_HW_HAS_GETENTROPY - // Try to get MAX_PAYLOAD random bytes - while (hwGetentropy(&_signing_verifying_nonce, MAX_PAYLOAD) != MAX_PAYLOAD); + // Try to get MAX_PAYLOAD_SIZE random bytes + while (hwGetentropy(&_signing_verifying_nonce, MAX_PAYLOAD_SIZE) != MAX_PAYLOAD_SIZE); #else // 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 @@ -164,13 +143,13 @@ bool signerAtsha204SoftGetNonce(MyMessage &msg) SHA256(_signing_verifying_nonce, randBuffer, sizeof(randBuffer)); #endif - if (MAX_PAYLOAD < 32) { + if (MAX_PAYLOAD_SIZE < 32) { // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - (void)memset((void *)&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD); + (void)memset((void *)&_signing_verifying_nonce[MAX_PAYLOAD_SIZE], 0xAA, 32u - MAX_PAYLOAD_SIZE); } // Transfer the first part of the nonce to the message - msg.set(_signing_verifying_nonce, MIN(MAX_PAYLOAD, 32)); + msg.set(_signing_verifying_nonce, MIN((uint8_t)MAX_PAYLOAD_SIZE, (uint8_t)32)); _signing_verification_ongoing = true; _signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce // Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover) @@ -187,46 +166,48 @@ void signerAtsha204SoftPutNonce(MyMessage &msg) if (!_signing_init_ok) { return; } - (void)memcpy((void *)_signing_nonce, (const void *)msg.getCustom(), MIN(MAX_PAYLOAD, 32)); - if (MAX_PAYLOAD < 32) { + (void)memcpy((void *)_signing_nonce, (const void *)msg.getCustom(), MIN((uint8_t)MAX_PAYLOAD_SIZE, + (uint8_t)32)); + if (MAX_PAYLOAD_SIZE < 32) { // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - (void)memset((void *)&_signing_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD); + (void)memset((void *)&_signing_nonce[MAX_PAYLOAD_SIZE], 0xAA, 32u - MAX_PAYLOAD_SIZE); } } bool signerAtsha204SoftSignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it - if (mGetLength(msg) > MAX_PAYLOAD-2) { - SIGN_DEBUG(PSTR("!SGN:BND:SIG,SIZE,%" PRIu8 ">%" PRIu8 "\n"), mGetLength(msg), - MAX_PAYLOAD-2); //Message too large + if (msg.getLength() > MAX_PAYLOAD_SIZE - 2u) { + SIGN_DEBUG(PSTR("!SGN:BND:SIG,SIZE,%" PRIu8 ">%" PRIu8 "\n"), msg.getLength(), + MAX_PAYLOAD_SIZE - 2); //Message too large return false; } // Calculate signature of message - mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated + msg.setSigned(true); // make sure signing flag is set before signature is calculated signerCalculateSignature(msg, true); - - if (DO_WHITELIST(msg.destination)) { +#if defined(MY_SIGNING_NODE_WHITELISTING) + if (DO_WHITELIST(msg.getDestination())) { // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has // provided. We can reuse the nonce buffer now since it is no longer needed (void)memcpy((void *)_signing_nonce, (const void *)_signing_hmac, 32); - _signing_nonce[32] = msg.sender; + _signing_nonce[32] = msg.getSender(); (void)memcpy((void *)&_signing_nonce[33], (const void *)_signing_node_serial_info, 9); SHA256(_signing_hmac, _signing_nonce, 32+1+9); - SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,ID=%" PRIu8 "\n"), msg.sender); + SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,ID=%" PRIu8 "\n"), msg.getSender()); #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(_signing_node_serial_info, 9); - SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,SERIAL=%s\n"), printStr); + hwDebugBuf2Str(_signing_node_serial_info, 9); + SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,SERIAL=%s\n"), hwDebugPrintStr); #endif } +#endif // Overwrite the first byte in the signature with the signing identifier _signing_hmac[0] = SIGNING_IDENTIFIER; // Transfer as much signature data as the remaining space in the message permits - (void)memcpy((void *)&msg.data[mGetLength(msg)], (const void *)_signing_hmac, - MIN(MAX_PAYLOAD-mGetLength(msg), 32)); + (void)memcpy((void *)&msg.data[msg.getLength()], (const void *)_signing_hmac, + MIN((uint8_t)(MAX_PAYLOAD_SIZE - msg.getLength()), (uint8_t)32)); return true; } @@ -244,8 +225,8 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) _signing_verification_ongoing = false; - if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) { - SIGN_DEBUG(PSTR("!SGN:BND:VER,IDENT=%" PRIu8 "\n"), msg.data[mGetLength(msg)]); + if (msg.data[msg.getLength()] != SIGNING_IDENTIFIER) { + SIGN_DEBUG(PSTR("!SGN:BND:VER,IDENT=%" PRIu8 "\n"), msg.data[msg.getLength()]); return false; } @@ -255,22 +236,22 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) // Look up the senders nodeId in our whitelist and salt the signature with that data size_t j; for (j = 0; j < NUM_OF(_signing_whitelist); j++) { - if (_signing_whitelist[j].nodeId == msg.sender) { + if (_signing_whitelist[j].nodeId == msg.getSender()) { // We can reuse the nonce buffer now since it is no longer needed (void)memcpy((void *)_signing_verifying_nonce, (const void *)_signing_hmac, 32); - _signing_verifying_nonce[32] = msg.sender; + _signing_verifying_nonce[32] = msg.getSender(); (void)memcpy((void *)&_signing_verifying_nonce[33], (const void *)_signing_whitelist[j].serial, 9); SHA256(_signing_hmac, _signing_verifying_nonce, 32+1+9); - SIGN_DEBUG(PSTR("SGN:BND:VER WHI,ID=%" PRIu8 "\n"), msg.sender); + SIGN_DEBUG(PSTR("SGN:BND:VER WHI,ID=%" PRIu8 "\n"), msg.getSender()); #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(_signing_whitelist[j].serial, 9); - SIGN_DEBUG(PSTR("SGN:BND:VER WHI,SERIAL=%s\n"), printStr); + hwDebugBuf2Str(_signing_whitelist[j].serial, 9); + SIGN_DEBUG(PSTR("SGN:BND:VER WHI,SERIAL=%s\n"), hwDebugPrintStr); #endif break; } } if (j == NUM_OF(_signing_whitelist)) { - SIGN_DEBUG(PSTR("!SGN:BND:VER WHI,ID=%" PRIu8 " MISSING\n"), msg.sender); + SIGN_DEBUG(PSTR("!SGN:BND:VER WHI,ID=%" PRIu8 " MISSING\n"), msg.getSender()); return false; } #endif @@ -279,8 +260,8 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) _signing_hmac[0] = SIGNING_IDENTIFIER; // Compare the calculated signature with the provided signature - if (signerMemcmp(&msg.data[mGetLength(msg)], _signing_hmac, - MIN(MAX_PAYLOAD-mGetLength(msg), 32))) { + if (signerMemcmp(&msg.data[msg.getLength()], _signing_hmac, + MIN((uint8_t)(MAX_PAYLOAD_SIZE - msg.getLength()), (uint8_t)32))) { return false; } else { return true; @@ -292,19 +273,19 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) static void signerCalculateSignature(MyMessage &msg, const bool signing) { // Signature is calculated on everything expect the first byte in the header - uint8_t bytes_left = mGetLength(msg)+HEADER_SIZE-1; + uint8_t bytes_left = msg.getLength()+HEADER_SIZE-1; int16_t current_pos = 1-(int16_t)HEADER_SIZE; // Start at the second byte in the header uint8_t* nonce = signing ? _signing_nonce : _signing_verifying_nonce; #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(nonce, 32); - SIGN_DEBUG(PSTR("SGN:BND:NONCE=%s\n"), printStr); + hwDebugBuf2Str(nonce, 32); + SIGN_DEBUG(PSTR("SGN:BND:NONCE=%s\n"), hwDebugPrintStr); #endif uint8_t _signing_temp_message[32]; while (bytes_left) { - uint8_t bytes_to_include = MIN(bytes_left, 32); + uint8_t bytes_to_include = MIN(bytes_left, (uint8_t)32); (void)memset((void *)_signing_temp_message, 0x00, sizeof(_signing_temp_message)); (void)memcpy((void *)_signing_temp_message, (const void *)&msg.data[current_pos], bytes_to_include); @@ -322,8 +303,8 @@ static void signerCalculateSignature(MyMessage &msg, const bool signing) } } #ifdef MY_DEBUG_VERBOSE_SIGNING - buf2str(_signing_hmac, 32); - SIGN_DEBUG(PSTR("SGN:BND:HMAC=%s\n"), printStr); + hwDebugBuf2Str(_signing_hmac, 32); + SIGN_DEBUG(PSTR("SGN:BND:HMAC=%s\n"), hwDebugPrintStr); #endif } diff --git a/core/MySplashScreen.cpp b/core/MySplashScreen.cpp index 918860a4b..f4c6fff1a 100644 --- a/core/MySplashScreen.cpp +++ b/core/MySplashScreen.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MySplashScreen.h b/core/MySplashScreen.h index 4e42e9199..8b556b03b 100644 --- a/core/MySplashScreen.h +++ b/core/MySplashScreen.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index 0aced7d33..240c067e3 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -22,7 +22,7 @@ // debug #if defined(MY_DEBUG_VERBOSE_TRANSPORT) #define TRANSPORT_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug -extern char _convBuf[MAX_PAYLOAD * 2 + 1]; +extern char _convBuf[MAX_PAYLOAD_SIZE * 2 + 1]; #else #define TRANSPORT_DEBUG(x,...) //!< debug NULL #endif @@ -97,7 +97,7 @@ void stInitTransition(void) void stInitUpdate(void) { // initialise radio - if (!transportInit()) { + if (!transportHALInit()) { TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n")); setIndication(INDICATION_ERR_INIT_TRANSPORT); transportSwitchSM(stFailure); @@ -116,7 +116,7 @@ void stInitUpdate(void) _transportConfig.parentNodeId = GATEWAY_ADDRESS; _transportConfig.distanceGW = 0u; _transportConfig.nodeId = GATEWAY_ADDRESS; - transportSetAddress(GATEWAY_ADDRESS); + transportHALSetAddress(GATEWAY_ADDRESS); // GW mode: skip FPAR,ID,UPL states transportSwitchSM(stReady); #else @@ -424,10 +424,10 @@ void transportDisable(void) { if (RADIO_CAN_POWER_OFF == true) { TRANSPORT_DEBUG(PSTR("TSF:TDI:TPD\n")); // power down transport - transportPowerDown(); + transportHALPowerDown(); } else { TRANSPORT_DEBUG(PSTR("TSF:TDI:TSL\n")); // send transport to sleep - transportSleep(); + transportHALSleep(); } } @@ -435,11 +435,11 @@ void transportReInitialise(void) { if (RADIO_CAN_POWER_OFF == true) { TRANSPORT_DEBUG(PSTR("TSF:TRI:TPU\n")); // transport power up - transportPowerUp(); - transportSetAddress(_transportConfig.nodeId); + transportHALPowerUp(); + transportHALSetAddress(_transportConfig.nodeId); } else { TRANSPORT_DEBUG(PSTR("TSF:TRI:TSB\n")); // transport standby - transportStandBy(); + transportHALStandBy(); } } @@ -497,7 +497,7 @@ bool transportAssignNodeID(const uint8_t newNodeId) // verify if ID valid if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) { _transportConfig.nodeId = newNodeId; - transportSetAddress(newNodeId); + transportHALSetAddress(newNodeId); // Write ID to EEPROM hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId); TRANSPORT_DEBUG(PSTR("TSF:SID:OK,ID=%" PRIu8 "\n"),newNodeId); // Node ID assigned @@ -505,15 +505,13 @@ bool transportAssignNodeID(const uint8_t newNodeId) } else { TRANSPORT_DEBUG(PSTR("!TSF:SID:FAIL,ID=%" PRIu8 "\n"),newNodeId); // ID is invalid, cannot assign ID setIndication(INDICATION_ERR_NET_FULL); - _transportConfig.nodeId = AUTO; return false; } } bool transportRouteMessage(MyMessage &message) { - const uint8_t destination = message.destination; - uint8_t route = _transportConfig.parentNodeId; // by default, all traffic is routed via parent node + const uint8_t destination = message.getDestination(); if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) { TRANSPORT_DEBUG(PSTR("!TSF:RTE:FPAR ACTIVE\n")); // find parent active, message not sent @@ -521,6 +519,8 @@ bool transportRouteMessage(MyMessage &message) return false; } + uint8_t route; + if (destination == GATEWAY_ADDRESS) { route = _transportConfig.parentNodeId; // message to GW always routes via parent } else if (destination == BROADCAST_ADDRESS) { @@ -532,7 +532,7 @@ bool transportRouteMessage(MyMessage &message) if (route == AUTO) { TRANSPORT_DEBUG(PSTR("!TSF:RTE:%" PRIu8 " UNKNOWN\n"), destination); // route unknown #if !defined(MY_GATEWAY_FEATURE) - if (message.last != _transportConfig.parentNodeId) { + if (message.getLast() != _transportConfig.parentNodeId) { // message not from parent, i.e. child node - route it to parent route = _transportConfig.parentNodeId; } else { @@ -602,13 +602,13 @@ bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t ms { const uint32_t enterMS = hwMillis(); // invalidate msg type - _msg.type = !msgType; + _msg.setType(!msgType); bool expectedResponse = false; while ((hwMillis() - enterMS < waitingMS) && !expectedResponse) { // process incoming messages transportProcessFIFO(); doYield(); - expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType); + expectedResponse = (_msg.getCommand() == cmd && _msg.getType() == msgType); } return expectedResponse; } @@ -648,44 +648,27 @@ void transportProcessMessage(void) (void)signerCheckTimer(); // receive message setIndication(INDICATION_RX); - uint8_t payloadLength = transportReceive((uint8_t *) - &_msg.last); // last is the first byte of the payload buffer + uint8_t payloadLength; + // last is the first byte of the payload buffer + if (!transportHALReceive(&_msg, &payloadLength)) { + return; + } // get message length and limit size - const uint8_t msgLength = min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD); + const uint8_t msgLength = _msg.getLength(); // calculate expected length - const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength); -#if defined(MY_RF24_ENABLE_ENCRYPTION) || defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) || defined(MY_RFM95_ENABLE_ENCRYPTION) - // payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check - payloadLength = expectedMessageLength; -#endif - const uint8_t command = mGetCommand(_msg); - const uint8_t type = _msg.type; - const uint8_t sender = _msg.sender; - const uint8_t last = _msg.last; - const uint8_t destination = _msg.destination; + + const uint8_t command = _msg.getCommand(); + const uint8_t type = _msg.getType(); + const uint8_t sender = _msg.getSender(); + const uint8_t last = _msg.getLast(); + const uint8_t destination = _msg.getDestination(); TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%" PRIu8 "-%" PRIu8 "-%" PRIu8 ",s=%" PRIu8 ",c=%" PRIu8 ",t=%" PRIu8 ",pt=%" PRIu8 ",l=%" PRIu8 ",sg=%" PRIu8 ":%s\n"), - sender, last, destination, _msg.sensor, command, type, mGetPayloadType(_msg), msgLength, - mGetSigned(_msg), ((command == C_INTERNAL && + sender, last, destination, _msg.getSensor(), command, type, _msg.getPayloadType(), msgLength, + _msg.getSigned(), ((command == C_INTERNAL && type == I_NONCE_RESPONSE) ? "" : _msg.getString(_convBuf))); - // Reject payloads with incorrect length - if (payloadLength != expectedMessageLength) { - setIndication(INDICATION_ERR_LENGTH); - TRANSPORT_DEBUG(PSTR("!TSF:MSG:LEN=%" PRIu8 ",EXP=%" PRIu8 "\n"), payloadLength, - expectedMessageLength); // invalid payload length - return; - } - - // Reject messages with incorrect protocol version - if (mGetVersion(_msg) != PROTOCOL_VERSION) { - setIndication(INDICATION_ERR_VERSION); - TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%" PRIu8 "!=%" PRIu8 "\n"), mGetVersion(_msg), - PROTOCOL_VERSION); // protocol version mismatch - return; - } - // Reject messages that do not pass verification if (!signerVerifyMsg(_msg)) { setIndication(INDICATION_ERR_SIGN); @@ -714,24 +697,22 @@ void transportProcessMessage(void) // Is message addressed to this node? if (destination == _transportConfig.nodeId) { - // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes) - mSetLength(_msg, min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD)); // null terminate data _msg.data[msgLength] = 0u; - // Check if sender requests an ack back. - if (mGetRequestAck(_msg)) { - TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested + // Check if sender requests an echo. + if (_msg.getRequestEcho()) { + TRANSPORT_DEBUG(PSTR("TSF:MSG:ECHO REQ\n")); // ECHO requested _msgTmp = _msg; // Copy message - mSetRequestAck(_msgTmp, - false); // Reply without ack flag (otherwise we would end up in an eternal loop) - mSetAck(_msgTmp, true); // set ACK flag - _msgTmp.sender = _transportConfig.nodeId; - _msgTmp.destination = sender; - // send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply + // Reply without echo flag (otherwise we would end up in an eternal loop) + _msgTmp.setRequestEcho(false); + _msgTmp.setEcho(true); // set ECHO flag + _msgTmp.setSender(_transportConfig.nodeId); + _msgTmp.setDestination(sender); + // send ECHO, use transportSendRoute since ECHO reply is not internal, i.e. if !transportOK do not reply (void)transportSendRoute(_msgTmp); } - if(!mGetAck(_msg)) { - // only process if not ACK + if(!_msg.isEcho()) { + // only process if not ECHO if (command == C_INTERNAL) { // Process signing related internal messages if (signerProcessInternal(_msg)) { @@ -741,7 +722,7 @@ void transportProcessMessage(void) if (type == I_ID_RESPONSE) { #if (MY_NODE_ID == AUTO) // only active if node ID dynamic - if ((_msg.sensor == _transportToken) || (_msg.sensor == AUTO)) { + if ((_msg.getSensor() == _transportToken) || (_msg.getSensor() == AUTO)) { (void)transportAssignNodeID(_msg.getByte()); } else { TRANSPORT_DEBUG(PSTR("!TSF:MSG:ID TK INVALID\n")); @@ -806,15 +787,15 @@ void transportProcessMessage(void) if (type == I_SIGNAL_REPORT_REQUEST) { int16_t value = INVALID_RSSI; #if defined(MY_SIGNAL_REPORT_ENABLED) - const char command = _msg.data[0]; + const char internalCommand = _msg.data[0]; if (_msg.data[1] != '!') { - value = transportSignalReport(command); + value = transportSignalReport(internalCommand); } else { // send request - if (transportRouteMessage(build(_msgTmp, _msg.last, NODE_SENSOR_ID, C_INTERNAL, + if (transportRouteMessage(build(_msgTmp, _msg.getLast(), NODE_SENSOR_ID, C_INTERNAL, I_SIGNAL_REPORT_REVERSE).set((uint8_t)255))) { // S>s, R>r, ascii delta = 32 - value = transportSignalReport(command + 32); // reverse + value = transportSignalReport(internalCommand + 32); // reverse }; } #endif @@ -834,7 +815,7 @@ void transportProcessMessage(void) } } else { TRANSPORT_DEBUG( - PSTR("TSF:MSG:ACK\n")); // received message is ACK, no internal processing, handover to msg callback + PSTR("TSF:MSG:ECHO\n")); // received message is ECHO, no internal processing, handover to msg callback } #if defined(MY_OTA_LOG_RECEIVER_FEATURE) if ((type == I_LOG_MESSAGE) && (command == C_INTERNAL)) { @@ -948,7 +929,7 @@ void transportInvokeSanityCheck(void) { // Suppress this because the function may return a variable value in some configurations // cppcheck-suppress knownConditionTrueFalse - if (!transportSanityCheck()) { + if (!transportHALSanityCheck()) { TRANSPORT_DEBUG(PSTR("!TSF:SAN:FAIL\n")); // sanity check fail transportSwitchSM(stFailure); } else { @@ -972,7 +953,7 @@ void transportProcessFIFO(void) uint8_t _processedMessages = MAX_SUBSEQ_MSGS; // process all msgs in FIFO or counter exit - while (transportAvailable() && _processedMessages--) { + while (transportHALDataAvailable() && _processedMessages--) { transportProcessMessage(); } #if defined(MY_OTA_FIRMWARE_FEATURE) @@ -985,7 +966,8 @@ void transportProcessFIFO(void) bool transportSendWrite(const uint8_t to, MyMessage &message) { - message.last = _transportConfig.nodeId; // Update last + message.setLast(_transportConfig.nodeId); // Update last + // sign message if required if (!signerSignMsg(message)) { TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n")); @@ -994,27 +976,26 @@ bool transportSendWrite(const uint8_t to, MyMessage &message) } // msg length changes if signed - const uint8_t totalMsgLength = HEADER_SIZE + ( mGetSigned(message) ? MAX_PAYLOAD : mGetLength( - message) ); - + const uint8_t totalMsgLength = HEADER_SIZE + ( message.getSigned() ? MAX_PAYLOAD_SIZE : + message.getLength() ); + const bool noACK = _transportConfig.passiveMode || (to == BROADCAST_ADDRESS); // send setIndication(INDICATION_TX); - bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength), - _transportConfig.passiveMode); - // broadcasting (workaround counterfeits) - result |= (to == BROADCAST_ADDRESS); + const bool result = transportHALSend(to, &message, totalMsgLength, + noACK); TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%" PRIu8 "-%" PRIu8 "-%" PRIu8 "-%" PRIu8 ",s=%" PRIu8 ",c=%" PRIu8 ",t=%" PRIu8 ",pt=%" PRIu8 ",l=%" PRIu8 ",sg=%" PRIu8 ",ft=%" PRIu8 ",st=%s:%s\n"), - (_transportConfig.passiveMode ? "?" : result ? "" : "!"), message.sender, message.last, to, - message.destination, - message.sensor, - mGetCommand(message), message.type, - mGetPayloadType(message), mGetLength(message), mGetSigned(message), + (noACK ? "?" : result ? "" : "!"), message.getSender(), message.getLast(), + to, + message.getDestination(), + message.getSensor(), + message.getCommand(), message.getType(), + message.getPayloadType(), message.getLength(), message.getSigned(), _transportSM.failedUplinkTransmissions, (result ? "OK" : "NACK"), - ((mGetCommand(message) == C_INTERNAL && - message.type == I_NONCE_RESPONSE) ? "" : message.getString(_convBuf))); + ((message.getCommand() == C_INTERNAL && + message.getType() == I_NONCE_RESPONSE) ? "" : message.getString(_convBuf))); return result; } @@ -1110,25 +1091,26 @@ void transportTogglePassiveMode(const bool OnOff) int16_t transportGetSignalReport(const signalReport_t signalReport) { +#if defined(MY_SIGNAL_REPORT_ENABLED) int16_t result; switch (signalReport) { case SR_RX_RSSI: - result = transportGetReceivingRSSI(); + result = transportHALGetReceivingRSSI(); break; case SR_TX_RSSI: - result = transportGetSendingRSSI(); + result = transportHALGetSendingRSSI(); break; case SR_RX_SNR: - result = transportGetReceivingSNR(); + result = transportHALGetReceivingSNR(); break; case SR_TX_SNR: - result = transportGetSendingSNR(); + result = transportHALGetSendingSNR(); break; case SR_TX_POWER_LEVEL: - result = transportGetTxPowerLevel(); + result = transportHALGetTxPowerLevel(); break; case SR_TX_POWER_PERCENT: - result = transportGetTxPowerPercent(); + result = transportHALGetTxPowerPercent(); break; case SR_UPLINK_QUALITY: result = transportInternalToRSSI(_transportSM.uplinkQualityRSSI); @@ -1138,10 +1120,15 @@ int16_t transportGetSignalReport(const signalReport_t signalReport) break; } return result; +#else + (void)signalReport; + return 0; +#endif } int16_t transportSignalReport(const char command) { +#if defined(MY_SIGNAL_REPORT_ENABLED) signalReport_t reportCommand; switch (command) { case 'S': @@ -1179,4 +1166,8 @@ int16_t transportSignalReport(const char command) const uint16_t result = transportGetSignalReport(reportCommand); TRANSPORT_DEBUG(PSTR("TSF:SIR:CMD=%" PRIu8 ",VAL=%" PRIu16 "\n"), reportCommand, result); return result; +#else + (void)command; + return 0; +#endif } diff --git a/core/MyTransport.h b/core/MyTransport.h index 54fc7d695..329fc722a 100644 --- a/core/MyTransport.h +++ b/core/MyTransport.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -91,8 +91,8 @@ * |!| 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 | ECHO REQ | ECHO message requested +* | | TSF | MSG | ECHO | ECHO 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 @@ -106,8 +106,6 @@ * | | TSF | MSG | RCV CB | Hand over message to @ref receive() callback function * | | TSF | MSG | REL MSG | Relay message * | | TSF | MSG | REL PxNG,HP=%%d | Relay PING/PONG message, increment hop counter (HP) -* |!| TSF | MSG | LEN=%%d,EXP=%%d | Invalid message length (LEN), exptected length (EXP) -* |!| 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 @@ -155,6 +153,46 @@ * - ft=failed uplink transmission counter * - st=send status, OK=success, NACK=no radio ACK received * +* @startuml +* state top as "Transport" { +* state Init +* state Failure +* state Ready +* state Parent +* state ID +* state Uplink +* } +* +* [*] --> Init +* Init : entry / Read config from eeprom +* Init --> Failure : [! transportInit()\n|| ID == 0\n|| ID == 255 ] +* Init --> Ready : [MY_GATEWAY_FEATURE] +* Init --> Parent : [else] +* +* Parent : entry / Broadcast Find Parent +* Parent --> ID : [MY_PARENT_NODE_IS_STATIC\n|| MY_PASSIVE_NODE\n|| Parent found] +* Parent --> Parent : [timeout\n&& retries left] +* Parent --> Failure : [timeout\n&& no retries left] +* +* ID : entry / Request Node ID +* ID --> Uplink : [ID valid] +* ID --> ID : [timeout\n&& retries left] +* ID --> Failure : [timeout\n&& no retries left] +* +* Uplink : entry / Check uplink (PING) +* Uplink --> Uplink : [timeout\n&& retries left] +* Uplink --> Parent : [timeout\n&& no retries left] +* Uplink --> Ready : [MY_TRANSPORT_UPLINK_CHECK_DISABLED\n|| Uplink ok (PONG)] +* +* Ready : entry / Transport ready callback +* Ready : MY_GATEWAY_FEATURE && Network discovery required / Send discovery +* Ready --> Parent : [!MY_PARENT_NODE_IS_STATIC\n&& Uplink failure overflow] +* +* Failure : entry / Disable transport +* Failure --> Init : [timeout] +* top --> Failure : [MY_TRANSPORT_SANITY_CHECK\n&& !transportSanityCheck] +* @enduml +* * @brief API declaration for MyTransport * */ @@ -192,13 +230,13 @@ #define MY_TRANSPORT_STATE_RETRIES (3u) //!< retries before switching to FAILURE #endif -#define AUTO (255u) //!< ID 255 is reserved +#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 number of hops for ping/pong -#define INVALID_HOPS (255u) //!< invalid hops -#define MAX_SUBSEQ_MSGS (5u) //!< Maximum number of subsequently processed messages in FIFO (to prevent transport deadlock if HW issue) -#define UPLINK_QUALITY_WEIGHT (0.05f) //!< UPLINK_QUALITY_WEIGHT +#define MAX_HOPS (254u) //!< maximal number of hops for ping/pong +#define INVALID_HOPS (255u) //!< invalid hops +#define MAX_SUBSEQ_MSGS (5u) //!< Maximum number of subsequently processed messages in FIFO (to prevent transport deadlock if HW issue) +#define UPLINK_QUALITY_WEIGHT (0.05f) //!< UPLINK_QUALITY_WEIGHT // parent node check @@ -222,10 +260,10 @@ typedef void(*transportCallback_t)(void); */ 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) - uint8_t passiveMode : 1; //!< Passive mode - uint8_t reserved : 7; //!< Reserved + uint8_t parentNodeId; //!< Where this node sends its messages + uint8_t distanceGW; //!< This nodes distance to sensor net gateway (number of hops) + uint8_t passiveMode : 1; //!< Passive mode + uint8_t reserved : 7; //!< Reserved } transportConfig_t; /** @@ -243,8 +281,8 @@ typedef struct { typedef int16_t transportRSSI_t; //!< Datatype for internal RSSI storage // helper macro for conversion -#define transportInternalToRSSI(__value) ((int16_t)__value>>4) //!< Convert internal RSSI to RSSI -#define transportRSSItoInternal(__value) ((transportRSSI_t)__value<<4) //!< Convert RSSI to internal RSSI +#define transportInternalToRSSI(__value) ((int16_t)__value >> 4) //!< Convert internal RSSI to RSSI +#define transportRSSItoInternal(__value) ((transportRSSI_t)__value << 4) //!< Convert RSSI to internal RSSI /** * @brief Status variables and SM state @@ -253,31 +291,32 @@ typedef int16_t transportRSSI_t; //!< Datatype for internal RSSI storage */ typedef struct { // SM variables - transportState_t *currentState; //!< pointer to current FSM state - uint32_t stateEnter; //!< state enter timepoint + transportState_t *currentState; //!< pointer to current FSM state + uint32_t stateEnter; //!< state enter timepoint // general transport variables - uint32_t lastUplinkCheck; //!< last uplink check, required to prevent GW flooding + uint32_t lastUplinkCheck; //!< last uplink check, required to prevent GW flooding // 8 bits - bool findingParentNode : 1; //!< flag finding parent node is active - bool preferredParentFound : 1; //!< flag preferred parent found - bool uplinkOk : 1; //!< flag uplink ok - bool pingActive : 1; //!< flag ping active - bool transportActive : 1; //!< flag transport active - uint8_t stateRetries : 3; //!< retries / state re-enter (max 7) + bool findingParentNode : 1; //!< flag finding parent node is active + bool preferredParentFound : 1; //!< flag preferred parent found + bool uplinkOk : 1; //!< flag uplink ok + bool pingActive : 1; //!< flag ping active + bool transportActive : 1; //!< flag transport active + uint8_t stateRetries : 3; //!< retries / state re-enter (max 7) // 8 bits uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions (max 15) - uint8_t failureCounter : 3; //!< counter for TSM failures (max 7) - bool msgReceived : 1; //!< flag message received - - uint8_t pingResponse; //!< stores I_PONG hops - transportRSSI_t uplinkQualityRSSI; //!< Uplink quality, internal RSSI representation + uint8_t failureCounter : 3; //!< counter for TSM failures (max 7) + bool msgReceived : 1; //!< flag message received + uint8_t pingResponse; //!< stores I_PONG hops +#if defined(MY_SIGNAL_REPORT_ENABLED) + transportRSSI_t uplinkQualityRSSI; //!< Uplink quality, internal RSSI representation +#endif } transportSM_t; /** * @brief RAM routing table */ typedef struct { - uint8_t route[SIZE_ROUTES]; //!< route for node + uint8_t route[SIZE_ROUTES]; //!< route for node } routingTable_t; // PRIVATE functions @@ -508,6 +547,7 @@ void transportDisable(void); * @brief Reinitialise transport. Put transport to standby - If xxx_POWER_PIN set, power up and go to standby */ void transportReInitialise(void); + /** * @brief Get transport signal report * @param command: @@ -520,14 +560,14 @@ void transportReInitialise(void); * U = Uplink quality (via ACK from parent node), avg. RSSI * @return Signal report (if report is not available, INVALID_RSSI, INVALID_SNR, INVALID_PERCENT, or INVALID_LEVEL is sent instead) */ -int16_t transportSignalReport(const char command); +int16_t transportSignalReport(const char command) __attribute__((unused)); /** * @brief Get transport signal report * @param signalReport * @return report */ -int16_t transportGetSignalReport(const signalReport_t signalReport); +int16_t transportGetSignalReport(const signalReport_t signalReport) __attribute__((unused)); #endif // MyTransport_h /** @}*/ diff --git a/core/Version.h b/core/Version.h index e6a3874f7..4490778e7 100644 --- a/core/Version.h +++ b/core/Version.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -48,8 +48,8 @@ #define MYSENSORS_LIBRARY_VERSION_MAJOR 2 //!< Major release version #define MYSENSORS_LIBRARY_VERSION_MINOR 3 //!< Minor release version -#define MYSENSORS_LIBRARY_VERSION_PATCH 1 //!< Patch version -#define MYSENSORS_LIBRARY_VERSION_PRERELEASE "beta" //!< Pre-release suffix, i.e. alpha, beta, rc.1, etc +#define MYSENSORS_LIBRARY_VERSION_PATCH 2 //!< Patch version +#define MYSENSORS_LIBRARY_VERSION_PRERELEASE "" //!< Pre-release suffix, i.e. alpha, beta, rc.1, etc #define MYSENSORS_LIBRARY_VERSION_PRERELEASE_NUMBER 0xFF //!< incremental counter, starting at 0x00. 0xFF for final release diff --git a/drivers/NVM/Flash.h b/drivers/NVM/Flash.h index ffffbe26f..de23b7e3a 100644 --- a/drivers/NVM/Flash.h +++ b/drivers/NVM/Flash.h @@ -74,11 +74,11 @@ #define FLASH_WRITES_PER_WORD 2 #define FLASH_WRITES_PER_PAGE 403 #else -#define FLASH_ERASE_CYCLES 10000 -#define FLASH_PAGE_SIZE 4096 -#define FLASH_ERASE_PAGE_TIME 100 +#define FLASH_ERASE_CYCLES 10000 //!< FLASH_ERASE_CYCLES +#define FLASH_PAGE_SIZE 4096 //!< FLASH_PAGE_SIZE +#define FLASH_ERASE_PAGE_TIME 100 //!< FLASH_ERASE_PAGE_TIME //#define FLASH_SUPPORTS_RANDOM_WRITE true -#define FLASH_WRITES_PER_WORD 1 +#define FLASH_WRITES_PER_WORD 1 //!< FLASH_WRITES_PER_WORD #warning "Unknown platform. Please check the code." #endif @@ -166,7 +166,7 @@ class FlashClass void wait_for_ready(); }; -extern FlashClass Flash; +extern FlashClass Flash; //!< extern FlashClass /** Load Hardwarespecific files */ #ifdef NRF5 diff --git a/drivers/NVM/VirtualPage.h b/drivers/NVM/VirtualPage.h index de8fe7226..dc3016c10 100644 --- a/drivers/NVM/VirtualPage.h +++ b/drivers/NVM/VirtualPage.h @@ -134,6 +134,6 @@ class VirtualPageClass uint32_t get_page_erase_cycles(uint32_t *address); }; -extern VirtualPageClass VirtualPage; +extern VirtualPageClass VirtualPage; //!< extern VirtualPageClass /** @} */ diff --git a/drivers/SPIFlash/SPIFlash.cpp b/drivers/SPIFlash/SPIFlash.cpp index 38e308276..b966e0898 100644 --- a/drivers/SPIFlash/SPIFlash.cpp +++ b/drivers/SPIFlash/SPIFlash.cpp @@ -199,7 +199,7 @@ void SPIFlash::command(uint8_t cmd, bool isWrite) // that is because some chips can take several seconds to carry out a chip erase or other similar multi block or entire-chip operations // a recommended alternative to such situations where chip can be or not be present is to add a 10k or similar weak pulldown on the // open drain MISO input which can read noise/static and hence return a non 0 status byte, causing the while() to hang when a flash chip is not present - while(busy()); + if (cmd != SPIFLASH_WAKE) while(busy()); select(); SPI.transfer(cmd); } diff --git a/drivers/SPIFlash/SPIFlash.h b/drivers/SPIFlash/SPIFlash.h index 6adc3a242..89b7cb6aa 100644 --- a/drivers/SPIFlash/SPIFlash.h +++ b/drivers/SPIFlash/SPIFlash.h @@ -162,7 +162,7 @@ class SPIFlash { public: static uint8_t UNIQUEID[8]; //!< Storage for unique identifier - SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID=0); //!< Constructor + explicit SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID=0); //!< Constructor bool initialize(); //!< setup SPI, read device ID etc... void command(uint8_t cmd, bool isWrite= false); //!< Send a command to the flash chip, pass TRUE for isWrite when its a write command diff --git a/examples/AirQualitySensor/AirQualitySensor.ino b/examples/AirQualitySensor/AirQualitySensor.ino index 3fa4bf5c1..f3265d9fd 100644 --- a/examples/AirQualitySensor/AirQualitySensor.ino +++ b/examples/AirQualitySensor/AirQualitySensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -67,8 +67,7 @@ uint32_t SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) //VARIABLES float Ro = 10000.0; // this has to be tuned 10K Ohm int val = 0; // variable to store the value coming from the sensor -float valMQ =0.0; -float lastMQ =0.0; +uint16_t lastMQ = 0; float LPGCurve[3] = {2.3,0.21,-0.47}; //two points are taken from the curve. //with these two points, a line is formed which is "approximately equivalent" //to the original curve. @@ -149,18 +148,18 @@ Remarks: This function assumes that the sensor is in clean air. It use float MQCalibration(int mq_pin) { int i; - float val=0; + float inVal=0; for (i=0; i - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/BinarySwitchSleepSensor/BinarySwitchSleepSensor.ino b/examples/BinarySwitchSleepSensor/BinarySwitchSleepSensor.ino index 73f545172..5b0733845 100644 --- a/examples/BinarySwitchSleepSensor/BinarySwitchSleepSensor.ino +++ b/examples/BinarySwitchSleepSensor/BinarySwitchSleepSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/CO2Sensor/CO2Sensor.ino b/examples/CO2Sensor/CO2Sensor.ino index 5f088fa27..0299f4f17 100644 --- a/examples/CO2Sensor/CO2Sensor.ino +++ b/examples/CO2Sensor/CO2Sensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/ClearEepromConfig/ClearEepromConfig.ino b/examples/ClearEepromConfig/ClearEepromConfig.ino index 302991164..9ef6186aa 100644 --- a/examples/ClearEepromConfig/ClearEepromConfig.ino +++ b/examples/ClearEepromConfig/ClearEepromConfig.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/DimmableLEDActuator/DimmableLEDActuator.ino b/examples/DimmableLEDActuator/DimmableLEDActuator.ino index 411cc3b9d..538625ec0 100644 --- a/examples/DimmableLEDActuator/DimmableLEDActuator.ino +++ b/examples/DimmableLEDActuator/DimmableLEDActuator.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -85,13 +85,13 @@ void loop() void receive(const MyMessage &message) { - if (message.type == V_LIGHT || message.type == V_DIMMER) { + if (message.getType() == V_LIGHT || message.getType() == V_DIMMER) { // Retrieve the power or dim level from the incoming request message int requestedLevel = atoi( message.data ); // Adjust incoming level if this is a V_LIGHT variable update [0 == off, 1 == on] - requestedLevel *= ( message.type == V_LIGHT ? 100 : 1 ); + requestedLevel *= ( message.getType() == V_LIGHT ? 100 : 1 ); // Clip incoming level to valid range of 0 to 100 requestedLevel = requestedLevel > 100 ? 100 : requestedLevel; diff --git a/examples/DimmableLight/DimmableLight.ino b/examples/DimmableLight/DimmableLight.ino index 8fde150ea..f290cbf2a 100644 --- a/examples/DimmableLight/DimmableLight.ino +++ b/examples/DimmableLight/DimmableLight.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -90,7 +90,7 @@ void loop() void receive(const MyMessage &message) { - if (message.type == V_LIGHT) { + if (message.getType() == V_LIGHT) { Serial.println( "V_LIGHT command received..." ); int lstate= atoi( message.data ); @@ -112,7 +112,7 @@ void receive(const MyMessage &message) //When receiving a V_LIGHT command we switch the light between OFF and the last received dimmer value //This means if you previously set the lights dimmer value to 50%, and turn the light ON //it will do so at 50% - } else if (message.type == V_DIMMER) { + } else if (message.getType() == V_DIMMER) { Serial.println( "V_DIMMER command received..." ); int dimvalue= atoi( message.data ); if ((dimvalue<0)||(dimvalue>100)) { diff --git a/examples/DustSensor/DustSensor.ino b/examples/DustSensor/DustSensor.ino index 9452768ef..82a6df974 100644 --- a/examples/DustSensor/DustSensor.ino +++ b/examples/DustSensor/DustSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -60,7 +60,6 @@ float lastDUST =0.0; int samplingTime = 280; int deltaTime = 40; int sleepTime = 9680; -float voMeasured = 0; float calcVoltage = 0; float dustDensity = 0; diff --git a/examples/DustSensorDSM/DustSensorDSM.ino b/examples/DustSensorDSM/DustSensorDSM.ino index 337346572..39dd8b3de 100644 --- a/examples/DustSensorDSM/DustSensorDSM.ino +++ b/examples/DustSensorDSM/DustSensorDSM.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino b/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino index d2a08cfe4..91f3f53c8 100644 --- a/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino +++ b/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino @@ -1,38 +1,39 @@ /* - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Henrik Ekblad - * Copyright (C) 2013-2018 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. - * + The MySensors Arduino library handles the wireless radio link and protocol + between your home built sensors/actuators and HA controller of choice. + The sensors forms a self healing radio network with optional repeaters. Each + repeater and gateway builds a routing tables in EEPROM which keeps track of the + network topology allowing messages to be routed to nodes. + + Created by Henrik Ekblad + Copyright (C) 2013-2019 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. + ******************************* - * - * REVISION HISTORY - * Version 1.0 - Henrik Ekblad - * - * DESCRIPTION - * This sketch provides an example how to implement a LM393 PCB - * Use this sensor to measure kWh and Watt of your house meter - * You need to set the correct pulsefactor of your meter (blinks per kWh). - * The sensor starts by fetching current kWh value from gateway. - * Reports both kWh and Watt back to gateway. - * - * Unfortunately millis() won't increment when the Arduino is in - * sleepmode. So we cannot make this sensor sleep if we also want - * to calculate/report watt value. - * http://www.mysensors.org/build/pulse_power - */ + + REVISION HISTORY + Version 1.0 - Henrik Ekblad + Version 1.1 - Peter Andersson added millis watt calculation if time between pulses > 1h + + DESCRIPTION + This sketch provides an example how to implement a LM393 PCB + Use this sensor to measure kWh and Watt of your house meter + You need to set the correct pulsefactor of your meter (blinks per kWh). + The sensor starts by fetching current kWh value from gateway. + Reports both kWh and Watt back to gateway. + + Unfortunately millis() won't increment when the Arduino is in + sleepmode. So we cannot make this sensor sleep if we also want + to calculate/report watt value. + http://www.mysensors.org/build/pulse_power +*/ // Enable debug prints #define MY_DEBUG @@ -46,26 +47,54 @@ #include #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your light sensor. (Only 2 and 3 generates interrupt!) -#define PULSE_FACTOR 1000 // Number of blinks per of your meter +#define PULSE_FACTOR 1000 // Number of blinks per kWh of your meter. Normally 1000. #define SLEEP_MODE false // Watt value can only be reported when sleep mode is false. #define MAX_WATT 10000 // Max watt value to report. This filters outliers. #define CHILD_ID 1 // Id of the sensor child uint32_t SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't want to spam the gateway. -double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour +double ppwh = ((double)PULSE_FACTOR) / 1000; // Pulses per watt hour bool pcReceived = false; volatile uint32_t pulseCount = 0; -volatile uint32_t lastBlink = 0; +volatile uint32_t lastBlinkmicros = 0; +volatile uint32_t lastBlinkmillis = 0; volatile uint32_t watt = 0; uint32_t oldPulseCount = 0; uint32_t oldWatt = 0; double oldkWh; uint32_t lastSend; -MyMessage wattMsg(CHILD_ID,V_WATT); -MyMessage kWhMsg(CHILD_ID,V_KWH); -MyMessage pcMsg(CHILD_ID,V_VAR1); +MyMessage wattMsg(CHILD_ID, V_WATT); +MyMessage kWhMsg(CHILD_ID, V_KWH); +MyMessage pcMsg(CHILD_ID, V_VAR1); +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR +#else +#define IRQ_HANDLER_ATTR +#endif + +void IRQ_HANDLER_ATTR onPulse() +{ + if (!SLEEP_MODE) { + uint32_t newBlinkmicros = micros(); + uint32_t newBlinkmillis = millis(); + uint32_t intervalmicros = newBlinkmicros - lastBlinkmicros; + uint32_t intervalmillis = newBlinkmillis - lastBlinkmillis; + if (intervalmicros < 10000L && intervalmillis < 10L) { // Sometimes we get interrupt on RISING + return; + } + if (intervalmillis < 360000) { // Less than an hour since last pulse, use microseconds + watt = (3600000000.0 / intervalmicros) / ppwh; + } else { + watt = (3600000.0 / intervalmillis) / + ppwh; // more thAn an hour since last pulse, use milliseconds as micros will overflow after 70min + } + lastBlinkmicros = newBlinkmicros; + lastBlinkmillis = newBlinkmillis; + } + pulseCount++; +} void setup() { @@ -74,16 +103,16 @@ void setup() // Use the internal pullup to be able to hook up this sketch directly to an energy meter with S0 output // If no pullup is used, the reported usage will be too high because of the floating pin - pinMode(DIGITAL_INPUT_SENSOR,INPUT_PULLUP); + pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, RISING); - lastSend=millis(); + lastSend = millis(); } void presentation() { // Send the sketch version information to the gateway and Controller - sendSketchInfo("Energy Meter", "1.0"); + sendSketchInfo(F("Energy Meter"), F("1.1")); // Register this device as power sensor present(CHILD_ID, S_POWER); @@ -97,9 +126,9 @@ void loop() if (pcReceived && (SLEEP_MODE || sendTime)) { // New watt value has been calculated if (!SLEEP_MODE && watt != oldWatt) { - // Check that we don't get unreasonable large watt value. + // Check that we don't get unreasonable large watt value, which // could happen when long wraps or false interrupt triggered - if (watt<((uint32_t)MAX_WATT)) { + if (watt < ((uint32_t)MAX_WATT)) { send(wattMsg.set(watt)); // Send watt value to gw } Serial.print("Watt:"); @@ -110,7 +139,7 @@ void loop() // Pulse count value has changed if (pulseCount != oldPulseCount) { send(pcMsg.set(pulseCount)); // Send pulse count value to gw - double kWh = ((double)pulseCount/((double)PULSE_FACTOR)); + double kWh = ((double)pulseCount / ((double)PULSE_FACTOR)); oldPulseCount = pulseCount; if (kWh != oldkWh) { send(kWhMsg.set(kWh, 4)); // Send kWh value to gw @@ -119,36 +148,22 @@ void loop() } lastSend = now; } else if (sendTime && !pcReceived) { - // No pulse count value received. Try requesting it again + // No pulse count value received from controller. Try requesting it again. request(CHILD_ID, V_VAR1); - lastSend=now; + lastSend = now; } if (SLEEP_MODE) { - sleep(SEND_FREQUENCY); + sleep(SEND_FREQUENCY, false); } } void receive(const MyMessage &message) { - if (message.type==V_VAR1) { + if (message.getType()==V_VAR1) { pulseCount = oldPulseCount = message.getLong(); Serial.print("Received last pulse count value from gw:"); Serial.println(pulseCount); pcReceived = true; } } - -void onPulse() -{ - if (!SLEEP_MODE) { - uint32_t newBlink = micros(); - uint32_t interval = newBlink-lastBlink; - if (interval<10000L) { // Sometimes we get interrupt on RISING - return; - } - watt = (3600000000.0 /interval) / ppwh; - lastBlink = newBlink; - } - pulseCount++; -} diff --git a/examples/GatewayESP32/GatewayESP32.ino b/examples/GatewayESP32/GatewayESP32.ino index dfc4f00bf..3fa69521a 100644 --- a/examples/GatewayESP32/GatewayESP32.ino +++ b/examples/GatewayESP32/GatewayESP32.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -41,8 +41,11 @@ #define MY_WIFI_SSID "MySSID" #define MY_WIFI_PASSWORD "MyVerySecretPassword" +// Enable UDP communication +//#define MY_USE_UDP // If using UDP you need to set MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS below + // Set the hostname for the WiFi Client. This is the hostname -// it will pass to the DHCP server if not static. +// passed to the DHCP server if not static. #define MY_HOSTNAME "ESP32_GW" // Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP) @@ -58,6 +61,11 @@ // How many clients should be able to connect to this gateway (default 1) #define MY_GATEWAY_MAX_CLIENTS 2 +// Controller ip address. Enables client mode (default is "server" mode). +// Also enable this if MY_USE_UDP is used and you want sensor data sent somewhere. +//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 +//#define MY_CONTROLLER_URL_ADDRESS "my.controller.org" + #include void setup() diff --git a/examples/GatewayESP32MQTTClient/GatewayESP32MQTTClient.ino b/examples/GatewayESP32MQTTClient/GatewayESP32MQTTClient.ino index a448e1abf..0aa1f8d94 100644 --- a/examples/GatewayESP32MQTTClient/GatewayESP32MQTTClient.ino +++ b/examples/GatewayESP32MQTTClient/GatewayESP32MQTTClient.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -55,7 +55,7 @@ #define MY_WIFI_PASSWORD "MyVerySecretPassword" // Set the hostname for the WiFi Client. This is the hostname -// it will pass to the DHCP server if not static. +// passed to the DHCP server if not static. #define MY_HOSTNAME "ESP32_MQTT_GW" // Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP) diff --git a/examples/GatewayESP32OTA/GatewayESP32OTA.ino b/examples/GatewayESP32OTA/GatewayESP32OTA.ino index 5944b7599..2610babac 100644 --- a/examples/GatewayESP32OTA/GatewayESP32OTA.ino +++ b/examples/GatewayESP32OTA/GatewayESP32OTA.ino @@ -6,7 +6,7 @@ network topology allowing messages to be routed to nodes. Created by Henrik Ekblad - Copyright (C) 2013-2018 Sensnology AB + Copyright (C) 2013-2019 Sensnology AB Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors Documentation: http://www.mysensors.org @@ -43,7 +43,7 @@ #define MY_WIFI_PASSWORD "MyVerySecretPassword" // Set the hostname for the WiFi Client. This is the hostname -// it will pass to the DHCP server if not static. +// passed to the DHCP server if not static. #define MY_HOSTNAME "ESP32_GW" // Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP) @@ -71,8 +71,6 @@ //#define MY_WITH_LEDS_BLINKING_INVERSE // At the time of Error, Receive, Transmit the pin is at a high level - - #include #include diff --git a/examples/GatewayESP8266/GatewayESP8266.ino b/examples/GatewayESP8266/GatewayESP8266.ino index 30c200861..9326400c9 100644 --- a/examples/GatewayESP8266/GatewayESP8266.ino +++ b/examples/GatewayESP8266/GatewayESP8266.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,7 +19,7 @@ ******************************* * * REVISION HISTORY - * Version 1.0 - Henrik EKblad + * Version 1.0 - Henrik Ekblad * Contribution by a-lurker and Anticimex, * Contribution by Norbert Truchsess * Contribution by Ivo Pullens (ESP8266 support) @@ -68,11 +68,11 @@ #define MY_WIFI_PASSWORD "MyVerySecretPassword" // Enable UDP communication -//#define MY_USE_UDP // If using UDP you need to set MY_CONTROLLER_IP_ADDRESS below +//#define MY_USE_UDP // If using UDP you need to set MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS below // Set the hostname for the WiFi Client. This is the hostname // it will pass to the DHCP server if not static. -//#define MY_HOSTNAME "sensor-gateway" +#define MY_HOSTNAME "ESP8266_GW" // Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP) //#define MY_IP_ADDRESS 192,168,178,87 @@ -90,6 +90,7 @@ // Controller ip address. Enables client mode (default is "server" mode). // Also enable this if MY_USE_UDP is used and you want sensor data sent somewhere. //#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 +//#define MY_CONTROLLER_URL_ADDRESS "my.controller.org" // Enable inclusion mode //#define MY_INCLUSION_MODE_FEATURE @@ -110,11 +111,6 @@ //#define MY_DEFAULT_RX_LED_PIN 16 // Receive led pin //#define MY_DEFAULT_TX_LED_PIN 16 // the PCB, on board LED -#if defined(MY_USE_UDP) -#include -#endif - -#include #include void setup() diff --git a/examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino b/examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino index a036c2106..b8051d554 100644 --- a/examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino +++ b/examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -74,8 +74,8 @@ #define MY_WIFI_PASSWORD "MyVerySecretPassword" // Set the hostname for the WiFi Client. This is the hostname -// it will pass to the DHCP server if not static. -// #define MY_HOSTNAME "mqtt-sensor-gateway" +// passed to the DHCP server if not static. +#define MY_HOSTNAME "ESP8266_MQTT_GW" // Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP) //#define MY_IP_ADDRESS 192,168,178,87 @@ -110,7 +110,6 @@ //#define MY_DEFAULT_RX_LED_PIN 16 // Receive led pin //#define MY_DEFAULT_TX_LED_PIN 16 // the PCB, on board LED -#include #include void setup() diff --git a/examples/GatewayESP8266OTA/GatewayESP8266OTA.ino b/examples/GatewayESP8266OTA/GatewayESP8266OTA.ino index 5d46594d5..56fa4a4a3 100644 --- a/examples/GatewayESP8266OTA/GatewayESP8266OTA.ino +++ b/examples/GatewayESP8266OTA/GatewayESP8266OTA.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,7 +19,7 @@ ******************************* * * REVISION HISTORY - * Version 1.0 - Henrik EKblad + * Version 1.0 - Henrik Ekblad * Contribution by tekka, * Contribution by a-lurker and Anticimex, * Contribution by Norbert Truchsess @@ -69,8 +69,8 @@ #define MY_WIFI_PASSWORD "MyVerySecretPassword" // Set the hostname for the WiFi Client. This is the hostname -// it will pass to the DHCP server if not static. -// #define MY_HOSTNAME "sensor-ota-gateway" +// passed to the DHCP server if not static. +#define MY_HOSTNAME "ESP8266_GW" // Enable UDP communication //#define MY_USE_UDP // If using UDP you need to set MY_CONTROLLER_IP_ADDRESS below @@ -96,7 +96,7 @@ #define MY_INCLUSION_MODE_FEATURE // Enable Inclusion mode button on gateway -// #define MY_INCLUSION_BUTTON_FEATURE +//#define MY_INCLUSION_BUTTON_FEATURE // Set inclusion mode duration (in seconds) #define MY_INCLUSION_MODE_DURATION 60 // Digital pin used for inclusion mode button @@ -111,12 +111,6 @@ #define MY_DEFAULT_RX_LED_PIN 16 // Receive led pin #define MY_DEFAULT_TX_LED_PIN 16 // the PCB, on board LED -#if defined(MY_USE_UDP) -#include -#else -#include -#endif - #include #include diff --git a/examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino b/examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino index 5bfd2cc27..37d62611e 100644 --- a/examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino +++ b/examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/GatewaySerial/GatewaySerial.ino b/examples/GatewaySerial/GatewaySerial.ino index c2c8bb435..9b543fa02 100644 --- a/examples/GatewaySerial/GatewaySerial.ino +++ b/examples/GatewaySerial/GatewaySerial.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/GatewaySerialRS485/GatewaySerialRS485.ino b/examples/GatewaySerialRS485/GatewaySerialRS485.ino index 381fe2ab2..bf97470bb 100644 --- a/examples/GatewaySerialRS485/GatewaySerialRS485.ino +++ b/examples/GatewaySerialRS485/GatewaySerialRS485.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/GatewayW5100/GatewayW5100.ino b/examples/GatewayW5100/GatewayW5100.ino index ded710db4..9c9097ae8 100644 --- a/examples/GatewayW5100/GatewayW5100.ino +++ b/examples/GatewayW5100/GatewayW5100.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,8 +19,8 @@ ******************************* * * REVISION HISTORY - * Version 1.0 - Henrik EKblad - * Contribution by a-lurker and Anticimex, + * Version 1.0 - Henrik Ekblad + * Contribution by a-lurker and Anticimex * Contribution by Norbert Truchsess * Contribution by Tomas Hozza * @@ -75,7 +75,7 @@ #endif // Enable UDP communication -//#define MY_USE_UDP // If using UDP you need to set MY_CONTROLLER_IP_ADDRESS below +//#define MY_USE_UDP // If using UDP you need to set MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS below // Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP) #define MY_IP_ADDRESS 192,168,178,66 @@ -93,12 +93,14 @@ // Controller ip address. Enables client mode (default is "server" mode). // Also enable this if MY_USE_UDP is used and you want sensor data sent somewhere. //#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254 +//#define MY_CONTROLLER_URL_ADDRESS "my.controller.org" // The MAC address can be anything you want but should be unique on your network. // Newer boards have a MAC address printed on the underside of the PCB, which you can (optionally) use. // Note that most of the Arduino examples use "DEAD BEEF FEED" for the MAC address. #define MY_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED + // Enable inclusion mode #define MY_INCLUSION_MODE_FEATURE // Enable Inclusion mode button on gateway diff --git a/examples/GatewayW5100MQTTClient/GatewayW5100MQTTClient.ino b/examples/GatewayW5100MQTTClient/GatewayW5100MQTTClient.ino index 8a179bcfb..9c5a6b2b9 100644 --- a/examples/GatewayW5100MQTTClient/GatewayW5100MQTTClient.ino +++ b/examples/GatewayW5100MQTTClient/GatewayW5100MQTTClient.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/LightSensor/LightSensor.ino b/examples/LightSensor/LightSensor.ino index 377a79c83..b27616c5b 100644 --- a/examples/LightSensor/LightSensor.ino +++ b/examples/LightSensor/LightSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/LogOTAGateway/LogOTAGateway.ino b/examples/LogOTAGateway/LogOTAGateway.ino index c8f51f5de..4c1eccc01 100644 --- a/examples/LogOTAGateway/LogOTAGateway.ino +++ b/examples/LogOTAGateway/LogOTAGateway.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/LogOTANode/LogOTANode.ino b/examples/LogOTANode/LogOTANode.ino index 96a3209b6..8fd0d1b84 100644 --- a/examples/LogOTANode/LogOTANode.ino +++ b/examples/LogOTANode/LogOTANode.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -33,8 +33,8 @@ // Allow sending logs without MY_DEBUG_OTA enabled #define MY_OTA_LOG_SENDER_FEATURE -// Disable ACK for debug messages -//#define MY_DEBUG_OTA_DISABLE_ACK +// Disable echoing of debug messages +//#define MY_DEBUG_OTA_DISABLE_ECHO // Enable and select radio type attached #define MY_RADIO_RF24 @@ -69,6 +69,6 @@ void loop() // A debug message DEBUG_OUTPUT(PSTR("DEBUG\nc=%" PRId16 "\nmillis=%" PRId32 "\n"), c, hwMillis()); - // Send a log message with ACK to a node + // Send a log message to a node, requesting that the message is echoed back to this node OTALog(0, true, PSTR("LOG\nc=%" PRId16 "\nmillis=%" PRId32 "\n"), c, hwMillis()); -} \ No newline at end of file +} diff --git a/examples/MockMySensors/MockMySensors.ino b/examples/MockMySensors/MockMySensors.ino index 3498cd2c6..687be8e8c 100644 --- a/examples/MockMySensors/MockMySensors.ino +++ b/examples/MockMySensors/MockMySensors.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -1254,12 +1254,12 @@ void custom() void receive(const MyMessage &message) { - switch (message.type) { + switch (message.getType()) { #ifdef ID_S_ARMED case V_ARMED: isArmed = message.getBool(); Serial.print("Incoming change for ID_S_ARMED:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println((isArmed ? "Armed":"Disarmed" )); #ifdef ID_S_DOOR @@ -1277,20 +1277,20 @@ void receive(const MyMessage &message) case V_STATUS: // V_LIGHT: #ifdef ID_S_LIGHT - if(message.sensor==ID_S_LIGHT) { + if(message.getSensor()==ID_S_LIGHT) { isLightOn = message.getBool(); Serial.print("Incoming change for ID_S_LIGHT:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println((isLightOn ? "On":"Off")); light(); // temp ack } #endif // #ifdef ID_S_HEATER - // if(message.sensor == ID_S_HEATER){ + // if(message.getSensor() == ID_S_HEATER){ // heater_status = message.getBool(); // Serial.print("Incoming change for ID_S_HEATER:"); - // Serial.print(message.sensor); + // Serial.print(message.getSensor()); // Serial.print(", New status: "); // Serial.println(heater_status); // heater();//temp ack @@ -1307,7 +1307,7 @@ void receive(const MyMessage &message) } dimmerVal= message.getInt(); Serial.print("Incoming change for ID_S_DIMMER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(message.getInt()); dimmer();// temp ack @@ -1318,7 +1318,7 @@ void receive(const MyMessage &message) case V_UP: coverState=1; Serial.print("Incoming change for ID_S_COVER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println("V_UP"); cover(); // temp ack @@ -1327,7 +1327,7 @@ void receive(const MyMessage &message) case V_DOWN: coverState=-1; Serial.print("Incoming change for ID_S_COVER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println("V_DOWN"); cover(); //temp ack @@ -1336,7 +1336,7 @@ void receive(const MyMessage &message) case V_STOP: coverState=0; Serial.print("Incoming change for ID_S_COVER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println("V_STOP"); cover(); //temp ack @@ -1347,11 +1347,11 @@ void receive(const MyMessage &message) case V_HVAC_SETPOINT_HEAT: #ifdef ID_S_HEATER - if(message.sensor == ID_S_HEATER) { + if(message.getSensor() == ID_S_HEATER) { heater_setpoint=message.getFloat(); Serial.print("Incoming set point for ID_S_HEATER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(heater_setpoint,1); heater();//temp ack @@ -1359,10 +1359,10 @@ void receive(const MyMessage &message) #endif #ifdef ID_S_HVAC - if(message.sensor == ID_S_HVAC) { + if(message.getSensor() == ID_S_HVAC) { hvac_SetPointHeat=message.getFloat(); Serial.print("Incoming set point for ID_S_HVAC:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(hvac_SetPointHeat,1); hvac();//temp ack @@ -1372,10 +1372,10 @@ void receive(const MyMessage &message) case V_HVAC_FLOW_STATE: #ifdef ID_S_HEATER - if(message.sensor == ID_S_HEATER) { + if(message.getSensor() == ID_S_HEATER) { heater_flow_state=message.getString(); Serial.print("Incoming flow state change for ID_S_HEATER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(heater_flow_state); heater();//temp ack @@ -1383,11 +1383,11 @@ void receive(const MyMessage &message) #endif #ifdef ID_S_HVAC - if(message.sensor == ID_S_HVAC) { + if(message.getSensor() == ID_S_HVAC) { hvac_FlowState=message.getString(); Serial.print("Incoming set point for ID_S_HVAC:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(hvac_FlowState); hvac();//temp ack @@ -1399,7 +1399,7 @@ void receive(const MyMessage &message) case V_LOCK_STATUS: isLocked = message.getBool(); Serial.print("Incoming change for ID_S_LOCK:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(message.getBool()?"Locked":"Unlocked"); lock(); //temp ack @@ -1410,7 +1410,7 @@ void receive(const MyMessage &message) case V_IR_SEND: irVal = message.getLong(); Serial.print("Incoming change for ID_S_IR:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(irVal); ir(); // temp ack @@ -1418,7 +1418,7 @@ void receive(const MyMessage &message) case V_IR_RECEIVE: irVal = message.getLong(); Serial.print("Incoming change for ID_S_IR:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(irVal); ir(); // temp ack @@ -1429,7 +1429,7 @@ void receive(const MyMessage &message) case V_SCENE_ON: sceneVal = message.getInt(); Serial.print("Incoming change for ID_S_SCENE_CONTROLLER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.print(scenes[sceneVal]); Serial.println(" On"); @@ -1438,7 +1438,7 @@ void receive(const MyMessage &message) case V_SCENE_OFF: sceneVal = message.getInt(); Serial.print("Incoming change for ID_S_SCENE_CONTROLLER:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.print(scenes[sceneVal]); Serial.println(" Off"); @@ -1450,7 +1450,7 @@ void receive(const MyMessage &message) case V_RGB: rgbState=message.getString(); Serial.print("Incoming flow state change for ID_S_RGB_LIGHT:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(rgbState); rgbLight(); // temp ack @@ -1462,7 +1462,7 @@ void receive(const MyMessage &message) case V_RGBW: rgbwState=message.getString(); Serial.print("Incoming flow state change for ID_S_RGBW_LIGHT:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(rgbwState); rgbwLight(); @@ -1480,7 +1480,7 @@ void receive(const MyMessage &message) hvac_SetPointCool=message.getFloat(); Serial.print("Incoming set point for ID_S_HVAC:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(hvac_SetPointCool,1); hvac();//temp ack @@ -1490,7 +1490,7 @@ void receive(const MyMessage &message) hvac_Speed=message.getString(); Serial.print("Incoming set point for ID_S_HVAC:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(hvac_Speed); hvac();//temp ack @@ -1500,7 +1500,7 @@ void receive(const MyMessage &message) hvac_FlowMode=message.getString(); Serial.print("Incoming set point for ID_S_HVAC:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(hvac_FlowMode); hvac();//temp ack @@ -1509,7 +1509,7 @@ void receive(const MyMessage &message) default: Serial.print("Unknown/Unimplemented message type: "); - Serial.println(message.type); + Serial.println(message.getType()); } } diff --git a/examples/MotionSensor/MotionSensor.ino b/examples/MotionSensor/MotionSensor.ino index b4ee0a05f..502215a55 100644 --- a/examples/MotionSensor/MotionSensor.ino +++ b/examples/MotionSensor/MotionSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/MotionSensorRS485/MotionSensorRS485.ino b/examples/MotionSensorRS485/MotionSensorRS485.ino index 4e9790d62..96f0a18c6 100644 --- a/examples/MotionSensorRS485/MotionSensorRS485.ino +++ b/examples/MotionSensorRS485/MotionSensorRS485.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/Node2Node/Node2Node.ino b/examples/Node2Node/Node2Node.ino index 173816488..5efd17a54 100644 --- a/examples/Node2Node/Node2Node.ino +++ b/examples/Node2Node/Node2Node.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/PHSensor/PHSensor.ino b/examples/PHSensor/PHSensor.ino index 21b02d1ff..d6a51493f 100644 --- a/examples/PHSensor/PHSensor.ino +++ b/examples/PHSensor/PHSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/PassiveNode/PassiveNode.ino b/examples/PassiveNode/PassiveNode.ino index 8910902c7..74d5bcc71 100644 --- a/examples/PassiveNode/PassiveNode.ino +++ b/examples/PassiveNode/PassiveNode.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/PingPongSensor/MYSLog.h b/examples/PingPongSensor/MYSLog.h index e90f3e556..25c20543f 100644 --- a/examples/PingPongSensor/MYSLog.h +++ b/examples/PingPongSensor/MYSLog.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -32,12 +32,12 @@ #define LOGDEBUG 1 #if defined ( LOGDEBUG ) -#define LOG(fmt, args... ) log( fmt, ## args ); +#define LOG(fmt, args... ) debugLog( fmt, ## args ); #else -#define log(fmt, args... ) +#define LOG(fmt, args... ) #endif -void log(const char *fmt, ... ) +void debugLog(const char *fmt, ... ) { char buff[128]; va_list args; @@ -48,7 +48,7 @@ void log(const char *fmt, ... ) Serial.print(buff); } -void log(const __FlashStringHelper *fmt, ... ) +void debugLog(const __FlashStringHelper *fmt, ... ) { char buf[128]; // resulting string limited to 128 chars va_list args; diff --git a/examples/PingPongSensor/PingPongSensor.ino b/examples/PingPongSensor/PingPongSensor.ino index d29f755a8..896df628e 100644 --- a/examples/PingPongSensor/PingPongSensor.ino +++ b/examples/PingPongSensor/PingPongSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -89,7 +89,7 @@ void loop() void receive(const MyMessage &message) { - LOG(F("Received %s from %s\n"), msgTypeAsCharRepresentation((mysensors_data_t)message.type), + LOG(F("Received %s from %s\n"), msgTypeAsCharRepresentation((mysensors_data_t)message.getType()), nodeTypeAsCharRepresentation(message.sender)); delay(250); @@ -99,9 +99,9 @@ void receive(const MyMessage &message) void sendPingOrPongResponse( MyMessage msg ) { - MyMessage response = (msg.type == V_VAR1 ? mPong : mPing); + MyMessage response = (msg.getType() == V_VAR1 ? mPong : mPing); - LOG(F("Sending %s to %s\n"), msgTypeAsCharRepresentation( (mysensors_data_t)response.type ), + LOG(F("Sending %s to %s\n"), msgTypeAsCharRepresentation( (mysensors_data_t)response.getType() ), nodeTypeAsCharRepresentation(msg.sender)); // Set payload to current time in millis to ensure each message is unique diff --git a/examples/RFM69_RFM95_ATC_SignalReport/RFM69_RFM95_ATC_SignalReport.ino b/examples/RFM69_RFM95_ATC_SignalReport/RFM69_RFM95_ATC_SignalReport.ino index 08ffd5e3e..34d18b65f 100644 --- a/examples/RFM69_RFM95_ATC_SignalReport/RFM69_RFM95_ATC_SignalReport.ino +++ b/examples/RFM69_RFM95_ATC_SignalReport/RFM69_RFM95_ATC_SignalReport.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/RelayActuator/RelayActuator.ino b/examples/RelayActuator/RelayActuator.ino index ad57b82b8..998ee86b4 100644 --- a/examples/RelayActuator/RelayActuator.ino +++ b/examples/RelayActuator/RelayActuator.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -82,14 +82,14 @@ void loop() void receive(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. - if (message.type==V_STATUS) { + if (message.getType()==V_STATUS) { // Change relay state - digitalWrite(message.sensor-1+RELAY_PIN, message.getBool()?RELAY_ON:RELAY_OFF); + digitalWrite(message.getSensor()-1+RELAY_PIN, message.getBool()?RELAY_ON:RELAY_OFF); // Store state in eeprom - saveState(message.sensor, message.getBool()); + saveState(message.getSensor(), message.getBool()); // Write some debug info Serial.print("Incoming change for sensor:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(message.getBool()); } diff --git a/examples/RepeaterNode/RepeaterNode.ino b/examples/RepeaterNode/RepeaterNode.ino index 2829c8c54..95a1abe5a 100644 --- a/examples/RepeaterNode/RepeaterNode.ino +++ b/examples/RepeaterNode/RepeaterNode.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/SecretKnockSensor/SecretKnockSensor.ino b/examples/SecretKnockSensor/SecretKnockSensor.ino index ffe641e64..5a7e60540 100644 --- a/examples/SecretKnockSensor/SecretKnockSensor.ino +++ b/examples/SecretKnockSensor/SecretKnockSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -181,8 +181,8 @@ void listenToSecretKnock() } int currentKnockNumber = 0; // Position counter for the array. - int startTime = millis(); // Reference for when this knock started. - int now = millis(); + uint32_t startTime = millis(); // Reference for when this knock started. + uint32_t now; do { // Listen for the next knock or wait for it to timeout. knockSensorValue = digitalRead(knockSensor); @@ -397,7 +397,7 @@ void chirp(int playTime, int delayTime) void receive(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. - if (message.type==V_LOCK_STATUS) { + if (message.getType()==V_LOCK_STATUS) { // Change relay state setLockState(message.getBool(), false); diff --git a/examples/SecureActuator/SecureActuator.ino b/examples/SecureActuator/SecureActuator.ino index a8932f8f4..c8ebf3eeb 100644 --- a/examples/SecureActuator/SecureActuator.ino +++ b/examples/SecureActuator/SecureActuator.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -58,7 +58,7 @@ // Enable node whitelisting //#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} // Enable this if you want destination node to sign all messages sent to this node. -#define MY_SIGNING_REQUEST_SIGNATURES +#define MY_SIGNING_REQUEST_SIGNATURES //!< destination node signs all messages sent to this node // SETTINGS FOR MY_SIGNING_SOFT @@ -112,15 +112,15 @@ void loop() void receive(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. - // And acks are not accepted as control messages - if (message.type==V_LOCK_STATUS && message.sensor<=NOF_LOCKS && !mGetAck(message)) { + // And echoed messages are not accepted as control messages + if (message.getType()==V_LOCK_STATUS && message.getSensor()<=NOF_LOCKS && !message.isEcho()) { // Change relay state - digitalWrite(message.sensor-1+LOCK_1, message.getBool()?LOCK_LOCK:LOCK_UNLOCK); + digitalWrite(message.getSensor()-1+LOCK_1, message.getBool()?LOCK_LOCK:LOCK_UNLOCK); // Store state in eeprom - saveState(message.sensor, message.getBool()); + saveState(message.getSensor(), message.getBool()); // Write some debug info Serial.print("Incoming change for lock:"); - Serial.print(message.sensor); + Serial.print(message.getSensor()); Serial.print(", New status: "); Serial.println(message.getBool()); } diff --git a/examples/SecurityPersonalizer/SecurityPersonalizer.ino b/examples/SecurityPersonalizer/SecurityPersonalizer.ino index e456ef286..999dd8af7 100644 --- a/examples/SecurityPersonalizer/SecurityPersonalizer.ino +++ b/examples/SecurityPersonalizer/SecurityPersonalizer.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -414,7 +414,7 @@ static uint8_t user_aes_key[16] = {MY_AES_KEY}; #ifndef USE_SOFT_SIGNING const int sha204Pin = MY_SIGNING_ATSHA204_PIN; //!< The IO pin to use for ATSHA204A -atsha204Class sha204(sha204Pin); +atsha204Class sha204(sha204Pin); //!< atsha204Class static uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; static uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; static uint8_t ret_code; @@ -1868,3 +1868,5 @@ static void dump_detailed_atsha204a_configuration(void) #define PRINT_DETAILED_ATSHA204A_CONFIG #define RESET_EEPROM_PERSONALIZATION #endif + +/** @}*/ \ No newline at end of file diff --git a/examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino b/examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino index e5bed8dd8..9514569eb 100644 --- a/examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino +++ b/examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/SoilMoistSensor/SoilMoistSensor.ino b/examples/SoilMoistSensor/SoilMoistSensor.ino index a0f403993..5f5698dfd 100644 --- a/examples/SoilMoistSensor/SoilMoistSensor.ino +++ b/examples/SoilMoistSensor/SoilMoistSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -191,8 +191,8 @@ void addReading(long resistance) long average() { long sum = 0; - for (int i = 0; i < NUM_READS; i++) { - sum += buffer[i]; + for (int cnt = 0; cnt < NUM_READS; cnt++) { + sum += buffer[cnt]; } return (long)(sum / NUM_READS); } diff --git a/examples/UVSensor/UVSensor.ino b/examples/UVSensor/UVSensor.ino index 9720c741c..730311595 100644 --- a/examples/UVSensor/UVSensor.ino +++ b/examples/UVSensor/UVSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/VibrationSensor/VibrationSensor.ino b/examples/VibrationSensor/VibrationSensor.ino index b0d20c207..24367dfe9 100644 --- a/examples/VibrationSensor/VibrationSensor.ino +++ b/examples/VibrationSensor/VibrationSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino b/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino index 6a18eefb7..c80a4e8cd 100644 --- a/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino +++ b/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -69,13 +69,36 @@ volatile uint32_t lastBlink = 0; volatile double flow = 0; bool pcReceived = false; uint32_t oldPulseCount = 0; -uint32_t newBlink = 0; double oldflow = 0; -double volume =0; double oldvolume =0; uint32_t lastSend =0; uint32_t lastPulse =0; +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR +#else +#define IRQ_HANDLER_ATTR +#endif + +void IRQ_HANDLER_ATTR onPulse() +{ + if (!SLEEP_MODE) { + uint32_t newBlink = micros(); + uint32_t interval = newBlink-lastBlink; + + if (interval!=0) { + lastPulse = millis(); + if (interval<500000L) { + // Sometimes we get interrupt on RISING, 500000 = 0.5 second debounce ( max 120 l/min) + return; + } + flow = (60000000.0 /interval) / ppl; + } + lastBlink = newBlink; + } + pulseCount++; +} + void setup() { // initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion) @@ -153,13 +176,13 @@ void loop() } } if (SLEEP_MODE) { - sleep(SEND_FREQUENCY); + sleep(SEND_FREQUENCY, false); } } void receive(const MyMessage &message) { - if (message.type==V_VAR1) { + if (message.getType()==V_VAR1) { uint32_t gwPulseCount=message.getULong(); pulseCount += gwPulseCount; flow=oldflow=0; @@ -169,21 +192,3 @@ void receive(const MyMessage &message) } } -void onPulse() -{ - if (!SLEEP_MODE) { - uint32_t newBlink = micros(); - uint32_t interval = newBlink-lastBlink; - - if (interval!=0) { - lastPulse = millis(); - if (interval<500000L) { - // Sometimes we get interrupt on RISING, 500000 = 0.5 second debounce ( max 120 l/min) - return; - } - flow = (60000000.0 /interval) / ppl; - } - lastBlink = newBlink; - } - pulseCount++; -} diff --git a/examples_linux/mysgw.cpp b/examples_linux/mysgw.cpp index cd59dcbcb..e0c5e45c0 100644 --- a/examples_linux/mysgw.cpp +++ b/examples_linux/mysgw.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/architecture/AVR/MyHwAVR.cpp b/hal/architecture/AVR/MyHwAVR.cpp index f30f71e28..e14adfb0e 100644 --- a/hal/architecture/AVR/MyHwAVR.cpp +++ b/hal/architecture/AVR/MyHwAVR.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ */ #include "MyHwAVR.h" -#include "avr/boot.h" - bool hwInit(void) { @@ -33,7 +31,6 @@ bool hwInit(void) } #define WDTO_SLEEP_FOREVER (0xFFu) -#define INVALID_INTERRUPT_NUM (0xFFu) volatile uint8_t _wokeUpByInterrupt = INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu. @@ -42,6 +39,8 @@ volatile uint8_t _wakeUp1Interrupt = volatile uint8_t _wakeUp2Interrupt = INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback. +static uint32_t sleepRemainingMs = 0ul; + void wakeUp1(void) { // Disable sleep. When an interrupt occurs after attachInterrupt, @@ -123,7 +122,7 @@ void hwPowerDown(const uint8_t wdto) ADCSRA |= (1 << ADEN); } -void hwInternalSleep(uint32_t ms) +uint32_t hwInternalSleep(uint32_t ms) { // Sleeping with watchdog only supports multiples of 16ms. // Round up to next multiple of 16ms, to assure we sleep at least the @@ -140,6 +139,10 @@ void hwInternalSleep(uint32_t ms) } } } + if (interruptWakeUp()) { + return ms; + } + return 0ul; } int8_t hwSleep(uint32_t ms) @@ -147,9 +150,10 @@ int8_t hwSleep(uint32_t ms) // Return what woke the mcu. // Default: no interrupt triggered, timer wake up int8_t ret = MY_WAKE_UP_BY_TIMER; + sleepRemainingMs = 0ul; if (ms > 0u) { // sleep for defined time - hwInternalSleep(ms); + sleepRemainingMs = hwInternalSleep(ms); } else { // sleep until ext interrupt triggered hwPowerDown(WDTO_SLEEP_FOREVER); @@ -195,9 +199,10 @@ int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t inte attachInterrupt(interrupt2, wakeUp2, mode2); } + sleepRemainingMs = 0ul; if (ms > 0u) { // sleep for defined time - hwInternalSleep(ms); + sleepRemainingMs = hwInternalSleep(ms); } else { // sleep until ext interrupt triggered hwPowerDown(WDTO_SLEEP_FOREVER); @@ -223,6 +228,11 @@ int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t inte return ret; } +uint32_t hwGetSleepRemaining(void) +{ + return sleepRemainingMs; +} + inline void hwRandomNumberInit(void) { // This function initializes the random number generator with a seed @@ -261,10 +271,10 @@ bool hwUniqueID(unique_id_t *uniqueID) // padding (void)memset(uniqueID, MY_HWID_PADDING_BYTE, sizeof(unique_id_t)); // no unique ID for non-PB AVR, use HW specifics for diversification - *((uint8_t *)uniqueID) = boot_signature_byte_get(0x00); - *((uint8_t *)uniqueID + 1) = boot_signature_byte_get(0x02); - *((uint8_t *)uniqueID + 2) = boot_signature_byte_get(0x04); - *((uint8_t *)uniqueID + 3) = boot_signature_byte_get(0x01); //OSCCAL + *((uint8_t *)uniqueID) = SIGNATURE_2; + *((uint8_t *)uniqueID + 1) = SIGNATURE_1; + *((uint8_t *)uniqueID + 2) = SIGNATURE_0; + *((uint8_t *)uniqueID + 3) = OSCCAL; #if defined(__AVR_ATmega328PB__) // ATMEGA328PB specifics, has unique ID for(uint8_t idx = 0; idx < 10; idx++) { @@ -303,6 +313,7 @@ uint16_t hwCPUFrequency(void) // save WDT & timer settings const uint8_t WDTsave = WDTCSR; const uint8_t TCCR1Asave = TCCR1A; + const uint8_t TCCR1Bsave = TCCR1B; const uint8_t TCCR1Csave = TCCR1C; // setup timer1 TIFR1 = 0xFF; @@ -325,11 +336,13 @@ uint16_t hwCPUFrequency(void) WDTCSR |= (1 << WDCE) | (1 << WDE); WDTCSR = WDTsave; sei(); + const uint16_t result = TCNT1 * 2048UL / 100000UL; // restore timer settings TCCR1A = TCCR1Asave; + TCCR1B = TCCR1Bsave; TCCR1C = TCCR1Csave; // return frequency in 1/10MHz (accuracy +- 10%) - return TCNT1 * 2048UL / 100000UL; + return result; } int8_t hwCPUTemperature(void) @@ -355,33 +368,3 @@ uint16_t hwFreeMem(void) int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); } - -void hwDebugPrint(const char *fmt, ... ) -{ -#ifndef MY_DISABLED_SERIAL - char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; -#ifdef MY_GATEWAY_SERIAL - // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) - snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), - C_INTERNAL, I_LOG_MESSAGE, hwMillis()); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - // prepend timestamp - MY_DEBUGDEVICE.print(hwMillis()); - MY_DEBUGDEVICE.print(F(" ")); -#endif - va_list args; - va_start(args, fmt); - vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); -#ifdef MY_GATEWAY_SERIAL - // Truncate message if this is gateway node - fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; - fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; -#endif - va_end(args); - MY_DEBUGDEVICE.print(fmtBuffer); - MY_DEBUGDEVICE.flush(); -#else - (void)fmt; -#endif -} diff --git a/hal/architecture/AVR/MyHwAVR.h b/hal/architecture/AVR/MyHwAVR.h index 14e340e27..52c0d8d89 100644 --- a/hal/architecture/AVR/MyHwAVR.h +++ b/hal/architecture/AVR/MyHwAVR.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -26,8 +26,17 @@ #include #include #include +#include #include +#include +// Fast IO driver +#include "drivers/DigitalWriteFast/digitalWriteFast.h" + +// SOFTSPI +#ifdef MY_SOFTSPI +#include "hal/architecture/AVR/drivers/DigitalIO/DigitalIO.h" +#endif #ifdef __cplusplus #include @@ -68,7 +77,13 @@ bool hwInit(void); #define hwWriteConfigBlock(__buf, __pos, __length) eeprom_update_block((const void *)__buf, (void *)__pos, (uint32_t)__length) inline void hwRandomNumberInit(void); -void hwInternalSleep(uint32_t ms); +uint32_t hwInternalSleep(uint32_t ms); + +#if defined(MY_SOFTSPI) +SoftSPI hwSPI; //!< hwSPI +#else +#define hwSPI SPI //!< hwSPI +#endif #ifndef DOXYGEN #define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE) diff --git a/hal/architecture/AVR/MyMainAVR.cpp b/hal/architecture/AVR/MyMainAVR.cpp index d2792d1a4..dd02675f6 100644 --- a/hal/architecture/AVR/MyMainAVR.cpp +++ b/hal/architecture/AVR/MyMainAVR.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/architecture/AVR/drivers/DigitalIO/DigitalIO.h b/hal/architecture/AVR/drivers/DigitalIO/DigitalIO.h index f984ef8c3..4b4ca77e7 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/DigitalIO.h +++ b/hal/architecture/AVR/drivers/DigitalIO/DigitalIO.h @@ -26,11 +26,11 @@ #define DigitalIO_h 1 //------------------------------------------------------------------------------ /** DigitalPin version YYYYMMDD */ -#define DIGITAL_IO_VERSION 20140217 +#define DIGITAL_IO_VERSION 20151127 //------------------------------------------------------------------------------ #include "DigitalPin.h" #include "I2cConstants.h" #include "PinIO.h" #include "SoftI2cMaster.h" #include "SoftSPI.h" -#endif // DigitalIO_h +#endif // DigitalIO_h diff --git a/hal/architecture/AVR/drivers/DigitalIO/DigitalPin.h b/hal/architecture/AVR/drivers/DigitalIO/DigitalPin.h index b59897fd7..cf5467528 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/DigitalPin.h +++ b/hal/architecture/AVR/drivers/DigitalIO/DigitalPin.h @@ -22,379 +22,27 @@ * @brief Fast Digital Pin functions * * @defgroup digitalPin Fast Pin I/O - * @ingroup internals * @details Fast Digital I/O functions and template class. * @{ */ #ifndef DigitalPin_h #define DigitalPin_h +#if defined(__AVR__) || defined(DOXYGEN) #include -#include -#include -/** @brief Helper macro for complex inline attributes */ -#define ALWAYS_INLINE inline __attribute__((always_inline)) -//------------------------------------------------------------------------------ -/** - * @class pin_map_t - * @brief struct for mapping digital pins - */ -struct pin_map_t { - volatile uint8_t* ddr; /**< address of DDR for this pin */ +/** GpioPinMap type */ +struct GpioPinMap_t { volatile uint8_t* pin; /**< address of PIN for this pin */ + volatile uint8_t* ddr; /**< address of DDR for this pin */ volatile uint8_t* port; /**< address of PORT for this pin */ - uint8_t bit; /**< bit number for this pin */ -}; -//------------------------------------------------------------------------------ -#if defined(__AVR_ATmega168__)\ -||defined(__AVR_ATmega168P__)\ -||defined(__AVR_ATmega328P__) -// 168 and 328 Arduinos -const static pin_map_t pinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 0 - {&DDRD, &PIND, &PORTD, 1}, // D1 1 - {&DDRD, &PIND, &PORTD, 2}, // D2 2 - {&DDRD, &PIND, &PORTD, 3}, // D3 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRD, &PIND, &PORTD, 5}, // D5 5 - {&DDRD, &PIND, &PORTD, 6}, // D6 6 - {&DDRD, &PIND, &PORTD, 7}, // D7 7 - {&DDRB, &PINB, &PORTB, 0}, // B0 8 - {&DDRB, &PINB, &PORTB, 1}, // B1 9 - {&DDRB, &PINB, &PORTB, 2}, // B2 10 - {&DDRB, &PINB, &PORTB, 3}, // B3 11 - {&DDRB, &PINB, &PORTB, 4}, // B4 12 - {&DDRB, &PINB, &PORTB, 5}, // B5 13 - {&DDRC, &PINC, &PORTC, 0}, // C0 14 - {&DDRC, &PINC, &PORTC, 1}, // C1 15 - {&DDRC, &PINC, &PORTC, 2}, // C2 16 - {&DDRC, &PINC, &PORTC, 3}, // C3 17 - {&DDRC, &PINC, &PORTC, 4}, // C4 18 - {&DDRC, &PINC, &PORTC, 5} // C5 19 -}; -//------------------------------------------------------------------------------ -#elif defined(__AVR_ATmega1280__)\ -|| defined(__AVR_ATmega2560__) -// Mega -static const pin_map_t pinMap[] = { - {&DDRE, &PINE, &PORTE, 0}, // E0 0 - {&DDRE, &PINE, &PORTE, 1}, // E1 1 - {&DDRE, &PINE, &PORTE, 4}, // E4 2 - {&DDRE, &PINE, &PORTE, 5}, // E5 3 - {&DDRG, &PING, &PORTG, 5}, // G5 4 - {&DDRE, &PINE, &PORTE, 3}, // E3 5 - {&DDRH, &PINH, &PORTH, 3}, // H3 6 - {&DDRH, &PINH, &PORTH, 4}, // H4 7 - {&DDRH, &PINH, &PORTH, 5}, // H5 8 - {&DDRH, &PINH, &PORTH, 6}, // H6 9 - {&DDRB, &PINB, &PORTB, 4}, // B4 10 - {&DDRB, &PINB, &PORTB, 5}, // B5 11 - {&DDRB, &PINB, &PORTB, 6}, // B6 12 - {&DDRB, &PINB, &PORTB, 7}, // B7 13 - {&DDRJ, &PINJ, &PORTJ, 1}, // J1 14 - {&DDRJ, &PINJ, &PORTJ, 0}, // J0 15 - {&DDRH, &PINH, &PORTH, 1}, // H1 16 - {&DDRH, &PINH, &PORTH, 0}, // H0 17 - {&DDRD, &PIND, &PORTD, 3}, // D3 18 - {&DDRD, &PIND, &PORTD, 2}, // D2 19 - {&DDRD, &PIND, &PORTD, 1}, // D1 20 - {&DDRD, &PIND, &PORTD, 0}, // D0 21 - {&DDRA, &PINA, &PORTA, 0}, // A0 22 - {&DDRA, &PINA, &PORTA, 1}, // A1 23 - {&DDRA, &PINA, &PORTA, 2}, // A2 24 - {&DDRA, &PINA, &PORTA, 3}, // A3 25 - {&DDRA, &PINA, &PORTA, 4}, // A4 26 - {&DDRA, &PINA, &PORTA, 5}, // A5 27 - {&DDRA, &PINA, &PORTA, 6}, // A6 28 - {&DDRA, &PINA, &PORTA, 7}, // A7 29 - {&DDRC, &PINC, &PORTC, 7}, // C7 30 - {&DDRC, &PINC, &PORTC, 6}, // C6 31 - {&DDRC, &PINC, &PORTC, 5}, // C5 32 - {&DDRC, &PINC, &PORTC, 4}, // C4 33 - {&DDRC, &PINC, &PORTC, 3}, // C3 34 - {&DDRC, &PINC, &PORTC, 2}, // C2 35 - {&DDRC, &PINC, &PORTC, 1}, // C1 36 - {&DDRC, &PINC, &PORTC, 0}, // C0 37 - {&DDRD, &PIND, &PORTD, 7}, // D7 38 - {&DDRG, &PING, &PORTG, 2}, // G2 39 - {&DDRG, &PING, &PORTG, 1}, // G1 40 - {&DDRG, &PING, &PORTG, 0}, // G0 41 - {&DDRL, &PINL, &PORTL, 7}, // L7 42 - {&DDRL, &PINL, &PORTL, 6}, // L6 43 - {&DDRL, &PINL, &PORTL, 5}, // L5 44 - {&DDRL, &PINL, &PORTL, 4}, // L4 45 - {&DDRL, &PINL, &PORTL, 3}, // L3 46 - {&DDRL, &PINL, &PORTL, 2}, // L2 47 - {&DDRL, &PINL, &PORTL, 1}, // L1 48 - {&DDRL, &PINL, &PORTL, 0}, // L0 49 - {&DDRB, &PINB, &PORTB, 3}, // B3 50 - {&DDRB, &PINB, &PORTB, 2}, // B2 51 - {&DDRB, &PINB, &PORTB, 1}, // B1 52 - {&DDRB, &PINB, &PORTB, 0}, // B0 53 - {&DDRF, &PINF, &PORTF, 0}, // F0 54 - {&DDRF, &PINF, &PORTF, 1}, // F1 55 - {&DDRF, &PINF, &PORTF, 2}, // F2 56 - {&DDRF, &PINF, &PORTF, 3}, // F3 57 - {&DDRF, &PINF, &PORTF, 4}, // F4 58 - {&DDRF, &PINF, &PORTF, 5}, // F5 59 - {&DDRF, &PINF, &PORTF, 6}, // F6 60 - {&DDRF, &PINF, &PORTF, 7}, // F7 61 - {&DDRK, &PINK, &PORTK, 0}, // K0 62 - {&DDRK, &PINK, &PORTK, 1}, // K1 63 - {&DDRK, &PINK, &PORTK, 2}, // K2 64 - {&DDRK, &PINK, &PORTK, 3}, // K3 65 - {&DDRK, &PINK, &PORTK, 4}, // K4 66 - {&DDRK, &PINK, &PORTK, 5}, // K5 67 - {&DDRK, &PINK, &PORTK, 6}, // K6 68 - {&DDRK, &PINK, &PORTK, 7} // K7 69 + uint8_t mask; /**< bit mask for this pin */ }; -//------------------------------------------------------------------------------ -#elif defined(__AVR_ATmega1284P__)\ -|| defined(__AVR_ATmega1284__)\ -|| defined(__AVR_ATmega644P__)\ -|| defined(__AVR_ATmega644__)\ -|| defined(__AVR_ATmega64__)\ -|| defined(__AVR_ATmega32__)\ -|| defined(__AVR_ATmega324__)\ -|| defined(__AVR_ATmega16__) -#ifdef defined(VARIANT_MIGHTY) -// Mighty Layout -static const pin_map_t pinMap[] = { - {&DDRB, &PINB, &PORTB, 0}, // B0 0 - {&DDRB, &PINB, &PORTB, 1}, // B1 1 - {&DDRB, &PINB, &PORTB, 2}, // B2 2 - {&DDRB, &PINB, &PORTB, 3}, // B3 3 - {&DDRB, &PINB, &PORTB, 4}, // B4 4 - {&DDRB, &PINB, &PORTB, 5}, // B5 5 - {&DDRB, &PINB, &PORTB, 6}, // B6 6 - {&DDRB, &PINB, &PORTB, 7}, // B7 7 - {&DDRD, &PIND, &PORTD, 0}, // D0 8 - {&DDRD, &PIND, &PORTD, 1}, // D1 9 - {&DDRD, &PIND, &PORTD, 2}, // D2 10 - {&DDRD, &PIND, &PORTD, 3}, // D3 11 - {&DDRD, &PIND, &PORTD, 4}, // D4 12 - {&DDRD, &PIND, &PORTD, 5}, // D5 13 - {&DDRD, &PIND, &PORTD, 6}, // D6 14 - {&DDRD, &PIND, &PORTD, 7}, // D7 15 - {&DDRC, &PINC, &PORTC, 0}, // C0 16 - {&DDRC, &PINC, &PORTC, 1}, // C1 17 - {&DDRC, &PINC, &PORTC, 2}, // C2 18 - {&DDRC, &PINC, &PORTC, 3}, // C3 19 - {&DDRC, &PINC, &PORTC, 4}, // C4 20 - {&DDRC, &PINC, &PORTC, 5}, // C5 21 - {&DDRC, &PINC, &PORTC, 6}, // C6 22 - {&DDRC, &PINC, &PORTC, 7}, // C7 23 - {&DDRA, &PINA, &PORTA, 0}, // A0 24 - {&DDRA, &PINA, &PORTA, 1}, // A1 25 - {&DDRA, &PINA, &PORTA, 2}, // A2 26 - {&DDRA, &PINA, &PORTA, 3}, // A3 27 - {&DDRA, &PINA, &PORTA, 4}, // A4 28 - {&DDRA, &PINA, &PORTA, 5}, // A5 29 - {&DDRA, &PINA, &PORTA, 6}, // A6 30 - {&DDRA, &PINA, &PORTA, 7} // A7 31 -}; -#elif defined(VARIANT_BOBUINO) -// Bobuino Layout -static const pin_map_t pinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 0 - {&DDRD, &PIND, &PORTD, 1}, // D1 1 - {&DDRD, &PIND, &PORTD, 2}, // D2 2 - {&DDRD, &PIND, &PORTD, 3}, // D3 3 - {&DDRB, &PINB, &PORTB, 0}, // B0 4 - {&DDRB, &PINB, &PORTB, 1}, // B1 5 - {&DDRB, &PINB, &PORTB, 2}, // B2 6 - {&DDRB, &PINB, &PORTB, 3}, // B3 7 - {&DDRD, &PIND, &PORTD, 5}, // D5 8 - {&DDRD, &PIND, &PORTD, 6}, // D6 9 - {&DDRB, &PINB, &PORTB, 4}, // B4 10 - {&DDRB, &PINB, &PORTB, 5}, // B5 11 - {&DDRB, &PINB, &PORTB, 6}, // B6 12 - {&DDRB, &PINB, &PORTB, 7}, // B7 13 - {&DDRA, &PINA, &PORTA, 7}, // A7 14 - {&DDRA, &PINA, &PORTA, 6}, // A6 15 - {&DDRA, &PINA, &PORTA, 5}, // A5 16 - {&DDRA, &PINA, &PORTA, 4}, // A4 17 - {&DDRA, &PINA, &PORTA, 3}, // A3 18 - {&DDRA, &PINA, &PORTA, 2}, // A2 19 - {&DDRA, &PINA, &PORTA, 1}, // A1 20 - {&DDRA, &PINA, &PORTA, 0}, // A0 21 - {&DDRC, &PINC, &PORTC, 0}, // C0 22 - {&DDRC, &PINC, &PORTC, 1}, // C1 23 - {&DDRC, &PINC, &PORTC, 2}, // C2 24 - {&DDRC, &PINC, &PORTC, 3}, // C3 25 - {&DDRC, &PINC, &PORTC, 4}, // C4 26 - {&DDRC, &PINC, &PORTC, 5}, // C5 27 - {&DDRC, &PINC, &PORTC, 6}, // C6 28 - {&DDRC, &PINC, &PORTC, 7}, // C7 29 - {&DDRD, &PIND, &PORTD, 4}, // D4 30 - {&DDRD, &PIND, &PORTD, 7} // D7 31 -}; -#elif defined(VARIANT_STANDARD) -// Standard Layout -static const pin_map_t pinMap[] = { - {&DDRB, &PINB, &PORTB, 0}, // B0 0 - {&DDRB, &PINB, &PORTB, 1}, // B1 1 - {&DDRB, &PINB, &PORTB, 2}, // B2 2 - {&DDRB, &PINB, &PORTB, 3}, // B3 3 - {&DDRB, &PINB, &PORTB, 4}, // B4 4 - {&DDRB, &PINB, &PORTB, 5}, // B5 5 - {&DDRB, &PINB, &PORTB, 6}, // B6 6 - {&DDRB, &PINB, &PORTB, 7}, // B7 7 - {&DDRD, &PIND, &PORTD, 0}, // D0 8 - {&DDRD, &PIND, &PORTD, 1}, // D1 9 - {&DDRD, &PIND, &PORTD, 2}, // D2 10 - {&DDRD, &PIND, &PORTD, 3}, // D3 11 - {&DDRD, &PIND, &PORTD, 4}, // D4 12 - {&DDRD, &PIND, &PORTD, 5}, // D5 13 - {&DDRD, &PIND, &PORTD, 6}, // D6 14 - {&DDRD, &PIND, &PORTD, 7}, // D7 15 - {&DDRC, &PINC, &PORTC, 0}, // C0 16 - {&DDRC, &PINC, &PORTC, 1}, // C1 17 - {&DDRC, &PINC, &PORTC, 2}, // C2 18 - {&DDRC, &PINC, &PORTC, 3}, // C3 19 - {&DDRC, &PINC, &PORTC, 4}, // C4 20 - {&DDRC, &PINC, &PORTC, 5}, // C5 21 - {&DDRC, &PINC, &PORTC, 6}, // C6 22 - {&DDRC, &PINC, &PORTC, 7}, // C7 23 - {&DDRA, &PINA, &PORTA, 7}, // A7 24 - {&DDRA, &PINA, &PORTA, 6}, // A6 25 - {&DDRA, &PINA, &PORTA, 5}, // A5 26 - {&DDRA, &PINA, &PORTA, 4}, // A4 27 - {&DDRA, &PINA, &PORTA, 3}, // A3 28 - {&DDRA, &PINA, &PORTA, 2}, // A2 29 - {&DDRA, &PINA, &PORTA, 1}, // A1 30 - {&DDRA, &PINA, &PORTA, 0} // A0 31 -}; -#else // VARIANT_MIGHTY -#error Undefined variant 1284, 644, 324, 64, 32 -#endif // VARIANT_MIGHTY -//------------------------------------------------------------------------------ -#elif defined(__AVR_ATmega32U4__) -#ifdef CORE_TEENSY -// Teensy 2.0 -static const pin_map_t pinMap[] = { - {&DDRB, &PINB, &PORTB, 0}, // B0 0 - {&DDRB, &PINB, &PORTB, 1}, // B1 1 - {&DDRB, &PINB, &PORTB, 2}, // B2 2 - {&DDRB, &PINB, &PORTB, 3}, // B3 3 - {&DDRB, &PINB, &PORTB, 7}, // B7 4 - {&DDRD, &PIND, &PORTD, 0}, // D0 5 - {&DDRD, &PIND, &PORTD, 1}, // D1 6 - {&DDRD, &PIND, &PORTD, 2}, // D2 7 - {&DDRD, &PIND, &PORTD, 3}, // D3 8 - {&DDRC, &PINC, &PORTC, 6}, // C6 9 - {&DDRC, &PINC, &PORTC, 7}, // C7 10 - {&DDRD, &PIND, &PORTD, 6}, // D6 11 - {&DDRD, &PIND, &PORTD, 7}, // D7 12 - {&DDRB, &PINB, &PORTB, 4}, // B4 13 - {&DDRB, &PINB, &PORTB, 5}, // B5 14 - {&DDRB, &PINB, &PORTB, 6}, // B6 15 - {&DDRF, &PINF, &PORTF, 7}, // F7 16 - {&DDRF, &PINF, &PORTF, 6}, // F6 17 - {&DDRF, &PINF, &PORTF, 5}, // F5 18 - {&DDRF, &PINF, &PORTF, 4}, // F4 19 - {&DDRF, &PINF, &PORTF, 1}, // F1 20 - {&DDRF, &PINF, &PORTF, 0}, // F0 21 - {&DDRD, &PIND, &PORTD, 4}, // D4 22 - {&DDRD, &PIND, &PORTD, 5}, // D5 23 - {&DDRE, &PINE, &PORTE, 6} // E6 24 -}; -//------------------------------------------------------------------------------ -#else // CORE_TEENSY -// Leonardo -static const pin_map_t pinMap[] = { - {&DDRD, &PIND, &PORTD, 2}, // D2 0 - {&DDRD, &PIND, &PORTD, 3}, // D3 1 - {&DDRD, &PIND, &PORTD, 1}, // D1 2 - {&DDRD, &PIND, &PORTD, 0}, // D0 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRC, &PINC, &PORTC, 6}, // C6 5 - {&DDRD, &PIND, &PORTD, 7}, // D7 6 - {&DDRE, &PINE, &PORTE, 6}, // E6 7 - {&DDRB, &PINB, &PORTB, 4}, // B4 8 - {&DDRB, &PINB, &PORTB, 5}, // B5 9 - {&DDRB, &PINB, &PORTB, 6}, // B6 10 - {&DDRB, &PINB, &PORTB, 7}, // B7 11 - {&DDRD, &PIND, &PORTD, 6}, // D6 12 - {&DDRC, &PINC, &PORTC, 7}, // C7 13 - {&DDRB, &PINB, &PORTB, 3}, // B3 14 - {&DDRB, &PINB, &PORTB, 1}, // B1 15 - {&DDRB, &PINB, &PORTB, 2}, // B2 16 - {&DDRB, &PINB, &PORTB, 0}, // B0 17 - {&DDRF, &PINF, &PORTF, 7}, // F7 18 - {&DDRF, &PINF, &PORTF, 6}, // F6 19 - {&DDRF, &PINF, &PORTF, 5}, // F5 20 - {&DDRF, &PINF, &PORTF, 4}, // F4 21 - {&DDRF, &PINF, &PORTF, 1}, // F1 22 - {&DDRF, &PINF, &PORTF, 0}, // F0 23 - {&DDRD, &PIND, &PORTD, 4}, // D4 24 - {&DDRD, &PIND, &PORTD, 7}, // D7 25 - {&DDRB, &PINB, &PORTB, 4}, // B4 26 - {&DDRB, &PINB, &PORTB, 5}, // B5 27 - {&DDRB, &PINB, &PORTB, 6}, // B6 28 - {&DDRD, &PIND, &PORTD, 6} // D6 29 -}; -#endif // CORE_TEENSY -//------------------------------------------------------------------------------ -#elif defined(__AVR_AT90USB646__)\ -|| defined(__AVR_AT90USB1286__) -// Teensy++ 1.0 & 2.0 -static const pin_map_t pinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 0 - {&DDRD, &PIND, &PORTD, 1}, // D1 1 - {&DDRD, &PIND, &PORTD, 2}, // D2 2 - {&DDRD, &PIND, &PORTD, 3}, // D3 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRD, &PIND, &PORTD, 5}, // D5 5 - {&DDRD, &PIND, &PORTD, 6}, // D6 6 - {&DDRD, &PIND, &PORTD, 7}, // D7 7 - {&DDRE, &PINE, &PORTE, 0}, // E0 8 - {&DDRE, &PINE, &PORTE, 1}, // E1 9 - {&DDRC, &PINC, &PORTC, 0}, // C0 10 - {&DDRC, &PINC, &PORTC, 1}, // C1 11 - {&DDRC, &PINC, &PORTC, 2}, // C2 12 - {&DDRC, &PINC, &PORTC, 3}, // C3 13 - {&DDRC, &PINC, &PORTC, 4}, // C4 14 - {&DDRC, &PINC, &PORTC, 5}, // C5 15 - {&DDRC, &PINC, &PORTC, 6}, // C6 16 - {&DDRC, &PINC, &PORTC, 7}, // C7 17 - {&DDRE, &PINE, &PORTE, 6}, // E6 18 - {&DDRE, &PINE, &PORTE, 7}, // E7 19 - {&DDRB, &PINB, &PORTB, 0}, // B0 20 - {&DDRB, &PINB, &PORTB, 1}, // B1 21 - {&DDRB, &PINB, &PORTB, 2}, // B2 22 - {&DDRB, &PINB, &PORTB, 3}, // B3 23 - {&DDRB, &PINB, &PORTB, 4}, // B4 24 - {&DDRB, &PINB, &PORTB, 5}, // B5 25 - {&DDRB, &PINB, &PORTB, 6}, // B6 26 - {&DDRB, &PINB, &PORTB, 7}, // B7 27 - {&DDRA, &PINA, &PORTA, 0}, // A0 28 - {&DDRA, &PINA, &PORTA, 1}, // A1 29 - {&DDRA, &PINA, &PORTA, 2}, // A2 30 - {&DDRA, &PINA, &PORTA, 3}, // A3 31 - {&DDRA, &PINA, &PORTA, 4}, // A4 32 - {&DDRA, &PINA, &PORTA, 5}, // A5 33 - {&DDRA, &PINA, &PORTA, 6}, // A6 34 - {&DDRA, &PINA, &PORTA, 7}, // A7 35 - {&DDRE, &PINE, &PORTE, 4}, // E4 36 - {&DDRE, &PINE, &PORTE, 5}, // E5 37 - {&DDRF, &PINF, &PORTF, 0}, // F0 38 - {&DDRF, &PINF, &PORTF, 1}, // F1 39 - {&DDRF, &PINF, &PORTF, 2}, // F2 40 - {&DDRF, &PINF, &PORTF, 3}, // F3 41 - {&DDRF, &PINF, &PORTF, 4}, // F4 42 - {&DDRF, &PINF, &PORTF, 5}, // F5 43 - {&DDRF, &PINF, &PORTF, 6}, // F6 44 - {&DDRF, &PINF, &PORTF, 7} // F7 45 -}; -//------------------------------------------------------------------------------ -#else // CPU type -#error unknown CPU type -#endif // CPU type +/** Initializer macro. */ +#define GPIO_PIN(reg, bit) {&PIN##reg, &DDR##reg, &PORT##reg, 1 << bit} + +// Include pin map for current board. +#include "boards/GpioPinMap.h" //------------------------------------------------------------------------------ -/** count of pins */ -static const uint8_t digitalPinCount = sizeof(pinMap)/sizeof(pin_map_t); -//============================================================================== /** generate bad pin number error */ void badPinNumber(void) __attribute__((error("Pin number is too large or not a constant"))); @@ -402,100 +50,269 @@ __attribute__((error("Pin number is too large or not a constant"))); /** Check for valid pin number * @param[in] pin Number of pin to be checked. */ -static ALWAYS_INLINE void badPinCheck(uint8_t pin) +static inline __attribute__((always_inline)) +void badPinCheck(uint8_t pin) { - if (!__builtin_constant_p(pin) || pin >= digitalPinCount) { + if (!__builtin_constant_p(pin) || pin >= NUM_DIGITAL_PINS) { badPinNumber(); } } //------------------------------------------------------------------------------ -/** fast write helper +/** DDR register address + * @param[in] pin Arduino pin number + * @return register address + */ +static inline __attribute__((always_inline)) +volatile uint8_t* ddrReg(uint8_t pin) +{ + badPinCheck(pin); + return GpioPinMap[pin].ddr; +} +//------------------------------------------------------------------------------ +/** Bit mask for pin + * @param[in] pin Arduino pin number + * @return mask + */ +static inline __attribute__((always_inline)) +uint8_t pinMask(uint8_t pin) +{ + badPinCheck(pin); + return GpioPinMap[pin].mask; +} +//------------------------------------------------------------------------------ +/** PIN register address + * @param[in] pin Arduino pin number + * @return register address + */ +static inline __attribute__((always_inline)) +volatile uint8_t* pinReg(uint8_t pin) +{ + badPinCheck(pin); + return GpioPinMap[pin].pin; +} +//------------------------------------------------------------------------------ +/** PORT register address + * @param[in] pin Arduino pin number + * @return register address + */ +static inline __attribute__((always_inline)) +volatile uint8_t* portReg(uint8_t pin) +{ + badPinCheck(pin); + return GpioPinMap[pin].port; +} +//------------------------------------------------------------------------------ +/** Fast write helper. * @param[in] address I/O register address - * @param[in] bit bit number to write + * @param[in] mask bit mask for pin * @param[in] level value for bit */ -static ALWAYS_INLINE void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) +static inline __attribute__((always_inline)) +void fastBitWriteSafe(volatile uint8_t* address, uint8_t mask, bool level) { - uint8_t oldSREG; - if (address > (uint8_t*)0X5F) { - oldSREG = SREG; + uint8_t s; + if (address > reinterpret_cast(0X3F)) { + s = SREG; cli(); } if (level) { - *address |= 1 << bit; + *address |= mask; } else { - *address &= ~(1 << bit); + *address &= ~mask; } - if (address > (uint8_t*)0X5F) { - SREG = oldSREG; + if (address > reinterpret_cast(0X3F)) { + SREG = s; } } //------------------------------------------------------------------------------ -/** read pin value +/** Read pin value. * @param[in] pin Arduino pin number * @return value read */ -static ALWAYS_INLINE bool fastDigitalRead(uint8_t pin) +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { - badPinCheck(pin); - return (*pinMap[pin].pin >> pinMap[pin].bit) & 1; + return *pinReg(pin) & pinMask(pin); } //------------------------------------------------------------------------------ -/** toggle a pin +/** Toggle a pin. * @param[in] pin Arduino pin number * * 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. */ -static ALWAYS_INLINE void fastDigitalToggle(uint8_t pin) +static inline __attribute__((always_inline)) +void fastDigitalToggle(uint8_t pin) { - badPinCheck(pin); - if (pinMap[pin].pin > (uint8_t*)0X5F) { + if (pinReg(pin) > reinterpret_cast(0X3F)) { // must write bit to high address port - *pinMap[pin].pin = 1 << pinMap[pin].bit; + *pinReg(pin) = pinMask(pin); } else { // will compile to sbi and PIN register will not be read. - *pinMap[pin].pin |= 1 << pinMap[pin].bit; + *pinReg(pin) |= pinMask(pin); } } //------------------------------------------------------------------------------ -/** Set pin value +/** Set pin value. * @param[in] pin Arduino pin number * @param[in] level value to write */ -static ALWAYS_INLINE void fastDigitalWrite(uint8_t pin, bool level) +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool level) { - badPinCheck(pin); - fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, level); + fastBitWriteSafe(portReg(pin), pinMask(pin), level); +} +//------------------------------------------------------------------------------ +/** Write the DDR register. + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDdrWrite(uint8_t pin, bool level) +{ + fastBitWriteSafe(ddrReg(pin), pinMask(pin), level); } //------------------------------------------------------------------------------ -/** set pin mode +/** Set pin mode. * @param[in] pin Arduino pin number - * @param[in] mode if true set output mode else input mode + * @param[in] mode INPUT, OUTPUT, or INPUT_PULLUP. * - * fastPinMode does not enable or disable the 20K pullup for input mode. + * The internal pullup resistors will be enabled if mode is INPUT_PULLUP + * and disabled if the mode is INPUT. */ -static ALWAYS_INLINE void fastPinMode(uint8_t pin, bool mode) +static inline __attribute__((always_inline)) +void fastPinMode(uint8_t pin, uint8_t mode) { - badPinCheck(pin); - fastBitWriteSafe(pinMap[pin].ddr, pinMap[pin].bit, mode); + fastDdrWrite(pin, mode == OUTPUT); + if (mode != OUTPUT) { + fastDigitalWrite(pin, mode == INPUT_PULLUP); + } } +#else // defined(__AVR__) +#if defined(CORE_TEENSY) +//------------------------------------------------------------------------------ +/** read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) +{ + return *portInputRegister(pin); +} +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool value) +{ + if (value) { + *portSetRegister(pin) = 1; + } else { + *portClearRegister(pin) = 1; + } +} +#elif defined(__SAM3X8E__) || defined(__SAM3X8H__) +//------------------------------------------------------------------------------ +/** read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) +{ + return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin; +} +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool value) +{ + if (value) { + g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin; + } else { + g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin; + } +} +#elif defined(ESP8266) +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] val value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, uint8_t val) +{ + if (pin < 16) { + if (val) { + GPOS = (1 << pin); + } else { + GPOC = (1 << pin); + } + } else if (pin == 16) { + if (val) { + GP16O |= 1; + } else { + GP16O &= ~1; + } + } +} +//------------------------------------------------------------------------------ +/** Read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) +{ + if (pin < 16) { + return GPIP(pin); + } else if (pin == 16) { + return GP16I & 0x01; + } + return 0; +} +#else // CORE_TEENSY +//------------------------------------------------------------------------------ +inline void fastDigitalWrite(uint8_t pin, bool value) +{ + digitalWrite(pin, value); +} +//------------------------------------------------------------------------------ +inline bool fastDigitalRead(uint8_t pin) +{ + return digitalRead(pin); +} +#endif // CORE_TEENSY +//------------------------------------------------------------------------------ +inline void fastDigitalToggle(uint8_t pin) +{ + fastDigitalWrite(pin, !fastDigitalRead(pin)); +} +//------------------------------------------------------------------------------ +inline void fastPinMode(uint8_t pin, uint8_t mode) +{ + pinMode(pin, mode); +} +#endif // __AVR__ //------------------------------------------------------------------------------ /** set pin configuration * @param[in] pin Arduino pin number - * @param[in] mode If true set output mode else input mode + * @param[in] mode mode INPUT or OUTPUT. * @param[in] level If mode is output, set level high/low. * If mode is input, enable or disable the pin's 20K pullup. */ -static ALWAYS_INLINE void fastPinConfig(uint8_t pin, bool mode, bool level) -{ - fastPinMode(pin, mode); - fastDigitalWrite(pin, level); -} +#define fastPinConfig(pin, mode, level)\ + {fastPinMode(pin, mode); fastDigitalWrite(pin, level);} //============================================================================== /** * @class DigitalPin - * @brief Fast AVR digital port I/O + * @brief Fast digital port I/O */ template class DigitalPin @@ -505,25 +322,7 @@ class DigitalPin /** Constructor */ DigitalPin() {} //---------------------------------------------------------------------------- - /** Constructor - * @param[in] pinMode if true set output mode else input mode. - */ - 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. - * If mode is input, enable or disable the pin's 20K pullup. - */ - DigitalPin(bool mode, bool level) - { - config(mode, level); - } - //---------------------------------------------------------------------------- - /** Asignment operator + /** Asignment operator. * @param[in] value If true set the pin's level high else set the * pin's level low. * @@ -535,7 +334,7 @@ class DigitalPin return *this; } //---------------------------------------------------------------------------- - /** Parenthesis operator + /** Parenthesis operator. * @return Pin's level */ inline operator bool () const __attribute__((always_inline)) @@ -543,12 +342,13 @@ class DigitalPin return read(); } //---------------------------------------------------------------------------- - /** set pin configuration - * @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 configuration. + * @param[in] mode: INPUT or OUTPUT. + * @param[in] level If mode is OUTPUT, set level high/low. + * If mode is INPUT, enable or disable the pin's 20K pullup. */ - ALWAYS_INLINE void config(bool mode, bool level) + inline __attribute__((always_inline)) + void config(uint8_t mode, bool level) { fastPinConfig(PinNumber, mode, level); } @@ -556,7 +356,8 @@ class DigitalPin /** * Set pin level high if output mode or enable 20K pullup if input mode. */ - ALWAYS_INLINE void high() + inline __attribute__((always_inline)) + void high() { write(true); } @@ -564,34 +365,39 @@ class DigitalPin /** * Set pin level low if output mode or disable 20K pullup if input mode. */ - ALWAYS_INLINE void low() + inline __attribute__((always_inline)) + void low() { write(false); } //---------------------------------------------------------------------------- /** - * Set pin mode - * @param[in] pinMode if true set output mode else input mode. + * Set pin mode. + * @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP. * - * mode() does not enable or disable the 20K pullup for input mode. + * The internal pullup resistors will be enabled if mode is INPUT_PULLUP + * and disabled if the mode is INPUT. */ - ALWAYS_INLINE void mode(bool pinMode) + inline __attribute__((always_inline)) + void mode(uint8_t mode) { - fastPinMode(PinNumber, pinMode); + fastPinMode(PinNumber, mode); } //---------------------------------------------------------------------------- - /** @return Pin's level */ - ALWAYS_INLINE bool read() const + /** @return Pin's level. */ + inline __attribute__((always_inline)) + bool read() const { return fastDigitalRead(PinNumber); } //---------------------------------------------------------------------------- - /** toggle a pin + /** 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() + inline __attribute__((always_inline)) + void toggle() { fastDigitalToggle(PinNumber); } @@ -600,10 +406,11 @@ class DigitalPin * @param[in] value If true set the pin's level high else set the * pin's level low. */ - ALWAYS_INLINE void write(bool value) + inline __attribute__((always_inline)) + void write(bool value) { fastDigitalWrite(PinNumber, value); } }; #endif // DigitalPin_h -/** @} */ \ No newline at end of file +/** @} */ diff --git a/hal/architecture/AVR/drivers/DigitalIO/I2cConstants.h b/hal/architecture/AVR/drivers/DigitalIO/I2cConstants.h index eb99c6bd1..f2d07671e 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/I2cConstants.h +++ b/hal/architecture/AVR/drivers/DigitalIO/I2cConstants.h @@ -22,7 +22,6 @@ * @brief Two Wire Interface constants. * * @defgroup twoWire I2C constants - * @ingroup internals * @details Two Wire Interface library. * @{ */ @@ -60,4 +59,4 @@ const uint8_t I2C_NO_PULLUPS = 0; /** Enable internal pull-ups on SDA and SCL. Used by TwiMaster class. */ const uint8_t I2C_INTERNAL_PULLUPS = 1; #endif // I2cConstants_h -/** @} */ \ No newline at end of file +/** @} */ diff --git a/hal/architecture/AVR/drivers/DigitalIO/PinIO.cpp b/hal/architecture/AVR/drivers/DigitalIO/PinIO.cpp index e2b842bec..b34034265 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/PinIO.cpp +++ b/hal/architecture/AVR/drivers/DigitalIO/PinIO.cpp @@ -25,6 +25,7 @@ * @details Two Wire Interface library. * @{ */ +#if defined(__AVR__) || defined(DOXYGEN) // AVR only #include "PinIO.h" #include #include @@ -54,20 +55,21 @@ bool PinIO::begin(uint8_t pin) return true; } //------------------------------------------------------------------------------ -/** Configure the 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. - * For input mode enable 20K pullup if true else Hi-Z. + * @param[in] mode: INPUT or OUTPUT. + * @param[in] level If mode is OUTPUT, set level high/low. + * If mode is INPUT, enable or disable the pin's 20K pullup. * * This function may be used with interrupts enabled or disabled. * The previous interrupt state will be restored. */ -void PinIO::config(bool mode, bool data) +void PinIO::config(uint8_t mode, bool level) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { modeI(mode); - writeI(data); + writeI(level); } } +#endif // __AVR__ /** @} */ diff --git a/hal/architecture/AVR/drivers/DigitalIO/PinIO.h b/hal/architecture/AVR/drivers/DigitalIO/PinIO.h index 694646031..2bb218e2e 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/PinIO.h +++ b/hal/architecture/AVR/drivers/DigitalIO/PinIO.h @@ -22,16 +22,15 @@ * @brief Digital AVR port I/O with runtime pin number. * * @defgroup runtimeDigital Runtime Pin I/O - * @ingroup internals * @details Two Wire Interface library. * @{ */ #ifndef PinIO_h #define PinIO_h +#if defined(__AVR__) || defined(DOXYGEN) // AVR only +#include #include #include -/** @brief Helper macro for complex inline attributes */ -#define ALWAYS_INLINE inline __attribute__((always_inline)) //------------------------------------------------------------------------------ /** * @class PinIO @@ -41,33 +40,15 @@ class PinIO { public: /** Create a PinIO object with no assigned pin. */ - // Suppress warning about uninitialized variables because initializing them in an init function - // allows the compiler to optimize away the variables in case the class is only instantiated but - // never used. // cppcheck-suppress uninitMemberVar PinIO() : bit_(0), mask_(0XFF) {} - /** Constructor - * @param[in] pin Pin assigned to this object. - */ explicit PinIO(uint8_t pin); - /** Initialize pin bit mask and port address. - * @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 - * - * @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. - * 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. - */ - void config(bool mode, bool data); + void config(uint8_t mode, bool data); //---------------------------------------------------------------------------- /** @return Pin's level */ - ALWAYS_INLINE bool read() + inline __attribute__((always_inline)) + bool read() { return *pinReg_ & bit_; } @@ -77,7 +58,8 @@ class PinIO * 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() + inline __attribute__((always_inline)) + void toggle() { *pinReg_ = bit_; } @@ -88,7 +70,8 @@ class PinIO * This function must be called with interrupts disabled. * This function will not change the interrupt state. */ - ALWAYS_INLINE void highI() + inline __attribute__((always_inline)) + void highI() { writeI(1); } @@ -98,24 +81,31 @@ class PinIO * This function must be called with interrupts disabled. * This function will not change the interrupt state. */ - ALWAYS_INLINE void lowI() + inline __attribute__((always_inline)) + void lowI() { writeI(0); } - /** - * Set pin mode - * @param[in] mode if true set output mode else input mode. + /** Set pin mode. + * + * @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP. * - * mode() does not enable or disable the 20K pullup for input mode. + * The internal pullup resistors will be enabled if mode is INPUT_PULLUP + * and disabled if the mode is INPUT. * * This function must be called with interrupts disabled. * This function will not change the interrupt state. */ - ALWAYS_INLINE void modeI(bool mode) + inline __attribute__((always_inline)) + void modeI(uint8_t mode) { volatile uint8_t* ddrReg = pinReg_ + 1; - *ddrReg = mode ? *ddrReg | bit_ : *ddrReg & mask_; + *ddrReg = mode == OUTPUT ? *ddrReg | bit_ : *ddrReg & mask_; + if (mode != OUTPUT) { + writeI(mode == INPUT_PULLUP); + } } + /** Write pin. * * @param[in] level If output mode set pin high if true else low. @@ -124,7 +114,8 @@ class PinIO * This function must be called with interrupts disabled. * This function will not change the interrupt state. */ - ALWAYS_INLINE void writeI(bool level) + inline __attribute__((always_inline)) + void writeI(bool level) { *portReg_ = level ? *portReg_ | bit_ : *portReg_ & mask_; } @@ -135,39 +126,47 @@ class PinIO * This function will enable interrupts. This function should not be * called in an ISR or where interrupts are disabled. */ - ALWAYS_INLINE void high() + inline __attribute__((always_inline)) + void high() { ATOMIC_BLOCK(ATOMIC_FORCEON) { highI(); } } + /** * 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. */ - ALWAYS_INLINE void low() + inline __attribute__((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 mode. + * + * @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP. * - * mode() does not enable or disable the 20K pullup for input mode. + * The internal pullup resistors will be enabled if mode is INPUT_PULLUP + * and disabled if the mode is INPUT. * * 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) + inline __attribute__((always_inline)) + void mode(uint8_t mode) { ATOMIC_BLOCK(ATOMIC_FORCEON) { - modeI(pinMode); + modeI(mode); } } + /** Write pin. * * @param[in] level If output mode set pin high if true else low. @@ -176,7 +175,8 @@ class PinIO * 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) + inline __attribute__((always_inline)) + void write(bool level) { ATOMIC_BLOCK(ATOMIC_FORCEON) { writeI(level); @@ -189,5 +189,6 @@ class PinIO volatile uint8_t* pinReg_; volatile uint8_t* portReg_; }; +#endif // __AVR__ #endif // PinIO_h /** @} */ diff --git a/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.cpp b/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.cpp index e504d3fc2..e3c6d372a 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.cpp +++ b/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.cpp @@ -17,9 +17,10 @@ * along with the Arduino DigitalIO Library. If not, see * . */ +#if defined(__AVR__) || defined(DOXYGEN) // AVR only /** * @file - * @brief Software I2C library + * @brief AVR Software I2C library * * @defgroup softI2C Software I2C * @details Software Two Wire Interface library. @@ -55,10 +56,10 @@ bool I2cMasterBase::transfer(uint8_t addrRW, start(); } if (!write(addrRW)) { - _state = addrRW & I2C_READ ? STATE_RX_ADDR_NACK : STATE_TX_ADDR_NACK; + _state = (addrRW & I2C_READ) ? STATE_RX_ADDR_NACK : STATE_TX_ADDR_NACK; return false; } - _state = addrRW & I2C_READ ? STATE_RX_DATA : STATE_TX_DATA; + _state = (addrRW & I2C_READ) ? STATE_RX_DATA : STATE_TX_DATA; return transferContinue(buf, nbytes, option); } //------------------------------------------------------------------------------ @@ -246,4 +247,6 @@ bool SoftI2cMaster::write(uint8_t data) writeSda(LOW); return rtn == 0; } +#endif // __AVR__ /** @} */ + diff --git a/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.h b/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.h index dd250eb9b..480459270 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.h +++ b/hal/architecture/AVR/drivers/DigitalIO/SoftI2cMaster.h @@ -21,18 +21,14 @@ #define SOFT_I2C_MASTER_H /** * @file - * @brief Software I2C library + * @brief AVR Software I2C library * * @defgroup softI2C Software I2C - * @ingroup internals * @details Software Two Wire Interface library. * @{ */ -#if ARDUINO < 100 -#error Requires Arduino 1.0 or greater. -#else // ARDUINO +#if defined(__AVR__) || defined(DOXYGEN) // AVR only #include -#endif // ARDUINO #include #include "DigitalPin.h" #include "I2cConstants.h" @@ -92,45 +88,9 @@ class I2cMasterBase */ virtual void stop() = 0; - /** - * Start an I2C transfer with possible continuation. - * - * @param[in] addressRW I2C slave address plus R/W bit. - * The I2C slave address is in the high seven bits - * and is ORed with on of the following: - * - I2C_READ for a read transfer. - * - I2C_WRITE for a write transfer. - * . - * @param[in,out] buf Source or destination for transfer. - * @param[in] nbyte Number of bytes to transfer (may be zero). - * @param[in] option Option for ending the transfer, one of: - * - I2C_STOP end the transfer with an I2C stop - * condition. - * - I2C_REP_START end the transfer with an I2C - * repeated start condition. - * - I2C_CONTINUE allow additional transferContinue() - * calls. - * . - * @return true for success else false. - */ bool transfer(uint8_t addressRW, void *buf, size_t nbyte, uint8_t option = I2C_STOP); - /** - * Continue an I2C transfer. - * - * @param[in,out] buf Source or destination for transfer. - * @param[in] nbyte Number of bytes to transfer (may be zero). - * @param[in] option Option for ending the transfer, one of: - * - I2C_STOP end the transfer with an I2C stop - * condition. - * - I2C_REP_START end the transfer with an I2C - * repeated start condition. - * - I2C_CONTINUE allow additional transferContinue() - * calls. - * . - * @return true for success else false. - */ bool transferContinue(void *buf, size_t nbyte, uint8_t option = I2C_STOP); /** Write a byte * @@ -147,27 +107,13 @@ class I2cMasterBase //============================================================================== /** * @class SoftI2cMaster - * @brief Software I2C master class + * @brief AVR Software I2C master class */ class SoftI2cMaster : public I2cMasterBase { public: SoftI2cMaster() {} - /** - * Constructor, initialize SCL/SDA pins and set the bus high. - * - * @param[in] sdaPin The software SDA pin number. - * - * @param[in] sclPin The software SCL pin number. - */ SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin); - /** - * Initialize SCL/SDA pins and set the bus high. - * - * @param[in] sdaPin The software SDA 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 start(); @@ -224,7 +170,7 @@ class SoftI2cMaster : public I2cMasterBase //------------------------------------------------------------------------------ /** * @class FastI2cMaster - * @brief Fast software I2C master class. + * @brief AVR Fast software I2C master class. */ template class FastI2cMaster : public I2cMasterBase @@ -323,13 +269,13 @@ class FastI2cMaster : public I2cMasterBase inline __attribute__((always_inline)) void sclWrite(bool value) { - fastPinMode(sclPin, !value); + fastDdrWrite(sclPin, !value); } //---------------------------------------------------------------------------- inline __attribute__((always_inline)) void sdaWrite(bool value) { - fastPinMode(sdaPin, !value); + fastDdrWrite(sdaPin, !value); } //---------------------------------------------------------------------------- inline __attribute__((always_inline)) @@ -362,6 +308,7 @@ class FastI2cMaster : public I2cMasterBase sclDelay(5); } }; +#endif // __AVR__ #endif // SOFT_I2C_MASTER_H /** @} */ diff --git a/hal/architecture/AVR/drivers/DigitalIO/SoftSPI.h b/hal/architecture/AVR/drivers/DigitalIO/SoftSPI.h index d71405521..1cccc9d03 100644 --- a/hal/architecture/AVR/drivers/DigitalIO/SoftSPI.h +++ b/hal/architecture/AVR/drivers/DigitalIO/SoftSPI.h @@ -22,7 +22,6 @@ * @brief Software SPI. * * @defgroup softSPI Software SPI - * @ingroup internals * @details Software SPI Template Class. * @{ */ @@ -35,13 +34,13 @@ #define nop asm volatile ("nop\n\t") //------------------------------------------------------------------------------ /** Pin Mode for MISO is input.*/ -const bool MISO_MODE = false; +#define MISO_MODE INPUT /** Pullups disabled for MISO are disabled. */ -const bool MISO_LEVEL = false; +#define MISO_LEVEL false /** Pin Mode for MOSI is output.*/ -const bool MOSI_MODE = true; +#define MOSI_MODE OUTPUT /** Pin Mode for SCK is output. */ -const bool SCK_MODE = true; +#define SCK_MODE OUTPUT //------------------------------------------------------------------------------ /** * @class SoftSPI @@ -63,7 +62,8 @@ class SoftSPI /** Soft SPI receive byte. * @return Data byte received. */ - ALWAYS_INLINE uint8_t receive() + inline __attribute__((always_inline)) + uint8_t receive() { uint8_t data = 0; receiveBit(7, &data); @@ -80,7 +80,8 @@ class SoftSPI /** Soft SPI send byte. * @param[in] data Data byte to send. */ - ALWAYS_INLINE void send(uint8_t data) + inline __attribute__((always_inline)) + void send(uint8_t data) { sendBit(7, data); sendBit(6, data); @@ -96,7 +97,8 @@ class SoftSPI * @param[in] txData Data byte to send. * @return Data byte received. */ - ALWAYS_INLINE uint8_t transfer(uint8_t txData) + inline __attribute__((always_inline)) + uint8_t transfer(uint8_t txData) { uint8_t rxData = 0; transferBit(7, &rxData, txData); @@ -112,15 +114,18 @@ class SoftSPI private: //---------------------------------------------------------------------------- - ALWAYS_INLINE bool MODE_CPHA(uint8_t mode) + inline __attribute__((always_inline)) + bool MODE_CPHA(uint8_t mode) { return (mode & 1) != 0; } - ALWAYS_INLINE bool MODE_CPOL(uint8_t mode) + inline __attribute__((always_inline)) + bool MODE_CPOL(uint8_t mode) { return (mode & 2) != 0; } - ALWAYS_INLINE void receiveBit(uint8_t bit, uint8_t* data) + inline __attribute__((always_inline)) + void receiveBit(uint8_t bit, uint8_t* data) { if (MODE_CPHA(Mode)) { fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); @@ -137,7 +142,8 @@ class SoftSPI } } //---------------------------------------------------------------------------- - ALWAYS_INLINE void sendBit(uint8_t bit, uint8_t data) + inline __attribute__((always_inline)) + void sendBit(uint8_t bit, uint8_t data) { if (MODE_CPHA(Mode)) { fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); @@ -152,7 +158,8 @@ class SoftSPI } } //---------------------------------------------------------------------------- - ALWAYS_INLINE void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData) + inline __attribute__((always_inline)) + void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData) { if (MODE_CPHA(Mode)) { fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); diff --git a/hal/architecture/AVR/drivers/DigitalIO/attic/ADS7818.h b/hal/architecture/AVR/drivers/DigitalIO/attic/ADS7818.h deleted file mode 100644 index 4e6f5d8ba..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/attic/ADS7818.h +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef ADS7818_h -#define ADS7871_h -//------------------------------------------------------------------------------ -/** nop to tune soft SPI timing */ -#define nop asm volatile ("nop\n\t") -//------------------------------------------------------------------------------ -/** doxygen can't handle __attribute__ very well */ -#define STATIC_ALWAYS_INLINE static inline __attribute__((always_inline)) -//------------------------------------------------------------------------------ -template -/** ADS7818 class */ -class ADS7818 -{ -public: - //---------------------------------------------------------------------------- - /** Set pin modes and initial levels. */ - void begin() - { - fastPinMode(ClkPin, 1); - fastDigitalWrite(ClkPin, 0); - fastPinMode(ConvPin, 1); - fastDigitalWrite(ConvPin, 1); - fastPinMode(DataPin, 0); - } - //---------------------------------------------------------------------------- - /** Read ADS7818 12-bit ADC - * \return 16-bit value read. - */ - inline __attribute__((always_inline)) - uint16_t read() - { - uint16_t v = 0; - fastDigitalWrite(ConvPin, 0); - fastDigitalWrite(ClkPin, 1); - fastDigitalWrite(ClkPin, 0); - fastDigitalWrite(ConvPin, 1); - fastDigitalWrite(ClkPin, 1); - fastDigitalWrite(ClkPin, 0); - - readBitFast16(v, 11); - readBitFast16(v, 10); - readBitFast16(v, 9); - readBitFast16(v, 8); - readBitFast16(v, 7); - readBitFast16(v, 6); - readBitFast16(v, 5); - readBitFast16(v, 4); - readBitFast16(v, 3); - readBitFast16(v, 2); - readBitFast16(v, 1); - readBitFast16(v, 0); - return v; - } -private: - //---------------------------------------------------------------------------- - inline __attribute__((always_inline)) - void readBitFast16(uint16_t &v, uint8_t b) - { - fastDigitalWrite(ClkPin, 1); - if (fastDigitalRead(DataPin)) { - v |= (1 << b); - } - fastDigitalWrite(ClkPin, 0); - } -}; -#if 0 -/** initialize I/O ports */ -STATIC_ALWAYS_INLINE void adcBegin() -{ - fastPinMode(ADC_CLK_PIN, 1); - fastDigitalWrite(ADC_CLK_PIN, 0); - fastPinMode(ADC_CONV_PIN, 1); - fastDigitalWrite(ADC_CONV_PIN, 1); - fastPinMode(ADC_DATA_PIN, 0); -} -//------------------------------------------------------------------------------ -/** clock for don't care bits - *\param[in] delayBefore if true, delay before raising clock*/ -STATIC_ALWAYS_INLINE void fastDummy(bool first) -{ - if (!first) { - nop; - } - fastDigitalWrite(ADC_CLK_PIN, 1); - nop; - fastDigitalWrite(ADC_CLK_PIN, 0); -} -//------------------------------------------------------------------------------ -/** read next bit fast as possible - * \param[in] v word to receive bit - * \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) -{ - fastDigitalWrite(ADC_CLK_PIN, 1); - if (fastDigitalRead(ADC_DATA_PIN)) { - v |= (1 << b); - } - fastDigitalWrite(ADC_CLK_PIN, 0); -} -//------------------------------------------------------------------------------ -/** Read AD7680 16-bit ADC in less than 8 microseconds - * cs is chip select pin - */ -STATIC_ALWAYS_INLINE uint16_t adcRead(bool centered = false) -{ - uint16_t v = 0; - fastDigitalWrite(ADC_CONV_PIN, 0); - fastDigitalWrite(ADC_CLK_PIN, 1); - fastDigitalWrite(ADC_CLK_PIN, 0); - fastDigitalWrite(ADC_CONV_PIN, 1); - fastDigitalWrite(ADC_CLK_PIN, 1); - fastDigitalWrite(ADC_CLK_PIN, 0); - // uint16_t v = 0; - - // fastDummy(1); - // fastDummy(0); - // fastDummy(0); - // fastDummy(0); - - // readBitFast16(v, 15); - // readBitFast16(v, 14); - // readBitFast16(v, 13); - // readBitFast16(v, 12); - readBitFast16(v, 11); - readBitFast16(v, 10); - readBitFast16(v, 9); - readBitFast16(v, 8); - readBitFast16(v, 7); - readBitFast16(v, 6); - readBitFast16(v, 5); - readBitFast16(v, 4); - readBitFast16(v, 3); - readBitFast16(v, 2); - readBitFast16(v, 1); - readBitFast16(v, 0); - return centered ? v ^ 0X8000 : v; -} -#endif // 0 -#endif // ADS7818_h \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/attic/MCP320X.h b/hal/architecture/AVR/drivers/DigitalIO/attic/MCP320X.h deleted file mode 100644 index 9da32dc5a..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/attic/MCP320X.h +++ /dev/null @@ -1,305 +0,0 @@ -#ifndef MCP320X_h -#define MCP320X_h -#include -//============================================================================== -template -/** MCP3201 class */ -class MCP3201 -{ -public: - //---------------------------------------------------------------------------- - /** Set pin modes and initial levels. */ - void begin() - { - fastPinMode(ClkPin, 1); - fastDigitalWrite(ClkPin, 0); - fastPinMode(CsPin, 1); - fastDigitalWrite(CsPin, 1); - fastPinMode(DoutPin, 0); - } - //---------------------------------------------------------------------------- - /** Read MCP3201 12-bit ADC - * \return 16-bit value read. - */ - inline __attribute__((always_inline)) - uint16_t read() - { - fastDigitalWrite(CsPin, 0); - // Start sample. - mcpAdcDummy(3); - // Extra sample time. - if (UsecDelay) { - delayMicroseconds(UsecDelay); - } - // End sample. - mcpAdcDummy(3); - // Null bit. - mcpAdcDummy(3); - uint16_t v = 0; - readBit(v, 11); - readBit(v, 10); - readBit(v, 9); - readBit(v, 8); - readBit(v, 7); - readBit(v, 6); - readBit(v, 5); - readBit(v, 4); - readBit(v, 3); - readBit(v, 2); - readBit(v, 1); - readBit(v, 0); - fastDigitalWrite(CsPin, 1); - return v; - } -private: - //---------------------------------------------------------------------------- - /** delay n nops - * \param[in] n nops to delay, must be a constant so compiler optimizes if(). - */ - inline __attribute__((always_inline)) - void delayCycles(uint8_t n) - { - if (n & 1) { - asm volatile("nop\n\t"); - } - if (n & 2) { - asm volatile("nop\n\t" "nop\n\t"); - } - if (n & 4) { - asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); - } - } - //---------------------------------------------------------------------------- - /** clock for don't care bits - *\param[in] delayBefore if true, delay before raising clock*/ - inline __attribute__((always_inline)) - void mcpAdcDummy(uint8_t delayBefore) - { - delayCycles(delayBefore); - 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 -/** MCP3202 class */ -class MCP3202 -{ -public: - //---------------------------------------------------------------------------- - /** Set pin modes and initial levels. */ - void begin() - { - fastPinMode(ClkPin, 1); - fastDigitalWrite(ClkPin, 0); - fastPinMode(CsPin, 1); - fastDigitalWrite(CsPin, 1); - fastPinMode(DoutPin, 0); - fastPinMode(DinPin, 1); - } - //---------------------------------------------------------------------------- - /** Read MCP3202 12-bit ADC - * \return 16-bit value read. - */ - inline __attribute__((always_inline)) - uint16_t read(uint8_t config) - { - uint16_t v = 0; - fastDigitalWrite(CsPin, 0); - // Start bit. - writeBit(true); - // Mode bit. - writeBit(config & 2); - // Channel Selection and start sample. - writeBit(config & 1); - // Extra sample time. - if (UsecDelay) { - delayMicroseconds(UsecDelay); - } - // MSB first format and end sample. - writeBit(true); - // Null bit. - writeBit(true); - // Data bits. - readBit(v, 11); - readBit(v, 10); - readBit(v, 9); - readBit(v, 8); - readBit(v, 7); - readBit(v, 6); - readBit(v, 5); - readBit(v, 4); - readBit(v, 3); - readBit(v, 2); - readBit(v, 1); - readBit(v, 0); - fastDigitalWrite(CsPin, 1); - return v; - } -private: - //---------------------------------------------------------------------------- - /** delay n nops - * \param[in] n nops to delay, must be a constant so compiler optimizes if(). - */ - inline __attribute__((always_inline)) - void delayCycles(uint8_t n) - { - if (n & 1) { - asm volatile("nop\n\t"); - } - if (n & 2) { - asm volatile("nop\n\t" "nop\n\t"); - } - if (n & 4) { - asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); - } - } - //---------------------------------------------------------------------------- - /** clock for don't care bits - *\param[in] delayBefore if true, delay before raising clock*/ - inline __attribute__((always_inline)) - void writeBit(bool value) - { - fastDigitalWrite(DinPin, value); - 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 -/** MCP3204 class */ -class MCP3204 -{ -public: - //---------------------------------------------------------------------------- - /** Set pin modes and initial levels. */ - void begin() - { - fastPinMode(ClkPin, 1); - fastDigitalWrite(ClkPin, 0); - fastPinMode(CsPin, 1); - fastDigitalWrite(CsPin, 1); - fastPinMode(DoutPin, 0); - fastPinMode(DinPin, 1); - } - //---------------------------------------------------------------------------- - /** Read MCP3204 12-bit ADC - * \return 16-bit value read. - */ - inline __attribute__((always_inline)) - uint16_t read(uint8_t config) - { - uint16_t v = 0; - fastDigitalWrite(CsPin, 0); - // Start bit. - writeBit(true); - // Mode bit. - writeBit(config & 8); - // D2 channel bit. - writeBit(config & 4); - // D1 channel bit. - writeBit(config & 2); - // D0 channel bit and start sample. - writeBit(config & 1); - // Extra sample time. - if (UsecDelay) { - delayMicroseconds(UsecDelay); - } - // End sample cycle. - writeBit(true); - // Null bit. - writeBit(true); - // Data bits. - readBit(v, 11); - readBit(v, 10); - readBit(v, 9); - readBit(v, 8); - readBit(v, 7); - readBit(v, 6); - readBit(v, 5); - readBit(v, 4); - readBit(v, 3); - readBit(v, 2); - readBit(v, 1); - readBit(v, 0); - fastDigitalWrite(CsPin, 1); - return v; - } -private: - //---------------------------------------------------------------------------- - /** delay n nops - * \param[in] n nops to delay, must be a constant so compiler optimizes if(). - */ - inline __attribute__((always_inline)) - void delayCycles(uint8_t n) - { - if (n & 1) { - asm volatile("nop\n\t"); - } - if (n & 2) { - asm volatile("nop\n\t" "nop\n\t"); - } - if (n & 4) { - asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); - } - } - //---------------------------------------------------------------------------- - /** clock for don't care bits - *\param[in] delayBefore if true, delay before raising clock*/ - inline __attribute__((always_inline)) - void writeBit(bool value) - { - fastDigitalWrite(DinPin, value); - 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. -template -class MCP3208 : public MCP3204 -{ -}; -#endif // MCP320X_h \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/attic/MCP355X.h b/hal/architecture/AVR/drivers/DigitalIO/attic/MCP355X.h deleted file mode 100644 index fb66d4a47..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/attic/MCP355X.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef MCP355X_h -#define MCP355X_h -#include -// Overflow values. -const int32_t MCP355X_OVH = 2097152L; -const int32_t MCP355X_OVL = -2097153L; -const int32_t MCP355X_ERR = 0X80000000; -const uint8_t MCP355X_TO_MS = 100; -//============================================================================== -template -/** MCP355X class */ -class MCP355X -{ -public: - //---------------------------------------------------------------------------- - /** Set pin modes and initial levels. */ - void begin(bool singleMode = true) - { - m_singleMode = singleMode; - fastPinMode(SdoPin, 0); - fastPinMode(SckPin, 1); - fastDigitalWrite(SckPin, 1); - fastPinMode(CsPin, 1); - fastDigitalWrite(CsPin, 1); - if (!m_singleMode) { - // Wait for conversion to complete. - delay(100); - // Start continuous conversion mode. - fastDigitalWrite(CsPin, 0); - } - } - //---------------------------------------------------------------------------- - /** Read MCP355X 22-bit ADC - * \return 22-bit value. - */ - inline __attribute__((always_inline)) - int32_t read() - { - uint8_t t = 0; - uint8_t v3 = 0; - if (m_singleMode) { - // Start conversion. - fastDigitalWrite(CsPin, 0); - // Delay at least 10 usec to avoid RDY glitch on exit from Shutdown. - delay(1); - // Toggle CsPin to indicate single conversion mode. - fastDigitalWrite(CsPin, 1); - // Wait for conversion to complete. - while (1) { - delay(1); - fastDigitalWrite(CsPin, 0); - // Delay while RDY settles. - delayCycles(4); - if (!fastDigitalRead(SdoPin)) { - break; - } - fastDigitalWrite(CsPin, 1); - if (t++ > MCP355X_TO_MS) { - return MCP355X_ERR; - } - } - } else { - while (1) { - if (!fastDigitalRead(SdoPin)) { - break; - } - delay(1); - if (t++ > MCP355X_TO_MS) { - return MCP355X_ERR; - } - } - } - uint8_t v2 = readByte(); - uint8_t v1 = readByte(); - uint8_t v0 = readByte(); - - // The 25th falling edge of SCK changes SDO/RDY from Data mode to RDY mode. - uint8_t dummy = 0; - readBit(dummy, 0); - - if (m_singleMode) { - fastDigitalWrite(CsPin, 1); - } - if ((v2 & 0XE0) == 0X20) { - // Negative in range so extend sign bit. - v2 |= 0XC0; - v3 = 0XFF; - } else if (v2 & 0X40) { - // Overflow high. Cause value to be >= MCP355X_OVH. - if (v2 & 0X20) { - v2 &= 0X3F; - } - } else if (v2 & 0X80) { - // Overflow low. Cause value to be <= MCP355X_OVH. - if ((v2 & 0X20) == 0) { - v2 |= 0X40; - } - v3 = 0XFF; - } - uint16_t v_high = (v3 << 8) | v2; - uint16_t v_low = (v1 << 8) | v0; - return ((uint32_t)v_high << 16) | v_low; - } -private: - //---------------------------------------------------------------------------- - /** delay n nops - * \param[in] n nops to delay, must be a constant so compiler optimizes if(). - */ - inline __attribute__((always_inline)) - void delayCycles(uint8_t n) - { - if (n & 1) { - asm volatile("nop\n\t"); - } - if (n & 2) { - asm volatile("nop\n\t" "nop\n\t"); - } - if (n & 4) { - asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); - } - } - //---------------------------------------------------------------------------- - // Default delay yields about 2 MHz clock. - inline __attribute__((always_inline)) - void readBit(uint8_t &v, uint8_t b, uint8_t delayRead = 0) - { - fastDigitalWrite(SckPin, 0); - delayCycles(2 + delayRead); - fastDigitalWrite(SckPin, 1); - if (fastDigitalRead(SdoPin)) { - v |= (1 << b); - } - 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 \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/AvrDevelopersGpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/AvrDevelopersGpioPinMap.h new file mode 100644 index 000000000..3da106316 --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/AvrDevelopersGpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef AvrDevelopersGpioPinMap_h +#define AvrDevelopersGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 4), // D4 + GPIO_PIN(B, 5), // D5 + GPIO_PIN(B, 6), // D6 + GPIO_PIN(B, 7), // D7 + GPIO_PIN(D, 0), // D8 + GPIO_PIN(D, 1), // D9 + GPIO_PIN(D, 2), // D10 + GPIO_PIN(D, 3), // D11 + GPIO_PIN(D, 4), // D12 + GPIO_PIN(D, 5), // D13 + GPIO_PIN(D, 6), // D14 + GPIO_PIN(D, 7), // D15 + GPIO_PIN(C, 0), // D16 + GPIO_PIN(C, 1), // D17 + GPIO_PIN(C, 2), // D18 + GPIO_PIN(C, 3), // D19 + GPIO_PIN(C, 4), // D20 + GPIO_PIN(C, 5), // D21 + GPIO_PIN(C, 6), // D22 + GPIO_PIN(C, 7), // D23 + GPIO_PIN(A, 7), // D24 + GPIO_PIN(A, 6), // D25 + GPIO_PIN(A, 5), // D26 + GPIO_PIN(A, 4), // D27 + GPIO_PIN(A, 3), // D28 + GPIO_PIN(A, 2), // D29 + GPIO_PIN(A, 1), // D30 + GPIO_PIN(A, 0) // D31 +}; +#endif // AvrDevelopersGpioPinMap_h \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/BobuinoGpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/BobuinoGpioPinMap.h new file mode 100644 index 000000000..170611a55 --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/BobuinoGpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef BobuinoGpioPinMap_h +#define BobuinoGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 4), // D4 + GPIO_PIN(B, 5), // D5 + GPIO_PIN(B, 6), // D6 + GPIO_PIN(B, 7), // D7 + GPIO_PIN(D, 0), // D8 + GPIO_PIN(D, 1), // D9 + GPIO_PIN(D, 2), // D10 + GPIO_PIN(D, 3), // D11 + GPIO_PIN(D, 4), // D12 + GPIO_PIN(D, 5), // D13 + GPIO_PIN(D, 6), // D14 + GPIO_PIN(D, 7), // D15 + GPIO_PIN(C, 0), // D16 + GPIO_PIN(C, 1), // D17 + GPIO_PIN(C, 2), // D18 + GPIO_PIN(C, 3), // D19 + GPIO_PIN(C, 4), // D20 + GPIO_PIN(C, 5), // D21 + GPIO_PIN(C, 6), // D22 + GPIO_PIN(C, 7), // D23 + GPIO_PIN(A, 0), // D24 + GPIO_PIN(A, 1), // D25 + GPIO_PIN(A, 2), // D26 + GPIO_PIN(A, 3), // D27 + GPIO_PIN(A, 4), // D28 + GPIO_PIN(A, 5), // D29 + GPIO_PIN(A, 6), // D30 + GPIO_PIN(A, 7) // D31 +}; +#endif // BobuinoGpioPinMap_h \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/GpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/GpioPinMap.h new file mode 100644 index 000000000..901ad3e78 --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/GpioPinMap.h @@ -0,0 +1,64 @@ +/* Arduino DigitalIO Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the Arduino DigitalIO Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino DigitalIO Library. If not, see + * . + */ +#ifndef GpioPinMap_h +#define GpioPinMap_h +#if defined(__AVR_ATmega168__)\ +||defined(__AVR_ATmega168P__)\ +||defined(__AVR_ATmega328P__) +// 168 and 328 Arduinos +#include "UnoGpioPinMap.h" +#elif defined(__AVR_ATmega1280__)\ +|| defined(__AVR_ATmega2560__) +// Mega ADK +#include "MegaGpioPinMap.h" +#elif defined(__AVR_ATmega32U4__) +#ifdef CORE_TEENSY +#include "Teensy2GpioPinMap.h" +#else // CORE_TEENSY +// Leonardo or Yun +#include "LeonardoGpioPinMap.h" +#endif // CORE_TEENSY +#elif defined(__AVR_AT90USB646__)\ +|| defined(__AVR_AT90USB1286__) +// Teensy++ 1.0 & 2.0 +#include "Teensy2ppGpioPinMap.h" +#elif defined(__AVR_ATmega1284P__)\ +|| defined(__AVR_ATmega1284__)\ +|| defined(__AVR_ATmega644P__)\ +|| defined(__AVR_ATmega644__)\ +|| defined(__AVR_ATmega64__)\ +|| defined(__AVR_ATmega32__)\ +|| defined(__AVR_ATmega324__)\ +|| defined(__AVR_ATmega16__) +#ifdef ARDUINO_1284P_AVR_DEVELOPERS +#include "AvrDevelopersGpioPinMap.h" +#elif defined(ARDUINO_1284P_BOBUINO) +#include "BobuinoGpioPinMap.h" +#elif defined(ARDUINO_1284P_SLEEPINGBEAUTY) +#include "SleepingBeautyGpioPinMap.h" +#elif defined(ARDUINO_1284P_STANDARD) +#include "Standard1284GpioPinMap.h" +#else // ARDUINO_1284P_SLEEPINGBEAUTY +#error Undefined variant 1284, 644, 324 +#endif // ARDUINO_1284P_SLEEPINGBEAUTY +#else // 1284P, 1284, 644 +#error Unknown board type. +#endif // end all boards +#endif // GpioPinMap_h diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/LeonardoGpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/LeonardoGpioPinMap.h new file mode 100644 index 000000000..48e173453 --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/LeonardoGpioPinMap.h @@ -0,0 +1,35 @@ +#ifndef LeonardoGpioPinMap_h +#define LeonardoGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 2), // D0 + GPIO_PIN(D, 3), // D1 + GPIO_PIN(D, 1), // D2 + GPIO_PIN(D, 0), // D3 + GPIO_PIN(D, 4), // D4 + GPIO_PIN(C, 6), // D5 + GPIO_PIN(D, 7), // D6 + GPIO_PIN(E, 6), // D7 + GPIO_PIN(B, 4), // D8 + GPIO_PIN(B, 5), // D9 + GPIO_PIN(B, 6), // D10 + GPIO_PIN(B, 7), // D11 + GPIO_PIN(D, 6), // D12 + GPIO_PIN(C, 7), // D13 + GPIO_PIN(B, 3), // D14 + GPIO_PIN(B, 1), // D15 + GPIO_PIN(B, 2), // D16 + GPIO_PIN(B, 0), // D17 + GPIO_PIN(F, 7), // D18 + GPIO_PIN(F, 6), // D19 + GPIO_PIN(F, 5), // D20 + GPIO_PIN(F, 4), // D21 + GPIO_PIN(F, 1), // D22 + GPIO_PIN(F, 0), // D23 + GPIO_PIN(D, 4), // D24 + GPIO_PIN(D, 7), // D25 + GPIO_PIN(B, 4), // D26 + GPIO_PIN(B, 5), // D27 + GPIO_PIN(B, 6), // D28 + GPIO_PIN(D, 6) // D29 +}; +#endif // LeonardoGpioPinMap_h diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/MegaGpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/MegaGpioPinMap.h new file mode 100644 index 000000000..aded09a64 --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/MegaGpioPinMap.h @@ -0,0 +1,75 @@ +#ifndef MegaGpioPinMap_h +#define MegaGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(E, 0), // D0 + GPIO_PIN(E, 1), // D1 + GPIO_PIN(E, 4), // D2 + GPIO_PIN(E, 5), // D3 + GPIO_PIN(G, 5), // D4 + GPIO_PIN(E, 3), // D5 + GPIO_PIN(H, 3), // D6 + GPIO_PIN(H, 4), // D7 + GPIO_PIN(H, 5), // D8 + GPIO_PIN(H, 6), // D9 + GPIO_PIN(B, 4), // D10 + GPIO_PIN(B, 5), // D11 + GPIO_PIN(B, 6), // D12 + GPIO_PIN(B, 7), // D13 + GPIO_PIN(J, 1), // D14 + GPIO_PIN(J, 0), // D15 + GPIO_PIN(H, 1), // D16 + GPIO_PIN(H, 0), // D17 + GPIO_PIN(D, 3), // D18 + GPIO_PIN(D, 2), // D19 + GPIO_PIN(D, 1), // D20 + GPIO_PIN(D, 0), // D21 + GPIO_PIN(A, 0), // D22 + GPIO_PIN(A, 1), // D23 + GPIO_PIN(A, 2), // D24 + GPIO_PIN(A, 3), // D25 + GPIO_PIN(A, 4), // D26 + GPIO_PIN(A, 5), // D27 + GPIO_PIN(A, 6), // D28 + GPIO_PIN(A, 7), // D29 + GPIO_PIN(C, 7), // D30 + GPIO_PIN(C, 6), // D31 + GPIO_PIN(C, 5), // D32 + GPIO_PIN(C, 4), // D33 + GPIO_PIN(C, 3), // D34 + GPIO_PIN(C, 2), // D35 + GPIO_PIN(C, 1), // D36 + GPIO_PIN(C, 0), // D37 + GPIO_PIN(D, 7), // D38 + GPIO_PIN(G, 2), // D39 + GPIO_PIN(G, 1), // D40 + GPIO_PIN(G, 0), // D41 + GPIO_PIN(L, 7), // D42 + GPIO_PIN(L, 6), // D43 + GPIO_PIN(L, 5), // D44 + GPIO_PIN(L, 4), // D45 + GPIO_PIN(L, 3), // D46 + GPIO_PIN(L, 2), // D47 + GPIO_PIN(L, 1), // D48 + GPIO_PIN(L, 0), // D49 + GPIO_PIN(B, 3), // D50 + GPIO_PIN(B, 2), // D51 + GPIO_PIN(B, 1), // D52 + GPIO_PIN(B, 0), // D53 + GPIO_PIN(F, 0), // D54 + GPIO_PIN(F, 1), // D55 + GPIO_PIN(F, 2), // D56 + GPIO_PIN(F, 3), // D57 + GPIO_PIN(F, 4), // D58 + GPIO_PIN(F, 5), // D59 + GPIO_PIN(F, 6), // D60 + GPIO_PIN(F, 7), // D61 + GPIO_PIN(K, 0), // D62 + GPIO_PIN(K, 1), // D63 + GPIO_PIN(K, 2), // D64 + GPIO_PIN(K, 3), // D65 + GPIO_PIN(K, 4), // D66 + GPIO_PIN(K, 5), // D67 + GPIO_PIN(K, 6), // D68 + GPIO_PIN(K, 7) // D69 +}; +#endif // MegaGpioPinMap_h diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/SleepingBeautyGpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/SleepingBeautyGpioPinMap.h new file mode 100644 index 000000000..3dacb1f4d --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/SleepingBeautyGpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef SleepingBeautyGpioPinMap_h +#define SleepingBeautyGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 0), // D0 + GPIO_PIN(D, 1), // D1 + GPIO_PIN(D, 2), // D2 + GPIO_PIN(D, 3), // D3 + GPIO_PIN(B, 0), // D4 + GPIO_PIN(B, 1), // D5 + GPIO_PIN(B, 2), // D6 + GPIO_PIN(B, 3), // D7 + GPIO_PIN(D, 6), // D8 + GPIO_PIN(D, 5), // D9 + GPIO_PIN(B, 4), // D10 + GPIO_PIN(B, 5), // D11 + GPIO_PIN(B, 6), // D12 + GPIO_PIN(B, 7), // D13 + GPIO_PIN(C, 7), // D14 + GPIO_PIN(C, 6), // D15 + GPIO_PIN(A, 5), // D16 + GPIO_PIN(A, 4), // D17 + GPIO_PIN(A, 3), // D18 + GPIO_PIN(A, 2), // D19 + GPIO_PIN(A, 1), // D20 + GPIO_PIN(A, 0), // D21 + GPIO_PIN(D, 4), // D22 + GPIO_PIN(D, 7), // D23 + GPIO_PIN(C, 2), // D24 + GPIO_PIN(C, 3), // D25 + GPIO_PIN(C, 4), // D26 + GPIO_PIN(C, 5), // D27 + GPIO_PIN(C, 1), // D28 + GPIO_PIN(C, 0), // D29 + GPIO_PIN(A, 6), // D30 + GPIO_PIN(A, 7) // D31 +}; +#endif // SleepingBeautyGpioPinMap_h \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/Standard1284GpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/Standard1284GpioPinMap.h new file mode 100644 index 000000000..887797a6c --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/Standard1284GpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef Standard1284GpioPinMap_h +#define Standard1284GpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 4), // D4 + GPIO_PIN(B, 5), // D5 + GPIO_PIN(B, 6), // D6 + GPIO_PIN(B, 7), // D7 + GPIO_PIN(D, 0), // D8 + GPIO_PIN(D, 1), // D9 + GPIO_PIN(D, 2), // D10 + GPIO_PIN(D, 3), // D11 + GPIO_PIN(D, 4), // D12 + GPIO_PIN(D, 5), // D13 + GPIO_PIN(D, 6), // D14 + GPIO_PIN(D, 7), // D15 + GPIO_PIN(C, 0), // D16 + GPIO_PIN(C, 1), // D17 + GPIO_PIN(C, 2), // D18 + GPIO_PIN(C, 3), // D19 + GPIO_PIN(C, 4), // D20 + GPIO_PIN(C, 5), // D21 + GPIO_PIN(C, 6), // D22 + GPIO_PIN(C, 7), // D23 + GPIO_PIN(A, 0), // D24 + GPIO_PIN(A, 1), // D25 + GPIO_PIN(A, 2), // D26 + GPIO_PIN(A, 3), // D27 + GPIO_PIN(A, 4), // D28 + GPIO_PIN(A, 5), // D29 + GPIO_PIN(A, 6), // D30 + GPIO_PIN(A, 7) // D31 +}; +#endif // Standard1284GpioPinMap_h \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/Teensy2GpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/Teensy2GpioPinMap.h new file mode 100644 index 000000000..90515965b --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/Teensy2GpioPinMap.h @@ -0,0 +1,30 @@ +#ifndef Teensy2GpioPinMap_h +#define Teensy2GpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 7), // D4 + GPIO_PIN(D, 0), // D5 + GPIO_PIN(D, 1), // D6 + GPIO_PIN(D, 2), // D7 + GPIO_PIN(D, 3), // D8 + GPIO_PIN(C, 6), // D9 + GPIO_PIN(C, 7), // D10 + GPIO_PIN(D, 6), // D11 + GPIO_PIN(D, 7), // D12 + GPIO_PIN(B, 4), // D13 + GPIO_PIN(B, 5), // D14 + GPIO_PIN(B, 6), // D15 + GPIO_PIN(F, 7), // D16 + GPIO_PIN(F, 6), // D17 + GPIO_PIN(F, 5), // D18 + GPIO_PIN(F, 4), // D19 + GPIO_PIN(F, 1), // D20 + GPIO_PIN(F, 0), // D21 + GPIO_PIN(D, 4), // D22 + GPIO_PIN(D, 5), // D23 + GPIO_PIN(E, 6), // D24 +}; +#endif // Teensy2GpioPinMap_h diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/Teensy2ppGpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/Teensy2ppGpioPinMap.h new file mode 100644 index 000000000..e184f6c11 --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/Teensy2ppGpioPinMap.h @@ -0,0 +1,51 @@ +#ifndef Teensypp2GpioPinMap_h +#define Teensypp2GpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 0), // D0 + GPIO_PIN(D, 1), // D1 + GPIO_PIN(D, 2), // D2 + GPIO_PIN(D, 3), // D3 + GPIO_PIN(D, 4), // D4 + GPIO_PIN(D, 5), // D5 + GPIO_PIN(D, 6), // D6 + GPIO_PIN(D, 7), // D7 + GPIO_PIN(E, 0), // D8 + GPIO_PIN(E, 1), // D9 + GPIO_PIN(C, 0), // D10 + GPIO_PIN(C, 1), // D11 + GPIO_PIN(C, 2), // D12 + GPIO_PIN(C, 3), // D13 + GPIO_PIN(C, 4), // D14 + GPIO_PIN(C, 5), // D15 + GPIO_PIN(C, 6), // D16 + GPIO_PIN(C, 7), // D17 + GPIO_PIN(E, 6), // D18 + GPIO_PIN(E, 7), // D19 + GPIO_PIN(B, 0), // D20 + GPIO_PIN(B, 1), // D21 + GPIO_PIN(B, 2), // D22 + GPIO_PIN(B, 3), // D23 + GPIO_PIN(B, 4), // D24 + GPIO_PIN(B, 5), // D25 + GPIO_PIN(B, 6), // D26 + GPIO_PIN(B, 7), // D27 + GPIO_PIN(A, 0), // D28 + GPIO_PIN(A, 1), // D29 + GPIO_PIN(A, 2), // D30 + GPIO_PIN(A, 3), // D31 + GPIO_PIN(A, 4), // D32 + GPIO_PIN(A, 5), // D33 + GPIO_PIN(A, 6), // D34 + GPIO_PIN(A, 7), // D35 + GPIO_PIN(E, 4), // D36 + GPIO_PIN(E, 5), // D37 + GPIO_PIN(F, 0), // D38 + GPIO_PIN(F, 1), // D39 + GPIO_PIN(F, 2), // D40 + GPIO_PIN(F, 3), // D41 + GPIO_PIN(F, 4), // D42 + GPIO_PIN(F, 5), // D43 + GPIO_PIN(F, 6), // D44 + GPIO_PIN(F, 7), // D45 +}; +#endif // Teensypp2GpioPinMap_h diff --git a/hal/architecture/AVR/drivers/DigitalIO/boards/UnoGpioPinMap.h b/hal/architecture/AVR/drivers/DigitalIO/boards/UnoGpioPinMap.h new file mode 100644 index 000000000..4b82e82b9 --- /dev/null +++ b/hal/architecture/AVR/drivers/DigitalIO/boards/UnoGpioPinMap.h @@ -0,0 +1,25 @@ +#ifndef UnoGpioPinMap_h +#define UnoGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 0), // D0 + GPIO_PIN(D, 1), // D1 + GPIO_PIN(D, 2), // D2 + GPIO_PIN(D, 3), // D3 + GPIO_PIN(D, 4), // D4 + GPIO_PIN(D, 5), // D5 + GPIO_PIN(D, 6), // D6 + GPIO_PIN(D, 7), // D7 + GPIO_PIN(B, 0), // D8 + GPIO_PIN(B, 1), // D9 + GPIO_PIN(B, 2), // D10 + GPIO_PIN(B, 3), // D11 + GPIO_PIN(B, 4), // D12 + GPIO_PIN(B, 5), // D13 + GPIO_PIN(C, 0), // D14 + GPIO_PIN(C, 1), // D15 + GPIO_PIN(C, 2), // D16 + GPIO_PIN(C, 3), // D17 + GPIO_PIN(C, 4), // D18 + GPIO_PIN(C, 5) // D19 +}; +#endif // UnoGpioPinMap_h \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinBlink/DigitalPinBlink.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinBlink/DigitalPinBlink.ino deleted file mode 100644 index d8fd19f30..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinBlink/DigitalPinBlink.ino +++ /dev/null @@ -1,13 +0,0 @@ -#include -// Create object for pin 13 in output mode and demo toggle(). -DigitalPin<13> pin13(OUTPUT); - -void setup() {} - -void loop() -{ - // toggle is a two byte instruction that executes - // in two cycles or 125 ns on a 16 MHz CPU - pin13.toggle(); - delay(250); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinConfigToggle/DigitalPinConfigToggle.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinConfigToggle/DigitalPinConfigToggle.ino deleted file mode 100644 index 78a75c989..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinConfigToggle/DigitalPinConfigToggle.ino +++ /dev/null @@ -1,20 +0,0 @@ -// Demo of config() and fast toggle() function. -#include - -// Class with compile time pin number. -DigitalPin<13> pin13; - -void setup() -{ - // Set mode to OUTPUT and level LOW. - pin13.config(OUTPUT, LOW); -} -void loop() -{ - // toggle is a two byte instruction that executes - // in two cycles or 125 ns on a 16 MHz CPU - pin13.toggle(); - delay(100); - pin13.toggle(); - delay(400); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinReadWrite/DigitalPinReadWrite.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinReadWrite/DigitalPinReadWrite.ino deleted file mode 100644 index 09fb8d2ea..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinReadWrite/DigitalPinReadWrite.ino +++ /dev/null @@ -1,12 +0,0 @@ -// Read pin 12 and write value to pin 13. -#include - -DigitalPin<12> pin12(INPUT); -DigitalPin<13> pin13(OUTPUT); - -void setup() {} - -void loop() -{ - pin13 = pin12; -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinShiftOut/DigitalPinShiftOut.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinShiftOut/DigitalPinShiftOut.ino deleted file mode 100644 index 6a31ee6eb..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/DigitalPinShiftOut/DigitalPinShiftOut.ino +++ /dev/null @@ -1,43 +0,0 @@ -// Scope test for fast shiftOut function. -#include - -// Create clockPin in output mode with inital level LOW. -DigitalPin<12> clockPin(OUTPUT, LOW); - -// Create dataPin in output mode with inital level HIGH. -DigitalPin<13> dataPin(OUTPUT, HIGH); - -//------------------------------------------------------------------------------ -// Time to send one bit is ten cycles or 625 ns for 16 MHz CPU. -inline __attribute__((always_inline)) -void sendBit(uint8_t bit, uint8_t data) -{ - dataPin = data & (1 << bit); - clockPin = 1; - // may want a nop here - clock pulse is 125 ns wide - clockPin = 0; -} -//------------------------------------------------------------------------------ -// Time to send one byte is 5 usec. -void shiftOut(uint8_t bits) -{ - sendBit(7, bits); - sendBit(6, bits); - sendBit(5, bits); - sendBit(4, bits); - sendBit(3, bits); - sendBit(2, bits); - sendBit(1, bits); - sendBit(0, bits); -} -//------------------------------------------------------------------------------ -void setup() -{ - // Not used. -} -//------------------------------------------------------------------------------ -void loop() -{ - shiftOut(0X55); - delay(2); -} diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOBegin/PinIOBegin.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOBegin/PinIOBegin.ino deleted file mode 100644 index 0ddcb1e05..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOBegin/PinIOBegin.ino +++ /dev/null @@ -1,26 +0,0 @@ -// Use begin to assign pin numbers. -// Read pin 12 and write the value to pin 13. -#include - -// Declare the PinIO instances. -PinIO readPin; -PinIO writePin; -//------------------------------------------------------------------------------ -void setup() -{ - // 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. - writePin.begin(13); - writePin.mode(OUTPUT); -} -//------------------------------------------------------------------------------ -void loop() -{ - // Copy the value read from readPin to writePin. - writePin.write(readPin.read()); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOConfigToggle/PinIOConfigToggle.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOConfigToggle/PinIOConfigToggle.ino deleted file mode 100644 index b9b981845..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOConfigToggle/PinIOConfigToggle.ino +++ /dev/null @@ -1,18 +0,0 @@ -// Test the config() and toggle() functions. -#include - -// Set runtime pin number. -PinIO pin13(13); - -void setup() -{ - // set mode to OUTPUT and level LOW - pin13.config(OUTPUT, LOW); -} -void loop() -{ - pin13.toggle(); - delay(100); - pin13.toggle(); - delay(400); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOReadWrite/PinIOReadWrite.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOReadWrite/PinIOReadWrite.ino deleted file mode 100644 index 3019a05a4..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/PinIOReadWrite/PinIOReadWrite.ino +++ /dev/null @@ -1,18 +0,0 @@ -// Read pin 12 and write the value to pin 13. -#include - -PinIO readPin(12); -PinIO writePin(13); - -void setup() -{ - // Set input mode and disable pull-up. - readPin.config(INPUT, LOW); - - // set output mode - writePin.mode(OUTPUT); -} -void loop() -{ - writePin.write(readPin.read()); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/ScanI2cBus/ScanI2cBus.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/ScanI2cBus/ScanI2cBus.ino deleted file mode 100644 index 18e5b6e11..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/ScanI2cBus/ScanI2cBus.ino +++ /dev/null @@ -1,85 +0,0 @@ -/* Scan I2C bus for devices. - * - * For I2C write addresses: the scanner sends a start, - * the write address, looks for an ACK, then sends a stop. - * - * For I2C read addresses: the scanner sends a start, the - * read address, looks for an ACK. If there is an ACK, it - * reads a byte and NACKs it. Finally it sends a stop. - * - * When the I2C chip responds to the read address, it - * outputs data and will miss a stop condition sent - * immediately after the read address (bus contention). - * - * If the I2C chip misses the stop condition, the - * address scanner will see ghost addresses until the - * read ends randomly. By reading a byte after any read - * address that ACKs, there is a chance to NACK the read - * and properly end the I2C transaction. - */ -#include - -// Set pin numbers for your configuration. -const uint8_t SDA_PIN = A4; -const uint8_t SCL_PIN = A5; - -SoftI2cMaster i2c(SCL_PIN, SDA_PIN); -//FastI2cMaster i2c; -//------------------------------------------------------------------------------ -void setup() -{ - - Serial.begin(9600); - while (!Serial); - - if (!digitalRead(SDA_PIN) && !digitalRead(SCL_PIN)) { - Serial.println("External pull-up resistors appear to be missing."); - Serial.println("Many false responses may be detected."); - Serial.println("Type any character to continue."); - - while (!Serial.available()); - Serial.println(); - - } - uint8_t add = 0; - bool found = false; - do { - bool wr = i2c.transfer(add | I2C_WRITE, 0, 0); - bool rd = i2c.transfer(add | I2C_READ, 0, 0, I2C_CONTINUE); - if (rd) { - uint8_t dummy; - // Must read byte, send NACK, and issue STOP. - i2c.transferContinue(&dummy, 1); - } - if (rd || wr) { - found = true; - Serial.print("Device at address: 0X"); - Serial.print(add, HEX); - Serial.print(" responds to "); - if (rd) { - Serial.print("Read"); - } - if (rd && wr) { - Serial.print(" and "); - } - if (wr) { - Serial.print("Write"); - } - Serial.println('.'); - } - add += 2; - } while (add); - - if (!found) { - Serial.println("No devices found."); - } - Serial.println("Done"); -} -//------------------------------------------------------------------------------ -void loop() -{ - // Not used. -} - - - diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/SoftDS1307Utility/SoftDS1307Utility.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/SoftDS1307Utility/SoftDS1307Utility.ino deleted file mode 100644 index 3009db79a..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/SoftDS1307Utility/SoftDS1307Utility.ino +++ /dev/null @@ -1,365 +0,0 @@ -// Utility sketch to explore DS1307 and demonstrate software I2C Master. -// - -#include - -// Set pin numbers for your configuration. -const uint8_t SDA_PIN = A4; -const uint8_t SCL_PIN = A5; - -//SoftI2cMaster rtc(SCL_PIN, SDA_PIN); -FastI2cMaster rtc; - -// i2c 8-bit address for DS1307. low bit is read/write -#define DS1307ADDR 0XD0 -//------------------------------------------------------------------------------ -/* - * Read 'count' bytes from the DS1307 starting at 'address'. - */ -uint8_t readDS1307(uint8_t address, uint8_t *buf, uint8_t count) -{ - - // Send address of data. - if (!rtc.transfer(DS1307ADDR | I2C_WRITE, &address, 1)) { - return false; - } - - // Read data. - return rtc.transfer(DS1307ADDR | I2C_READ, buf, count); -} -//------------------------------------------------------------------------------ -/* - * write 'count' bytes to DS1307 starting at 'address'. - */ -uint8_t writeDS1307(uint8_t address, uint8_t *buf, uint8_t count) -{ - - // Write address and continue transfer for write of data. - if (!rtc.transfer(DS1307ADDR | I2C_WRITE, &address, 1, I2C_CONTINUE)) { - return false; - } - // Write data. - return rtc.transferContinue(buf, count); -} -//------------------------------------------------------------------------------ -void setup(void) -{ - Serial.begin(9600); -} -//------------------------------------------------------------------------------ -/** Store and print a string in flash memory.*/ -#define PgmPrint(x) Serial.print(F(x)) -/** Store and print a string in flash memory followed by a CR/LF.*/ -#define PgmPrintln(x) Serial.println(F(x)) -//------------------------------------------------------------------------------- -// Day of week U.S. convention. -char *Ddd[] = {"Bad", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; -//------------------------------------------------------------------------------ -void hexPrint(uint8_t v) -{ - Serial.print(v >> 4, HEX); - Serial.print(v & 0XF, HEX); -} -//------------------------------------------------------------------------------ -void hexPrintln(uint8_t v) -{ - hexPrint(v); - Serial.println(); -} -//------------------------------------------------------------------------------ -// read hex input -uint8_t hexRead(uint16_t* v) -{ - bool valid = false; - uint16_t n = 0; - while (!Serial.available()); - while (Serial.available()) { - uint8_t c = Serial.read(); - // Exit if end-of-line. - if (c == '\n' || c == '\r') { - delay(10); - // If CR/LF. - Serial.read(); - break; - } - n <<= 4; - if ('a' <= c && c <= 'f') { - n += c - ('a' - 10); - } else if ('A' <= c && c <= 'F') { - n += c - ('A' - 10); - } else if ('0' <= c && c <= '9') { - n += c - '0'; - } else { - valid = false; - break; - } - delay(10); - valid = true; - } - if (!valid) { - PgmPrintln("Invalid entry"); - return false; - } - *v = n; - return true; -} -//------------------------------------------------------------------------------ -uint8_t bcdRead(uint8_t min, uint8_t max, uint8_t* n) -{ - uint16_t v; - if (!hexRead(&v)) { - return false; - } - uint8_t d = 10 * (v >> 4) + (v & 0XF); - if ((v >> 4) > 9 || (v & 0XF) > 9 || d < min || d > max) { - PgmPrintln("Invalid"); - return false; - } - *n = v; - return true; -} -//------------------------------------------------------------------------------ -void displayTime(void) -{ - uint8_t r[8]; - if (!readDS1307(0, r, 8)) { - PgmPrintln("Read Failed for display time"); - return; - } - PgmPrint("The current time is 20"); - // year - hexPrint(r[6]); - Serial.write('-'); - - // month - hexPrint(r[5]); - Serial.write('-'); - - // day - hexPrint(r[4]); - Serial.write(' '); - - Serial.print(Ddd[r[3] < 8 ? r[3] : 0]); - Serial.write(' '); - - // hour - hexPrint(r[2]); - Serial.write(':'); - - // minute - hexPrint(r[1]); - Serial.write(':'); - - // second - hexPrintln(r[0]); - - // Control register. - PgmPrint("Control Register: "); - hexPrintln(r[7]); -} -//------------------------------------------------------------------------------ -// dump registers and 56 bytes of RAM -void dumpAll(void) -{ - uint8_t buf[8]; - for (uint8_t a = 0; a < 64; a += 8) { - hexPrint(a); - Serial.write(' '); - if (!readDS1307(a, buf, 8)) { - PgmPrint("read failed for dumpAll"); - return; - } - for (uint8_t i = 0; i < 8; i++) { - Serial.write(' '); - hexPrint(buf[i]); - } - Serial.println(); - } -} -//------------------------------------------------------------------------------ -void fillNvRam(void) -{ - PgmPrint("Enter HEX value for all NV RAM locations (00-FF): "); - uint16_t v; - if (!hexRead(&v)) { - return; - } - hexPrint(v); - for (uint8_t a = 8; a < 64; a ++) { - if (!writeDS1307(a, (uint8_t *)&v, 1)) { - PgmPrintln("write failed for fillNvRam"); - } - } -} -//------------------------------------------------------------------------------ -// set control register -/* -The DS1307 control register is used to control the operation of the SQW/OUT pin. -+-----------------------------------------------+ -|BIT 7|BIT 6|BIT 5|BIT 4|BIT 3|BIT 2|BIT 1|BIT 0| -+-----------------------------------------------+ -|OUT | 0 | 0 |SQWE | 0 | 0 | RS1 | RS0 | -+-----------------------------------------------+ - -OUT (Output control): This bit controls the output level of the SQW/OUT pin -when the square wave output is disabled. If SQWE = 0, the logic level on the -SQW/OUT pin is 1 if OUT = 1 and is 0 if OUT = 0. - -SQWE (Square Wave Enable): This bit, when set to a logic 1, will enable the -oscillator output. The frequency of the square wave output depends upon the -value of the RS0 and RS1 bits. With the square wave output set to 1Hz, the -clock registers update on the falling edge of the square wave. - -Square wave Output Frequency for SQWE = 1. -RS1 RS0 FREQUENCY - 0 0 1Hz - 0 1 4.096kHz - 1 0 8.192kHz - 1 1 32.768kHz -*/ -void setControl(void) -{ - PgmPrintln("SQW/OUT pin: "); - PgmPrintln("(00) Low"); - PgmPrintln("(10) 1Hz"); - PgmPrintln("(11) 4.096kHz"); - PgmPrintln("(12) 8.192kHz"); - PgmPrintln("(13) 32.768kHz"); - PgmPrintln("(80) High"); - PgmPrint("Enter control: "); - uint16_t r; - if (!hexRead(&r)) { - return; - } - hexPrintln(r); - if (!writeDS1307(7, (uint8_t *)&r, 1)) { - PgmPrint("Write Failed for setControl"); - } -} -//------------------------------------------------------------------------------ -void setDate(void) -{ - uint8_t r[4]; - PgmPrint("Enter year (00-99): "); - if (!bcdRead(0, 99, &r[3])) { - return; - } - hexPrintln(r[3]); - PgmPrint("Enter month (01-12): "); - if (!bcdRead(1, 12, &r[2])) { - return; - } - hexPrintln(r[2]); - PgmPrint("Enter day (01-31): "); - if (!bcdRead(1, 31, &r[1])) { - return; - } - hexPrintln(r[1]); - PgmPrint("Enter day of week (01-07, Sun-Sat): "); - if (!bcdRead(1, 7, &r[0])) { - return; - } - hexPrintln(r[0]); - - if (!writeDS1307(3, r, 4)) { - PgmPrintln("Write failed for setDate"); - } -} -//------------------------------------------------------------------------------ -void setNvRam() -{ - uint8_t u; - uint16_t a, v; - while (true) { - PgmPrint("Enter zero to quit or Hex address (8-3F):"); - if (!hexRead(&a) || a == 0) { - return; - } - if (a < 8 || a > 0X3F) { - PgmPrintln("Invalid address"); - return; - } - hexPrintln(a); - if (!readDS1307(a, &u, 1)) { - PgmPrintln("Read failed"); - return; - } - PgmPrint("Current value: "); - hexPrintln(u); - PgmPrint("Enter new HEX value (00-FF): "); - if (!hexRead(&v)) { - return; - } - hexPrintln(v); - if (!writeDS1307(a, (uint8_t *)&v, 1)) { - PgmPrint("Write Failed"); - return; - } - } -} -//------------------------------------------------------------------------------ -void setTime(void) -{ - uint8_t r[3]; - PgmPrint("Enter hours (00-23): "); - if (!bcdRead(0, 23, &r[2])) { - return; - } - hexPrintln(r[2]); - PgmPrint("Enter minutes (00-59): "); - if (!bcdRead(0, 59, &r[1])) { - return; - } - hexPrintln(r[1]); - PgmPrint("Enter seconds (00-59): "); - if (!bcdRead(0, 59, &r[0])) { - return; - } - hexPrintln(r[0]); - - if (!writeDS1307(0, r, 3)) { - PgmPrintln("write failed in setTime"); - return; - } -} -//------------------------------------------------------------------------------ -void loop(void) -{ - Serial.println(); - displayTime(); - while (Serial.read() >= 0) {} - PgmPrintln("\nOptions are:"); - PgmPrintln("(0) Display date and time"); - PgmPrintln("(1) Set time"); - PgmPrintln("(2) Set date"); - PgmPrintln("(3) Set Control"); - PgmPrintln("(4) Dump all"); - PgmPrintln("(5) Fill NV RAM"); - PgmPrintln("(6) Set NV RAM value"); - PgmPrint("Enter option: "); - - uint16_t n; - if (!hexRead(&n)) { - return; - } - Serial.println(n, DEC); - if (n == 0) { - return; - } - Serial.println(); - if (n == 1) { - setTime(); - } else if (n == 2) { - setDate(); - } else if (n == 3) { - setControl(); - } else if (n == 4) { - dumpAll(); - } else if (n == 5) { - fillNvRam(); - } else if (n == 6) { - setNvRam(); - } else { - PgmPrintln("Invalid option"); - } -} diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/testArduino/testArduino.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/testArduino/testArduino.ino deleted file mode 100644 index 144c7fb3a..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/testArduino/testArduino.ino +++ /dev/null @@ -1,16 +0,0 @@ -// Scope test for write timing with Arduino digitaWrite(). -const uint8_t PIN13 = 13; - -void setup() -{ - // Set mode to OUTPUT. - pinMode(PIN13, OUTPUT); -} -void loop() -{ - digitalWrite(PIN13, HIGH); - digitalWrite(PIN13, LOW); - digitalWrite(PIN13, HIGH); - digitalWrite(PIN13, LOW); - delay(1); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/testDigitalPin/testDigitalPin.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/testDigitalPin/testDigitalPin.ino deleted file mode 100644 index 5b4f562aa..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/testDigitalPin/testDigitalPin.ino +++ /dev/null @@ -1,21 +0,0 @@ -// Scope test for write timing of DigitalPin functions. -#include - -// Class with compile time pin number. -DigitalPin<13> pin13; - -void setup() -{ - // set mode to OUTPUT - pin13.mode(OUTPUT); -} -void loop() -{ - pin13.high(); - pin13.low(); - pin13.write(1); - pin13.write(0); - pin13.toggle(); - pin13.toggle(); - delay(1); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/testFastDigital/testFastDigital.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/testFastDigital/testFastDigital.ino deleted file mode 100644 index 6fca39ab3..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/testFastDigital/testFastDigital.ino +++ /dev/null @@ -1,15 +0,0 @@ -// Test fastDigital function timing with a scope. -#include -const uint8_t PIN = 13; -void setup() -{ - fastPinMode(PIN, OUTPUT); -} -void loop() -{ - fastDigitalWrite(PIN, HIGH); - fastDigitalWrite(PIN, LOW); - fastDigitalToggle(PIN); - fastDigitalToggle(PIN); - delay(1); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/testPinIO/testPinIO.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/testPinIO/testPinIO.ino deleted file mode 100644 index 3cf8a8516..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/testPinIO/testPinIO.ino +++ /dev/null @@ -1,21 +0,0 @@ -// Scope test for PinIO class write timing. -#include - -// Class with runtime pin numbers. -PinIO pin(13); - -void setup() -{ - // Set mode to OUTPUT. - pin.mode(OUTPUT); -} -void loop() -{ - pin.high(); - pin.low(); - pin.write(1); - pin.write(0); - pin.toggle(); - pin.toggle(); - delay(1); -} \ No newline at end of file diff --git a/hal/architecture/AVR/drivers/DigitalIO/examples/testSoftSPI/testSoftSPI.ino b/hal/architecture/AVR/drivers/DigitalIO/examples/testSoftSPI/testSoftSPI.ino deleted file mode 100644 index f457a3fcb..000000000 --- a/hal/architecture/AVR/drivers/DigitalIO/examples/testSoftSPI/testSoftSPI.ino +++ /dev/null @@ -1,43 +0,0 @@ -// Scope test for development - assumes 328 processor. -#include - -const uint8_t SOFT_SPI_MISO_PIN = 7; -const uint8_t SOFT_SPI_MOSI_PIN = 8; -const uint8_t SOFT_SPI_SCK_PIN = 9; -const uint8_t SPI_MODE = 0; - -SoftSPI spi; - -int test; -void setup() -{ - Serial.begin(9600); - spi.begin(); - while(1) { - Serial.println("Enter:"); - Serial.println("R - Receive"); - Serial.println("S - Send"); - Serial.println("T - Transfer"); - while ((test = Serial.read()) <= 0) {} - test = toupper(test); - if (strchr("RST", test)) { - break; - } - Serial.println("Invalid entry"); - }; - Serial.print("Starting test "); - Serial.println((char)test); -} -void loop() -{ - if (test == 'S') { - spi.send(0X55); - } - if (test == 'R') { - Serial.println(spi.receive(), HEX); - } - if (test == 'T') { - Serial.println(spi.transfer(0XAA), HEX); - } - delay(10); -} \ No newline at end of file diff --git a/hal/architecture/ESP32/MyHwESP32.cpp b/hal/architecture/ESP32/MyHwESP32.cpp index 0dbd70b69..a7a0a599d 100644 --- a/hal/architecture/ESP32/MyHwESP32.cpp +++ b/hal/architecture/ESP32/MyHwESP32.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -142,33 +142,3 @@ uint16_t hwFreeMem(void) { return static_cast(ESP.getFreeHeap()); } - -void hwDebugPrint(const char *fmt, ...) -{ -#ifndef MY_DISABLED_SERIAL - char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; -#ifdef MY_GATEWAY_SERIAL - // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) - snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), - C_INTERNAL, I_LOG_MESSAGE, hwMillis()); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - // prepend timestamp - MY_DEBUGDEVICE.print(hwMillis()); - MY_DEBUGDEVICE.print(F(" ")); -#endif - va_list args; - va_start(args, fmt); - vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); -#ifdef MY_GATEWAY_SERIAL - // Truncate message if this is gateway node - fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; - fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; -#endif - va_end(args); - MY_DEBUGDEVICE.print(fmtBuffer); - MY_DEBUGDEVICE.flush(); -#else - (void)fmt; -#endif -} diff --git a/hal/architecture/ESP32/MyHwESP32.h b/hal/architecture/ESP32/MyHwESP32.h index 420249546..49b855a31 100644 --- a/hal/architecture/ESP32/MyHwESP32.h +++ b/hal/architecture/ESP32/MyHwESP32.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -40,6 +40,7 @@ #include #include "EEPROM.h" +#include #ifdef __cplusplus #include @@ -73,6 +74,7 @@ #define hwMillis() millis() #define hwMicros() micros() #define hwRandomNumberInit() randomSeed(esp_random()) +#define hwGetSleepRemaining() (0ul) bool hwInit(void); void hwReadConfigBlock(void *buf, void *addr, size_t length); @@ -82,6 +84,12 @@ uint8_t hwReadConfig(const int addr); ssize_t hwGetentropy(void *__buffer, size_t __length); #define MY_HW_HAS_GETENTROPY +// SOFTSPI +#ifdef MY_SOFTSPI +#error Soft SPI is not available on this architecture! +#endif +#define hwSPI SPI //!< hwSPI + /** * Restore interrupt state. * Helper function for MY_CRITICAL_SECTION. diff --git a/hal/architecture/ESP32/MyMainESP32.cpp b/hal/architecture/ESP32/MyMainESP32.cpp index 21df14d3f..6939f2f8c 100644 --- a/hal/architecture/ESP32/MyMainESP32.cpp +++ b/hal/architecture/ESP32/MyMainESP32.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -20,8 +20,11 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "esp_task_wdt.h" #include "Arduino.h" +TaskHandle_t loopTaskHandle = NULL; + #if CONFIG_AUTOSTART_ARDUINO #if CONFIG_FREERTOS_UNICORE @@ -30,20 +33,25 @@ #define ARDUINO_RUNNING_CORE 1 #endif -void MySensorsTask(void *pvParameters) +bool loopTaskWDTEnabled; + +void loopTask(void *pvParameters) { _begin(); // Startup MySensors library - for (;;) { - micros(); // update overflow + for(;;) { + if(loopTaskWDTEnabled) { + esp_task_wdt_reset(); + } _process(); // Process incoming data - loop(); // Call sketch loop + loop(); } } extern "C" void app_main() { + loopTaskWDTEnabled = false; initArduino(); - xTaskCreatePinnedToCore(MySensorsTask, "MySensorsTask", 8192, NULL, 1, NULL, ARDUINO_RUNNING_CORE); + xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE); } #endif diff --git a/hal/architecture/ESP8266/MyHwESP8266.cpp b/hal/architecture/ESP8266/MyHwESP8266.cpp index 716d8a382..75a149b53 100644 --- a/hal/architecture/ESP8266/MyHwESP8266.cpp +++ b/hal/architecture/ESP8266/MyHwESP8266.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,7 +18,6 @@ */ #include "MyHwESP8266.h" -#include bool hwInit(void) { @@ -99,7 +98,7 @@ int8_t hwSleep(uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) +int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms) { // TODO: Not supported! (void)interrupt; @@ -108,7 +107,8 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, +int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, + const uint8_t mode2, uint32_t ms) { // TODO: Not supported! @@ -154,34 +154,3 @@ uint16_t hwFreeMem(void) { return ESP.getFreeHeap(); } - -void hwDebugPrint(const char *fmt, ... ) -{ -#ifndef MY_DISABLED_SERIAL - char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; -#ifdef MY_GATEWAY_SERIAL - // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) - snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), - C_INTERNAL, I_LOG_MESSAGE, hwMillis()); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - // prepend timestamp - MY_DEBUGDEVICE.print(hwMillis()); - MY_DEBUGDEVICE.print(" "); -#endif - va_list args; - va_start(args, fmt); - // cppcheck-suppress wrongPrintfScanfArgNum - vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); -#ifdef MY_GATEWAY_SERIAL - // Truncate message if this is gateway node - fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; - fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; -#endif - va_end(args); - MY_DEBUGDEVICE.print(fmtBuffer); - MY_DEBUGDEVICE.flush(); -#else - (void)fmt; -#endif -} diff --git a/hal/architecture/ESP8266/MyHwESP8266.h b/hal/architecture/ESP8266/MyHwESP8266.h index f0486b88b..5b6f157fc 100644 --- a/hal/architecture/ESP8266/MyHwESP8266.h +++ b/hal/architecture/ESP8266/MyHwESP8266.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,6 +19,11 @@ #ifndef MyHwESP8266_h #define MyHwESP8266_h +#include +#include +#include +#include + #ifdef __cplusplus #include #endif @@ -44,6 +49,7 @@ #define hwMillis() millis() // The use of randomSeed switch to pseudo random number. Keep hwRandomNumberInit empty #define hwRandomNumberInit() +#define hwGetSleepRemaining() (0ul) bool hwInit(void); void hwReadConfigBlock(void *buf, void *addr, size_t length); @@ -53,6 +59,13 @@ uint8_t hwReadConfig(const int addr); ssize_t hwGetentropy(void *__buffer, size_t __length); //#define MY_HW_HAS_GETENTROPY +// SOFTSPI +#ifdef MY_SOFTSPI +#error Soft SPI is not available on this architecture! +#endif +#define hwSPI SPI //!< hwSPI + + /** * Restore interrupt state. * Helper function for MY_CRITICAL_SECTION. diff --git a/hal/architecture/ESP8266/MyMainESP8266.cpp b/hal/architecture/ESP8266/MyMainESP8266.cpp index 34cccf5da..4b7298a05 100644 --- a/hal/architecture/ESP8266/MyMainESP8266.cpp +++ b/hal/architecture/ESP8266/MyMainESP8266.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -45,10 +45,10 @@ extern void(*__init_array_end)(void); /* Not static, used in Esp.cpp */ struct rst_info resetInfo; -/* Not static, used in core_esp8266_postmortem.c. -* Placed into noinit section because we assign value to this variable -* before .bss is zero-filled, and need to preserve the value. -*/ +/* Not static, used in core_esp8266_postmortem.c and other places. + * Placed into noinit section because we assign value to this variable + * before .bss is zero-filled, and need to preserve the value. + */ cont_t* g_pcont __attribute__((section(".noinit"))); /* Event queue used by the main (arduino) task */ @@ -57,6 +57,13 @@ static os_event_t s_loop_queue[LOOP_QUEUE_SIZE]; /* Used to implement optimistic_yield */ static uint32_t s_micros_at_task_start; +/* For ets_intr_lock_nest / ets_intr_unlock_nest + * Max nesting seen by SDK so far is 2. + */ +#define ETS_INTR_LOCK_NEST_MAX 7 +static uint16_t ets_intr_lock_stack[ETS_INTR_LOCK_NEST_MAX]; +static byte ets_intr_lock_stack_ptr = 0; + extern "C" { extern const uint32_t __attribute__((section(".ver_number"))) core_version = @@ -83,24 +90,36 @@ void preloop_update_frequency() #endif } +extern "C" bool can_yield() +{ + return cont_can_yield(g_pcont); +} + +static inline void esp_yield_within_cont() __attribute__((always_inline)); +static void esp_yield_within_cont() +{ + cont_yield(g_pcont); + run_scheduled_recurrent_functions(); +} extern "C" void esp_yield() { - if (cont_can_yield(g_pcont)) { - cont_yield(g_pcont); + if (can_yield()) { + esp_yield_within_cont(); } } extern "C" void esp_schedule() { + // always on CONT stack here ets_post(LOOP_TASK_PRIORITY, 0, 0); } extern "C" void __yield() { - if (cont_can_yield(g_pcont)) { + if (can_yield()) { esp_schedule(); - esp_yield(); + esp_yield_within_cont(); } else { panic(); } @@ -110,12 +129,54 @@ extern "C" void yield(void) __attribute__((weak, alias("__yield"))); extern "C" void optimistic_yield(uint32_t interval_us) { - if (cont_can_yield(g_pcont) && + if (can_yield() && (system_get_time() - s_micros_at_task_start) > interval_us) { yield(); } } + +// Replace ets_intr_(un)lock with nestable versions +extern "C" void IRAM_ATTR ets_intr_lock() +{ + if (ets_intr_lock_stack_ptr < ETS_INTR_LOCK_NEST_MAX) { + ets_intr_lock_stack[ets_intr_lock_stack_ptr++] = xt_rsil(3); + } else { + xt_rsil(3); + } +} + +extern "C" void IRAM_ATTR ets_intr_unlock() +{ + if (ets_intr_lock_stack_ptr > 0) { + xt_wsr_ps(ets_intr_lock_stack[--ets_intr_lock_stack_ptr]); + } else { + xt_rsil(0); + } +} + + +// Save / Restore the PS state across the rom ets_post call as the rom code +// does not implement this correctly. +extern "C" bool ets_post_rom(uint8 prio, ETSSignal sig, ETSParam par); + +extern "C" bool IRAM_ATTR ets_post(uint8 prio, ETSSignal sig, ETSParam par) +{ + uint32_t saved; + asm volatile ("rsr %0,ps":"=a" (saved)); + bool rc = ets_post_rom(prio, sig, par); + xt_wsr_ps(saved); + return rc; +} + +extern "C" void __loop_end(void) +{ + run_scheduled_functions(); + run_scheduled_recurrent_functions(); +} + +extern "C" void loop_end(void) __attribute__((weak, alias("__loop_end"))); + static void loop_wrapper() { static bool setup_done = false; @@ -139,88 +200,131 @@ static void loop_task(os_event_t *events) panic(); } } +extern "C" { + + struct object { + long placeholder[10]; + }; + void __register_frame_info(const void *begin, struct object *ob); + extern char __eh_frame[]; +} static void do_global_ctors(void) { + static struct object ob; + __register_frame_info(__eh_frame, &ob); + void(**p)(void) = &__init_array_end; while (p != &__init_array_start) { (*--p)(); } } +extern "C" { + extern void __unhandled_exception(const char *str); + + static void __unhandled_exception_cpp() + { +#ifndef __EXCEPTIONS + abort(); +#else + static bool terminating; + if (terminating) { + abort(); + } + terminating = true; + /* Use a trick from vterminate.cc to get any std::exception what() */ + try { + __throw_exception_again; + } catch (const std::exception& e) { + __unhandled_exception(e.what()); + } catch (...) { + __unhandled_exception(""); + } +#endif + } + +} + void init_done() { system_set_os_print(1); gdb_init(); + std::set_terminate(__unhandled_exception_cpp); do_global_ctors(); esp_schedule(); } /* This is the entry point of the application. -* It gets called on the default stack, which grows down from the top -* of DRAM area. -* .bss has not been zeroed out yet, but .data and .rodata are in place. -* Cache is not enabled, so only ROM and IRAM functions can be called. -* Peripherals (except for SPI0 and UART0) are not initialized. -* This function does not return. -*/ + * It gets called on the default stack, which grows down from the top + * of DRAM area. + * .bss has not been zeroed out yet, but .data and .rodata are in place. + * Cache is not enabled, so only ROM and IRAM functions can be called. + * Peripherals (except for SPI0 and UART0) are not initialized. + * This function does not return. + */ /* -A bit of explanation for this entry point: - -SYS is the SDK task/context used by the upperlying system to run its -administrative tasks (at least WLAN and lwip's receive callbacks and -Ticker). NONOS-SDK is designed to run user's non-threaded code in -another specific task/context with its own stack in BSS. - -Some clever fellows found that the SYS stack was a large and quite unused -piece of ram that we could use for the user's stack instead of using user's -main memory, thus saving around 4KB on ram/heap. - -A problem arose later, which is that this stack can heavily be used by -the SDK for some features. One of these features is WPS. We still don't -know if other features are using this, or if this memory is going to be -used in future SDK releases. - -WPS beeing flawed by its poor security, or not beeing used by lots of -users, it has been decided that we are still going to use that memory for -user's stack and disable the use of WPS, with an option to revert that -back at the user's discretion. This selection can be done with the -global define NO_EXTRA_4K_HEAP. An option has been added to the board -generator script. - -References: -https://github.com/esp8266/Arduino/pull/4553 -https://github.com/esp8266/Arduino/pull/4622 -https://github.com/esp8266/Arduino/issues/4779 -https://github.com/esp8266/Arduino/pull/4889 + A bit of explanation for this entry point: -*/ + SYS is the SDK task/context used by the upperlying system to run its + administrative tasks (at least WLAN and lwip's receive callbacks and + Ticker). NONOS-SDK is designed to run user's non-threaded code in + another specific task/context with its own stack in BSS. -#ifdef NO_EXTRA_4K_HEAP -/* this is the default NONOS-SDK user's heap location */ -cont_t g_cont __attribute__((aligned(16))); -#endif + Some clever fellows found that the SYS stack was a large and quite unused + piece of ram that we could use for the user's stack instead of using user's + main memory, thus saving around 4KB on ram/heap. -extern "C" void ICACHE_RAM_ATTR app_entry(void) -{ -#ifdef NO_EXTRA_4K_HEAP + A problem arose later, which is that this stack can heavily be used by + the SDK for some features. One of these features is WPS. We still don't + know if other features are using this, or if this memory is going to be + used in future SDK releases. - /* this is the default NONOS-SDK user's heap location */ - g_pcont = &g_cont; + WPS beeing flawed by its poor security, or not beeing used by lots of + users, it has been decided that we are still going to use that memory for + user's stack and disable the use of WPS. -#else + app_entry() jumps to app_entry_custom() defined as "weakref" calling + itself a weak customizable function, allowing to use another one when + this is required (see core_esp8266_app_entry_noextra4k.cpp, used by WPS). + (note: setting app_entry() itself as "weak" is not sufficient and always + ends up with the other "noextra4k" one linked, maybe because it has a + default ENTRY(app_entry) value in linker scripts). + + References: + https://github.com/esp8266/Arduino/pull/4553 + https://github.com/esp8266/Arduino/pull/4622 + https://github.com/esp8266/Arduino/issues/4779 + https://github.com/esp8266/Arduino/pull/4889 + +*/ + +extern "C" void app_entry_redefinable(void) __attribute__((weak)); +extern "C" void app_entry_redefinable(void) +{ /* Allocate continuation context on this SYS stack, - and save pointer to it. */ + and save pointer to it. */ cont_t s_cont __attribute__((aligned(16))); g_pcont = &s_cont; -#endif - /* Call the entry point of the SDK code. */ call_user_start(); } +static void app_entry_custom(void) __attribute__((weakref("app_entry_redefinable"))); + +extern "C" void app_entry(void) +{ + return app_entry_custom(); +} + +extern "C" void preinit(void) __attribute__((weak)); +extern "C" void preinit(void) +{ + /* do nothing by default */ +} + extern "C" void user_init(void) { struct rst_info *rtc_info_ptr = system_get_rst_info(); @@ -228,12 +332,14 @@ extern "C" void user_init(void) uart_div_modify(0, UART_CLK_FREQ / (115200)); - init(); + init(); // in core_esp8266_wiring.c, inits hw regs and sdk timer initVariant(); cont_init(g_pcont); + preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. + ets_task(loop_task, LOOP_TASK_PRIORITY, s_loop_queue, LOOP_QUEUE_SIZE); diff --git a/hal/architecture/Linux/MyHwLinuxGeneric.cpp b/hal/architecture/Linux/MyHwLinuxGeneric.cpp index 6b5dd8d92..351d248b1 100644 --- a/hal/architecture/Linux/MyHwLinuxGeneric.cpp +++ b/hal/architecture/Linux/MyHwLinuxGeneric.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,16 +19,6 @@ #include "MyHwLinuxGeneric.h" -#include -#include -#include -#include -#include -#include -#include "SoftEeprom.h" -#include "log.h" -#include "config.h" - static SoftEeprom eeprom; static FILE *randomFp = NULL; @@ -113,7 +103,7 @@ int8_t hwSleep(uint32_t ms) } // Not supported! -int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) +int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms) { (void)interrupt; (void)mode; @@ -123,7 +113,8 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) } // Not supported! -int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, +int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, + const uint8_t mode2, uint32_t ms) { (void)interrupt1; @@ -172,40 +163,3 @@ void hwPinMode(uint8_t pin, uint8_t mode) { pinMode(pin, mode); } - -void hwDebugPrint(const char *fmt, ...) -{ -#ifndef MY_DISABLED_SERIAL -#ifdef MY_DEBUGDEVICE - char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; -#ifdef MY_GATEWAY_SERIAL - // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) - snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), - C_INTERNAL, I_LOG_MESSAGE, hwMillis()); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - // prepend timestamp - MY_DEBUGDEVICE.print(hwMillis()); - MY_DEBUGDEVICE.print(" "); -#endif - va_list args; - va_start (args, fmt ); - vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); -#ifdef MY_GATEWAY_SERIAL - // Truncate message if this is gateway node - fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; - fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; -#endif - va_end (args); - MY_DEBUGDEVICE.print(fmtBuffer); - MY_DEBUGDEVICE.flush(); -#else - va_list args; - va_start(args, fmt); - vlogDebug(fmt, args); - va_end(args); -#endif -#else - (void)fmt; -#endif -} diff --git a/hal/architecture/Linux/MyHwLinuxGeneric.h b/hal/architecture/Linux/MyHwLinuxGeneric.h index 0ae3dda60..8d3f1e450 100644 --- a/hal/architecture/Linux/MyHwLinuxGeneric.h +++ b/hal/architecture/Linux/MyHwLinuxGeneric.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -24,6 +24,16 @@ #include #include "SerialPort.h" #include "StdInOutStream.h" +#include +#include +#include +#include +#include +#include +#include +#include "SoftEeprom.h" +#include "log.h" +#include "config.h" #define CRYPTO_LITTLE_ENDIAN @@ -44,6 +54,7 @@ StdInOutStream Serial = StdInOutStream(); // Define these as macros (do nothing) #define hwWatchdogReset() #define hwReboot() +#define hwGetSleepRemaining() (0ul) inline void hwDigitalWrite(uint8_t, uint8_t); inline int hwDigitalRead(uint8_t); @@ -59,6 +70,12 @@ ssize_t hwGetentropy(void *__buffer, size_t __length); #define MY_HW_HAS_GETENTROPY inline uint32_t hwMillis(void); +// SOFTSPI +#ifdef MY_SOFTSPI +#error Soft SPI is not available on this architecture! +#endif +#define hwSPI SPI //!< hwSPI + #ifdef MY_RF24_IRQ_PIN static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/hal/architecture/Linux/MyMainLinuxGeneric.cpp b/hal/architecture/Linux/MyMainLinuxGeneric.cpp index b4f80edcc..449ef9be6 100644 --- a/hal/architecture/Linux/MyMainLinuxGeneric.cpp +++ b/hal/architecture/Linux/MyMainLinuxGeneric.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/BCM/BCM.cpp b/hal/architecture/Linux/drivers/BCM/BCM.cpp similarity index 97% rename from drivers/BCM/BCM.cpp rename to hal/architecture/Linux/drivers/BCM/BCM.cpp index ce5ba1216..5a6810826 100644 --- a/drivers/BCM/BCM.cpp +++ b/hal/architecture/Linux/drivers/BCM/BCM.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/BCM/BCM.h b/hal/architecture/Linux/drivers/BCM/BCM.h similarity index 98% rename from drivers/BCM/BCM.h rename to hal/architecture/Linux/drivers/BCM/BCM.h index 9c0bd8a35..31d42fe95 100644 --- a/drivers/BCM/BCM.h +++ b/hal/architecture/Linux/drivers/BCM/BCM.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/BCM/RPi.cpp b/hal/architecture/Linux/drivers/BCM/RPi.cpp similarity index 99% rename from drivers/BCM/RPi.cpp rename to hal/architecture/Linux/drivers/BCM/RPi.cpp index 4d47082fc..2aa2189a2 100644 --- a/drivers/BCM/RPi.cpp +++ b/hal/architecture/Linux/drivers/BCM/RPi.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/BCM/RPi.h b/hal/architecture/Linux/drivers/BCM/RPi.h similarity index 98% rename from drivers/BCM/RPi.h rename to hal/architecture/Linux/drivers/BCM/RPi.h index fb84b328a..14e8c96a8 100644 --- a/drivers/BCM/RPi.h +++ b/hal/architecture/Linux/drivers/BCM/RPi.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/BCM/SPIBCM.cpp b/hal/architecture/Linux/drivers/BCM/SPIBCM.cpp similarity index 98% rename from drivers/BCM/SPIBCM.cpp rename to hal/architecture/Linux/drivers/BCM/SPIBCM.cpp index c3ea6a5a5..9bbfae4d7 100644 --- a/drivers/BCM/SPIBCM.cpp +++ b/hal/architecture/Linux/drivers/BCM/SPIBCM.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/BCM/SPIBCM.h b/hal/architecture/Linux/drivers/BCM/SPIBCM.h similarity index 99% rename from drivers/BCM/SPIBCM.h rename to hal/architecture/Linux/drivers/BCM/SPIBCM.h index 0d95f23b3..38c9ec845 100644 --- a/drivers/BCM/SPIBCM.h +++ b/hal/architecture/Linux/drivers/BCM/SPIBCM.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/BCM/Wire.cpp b/hal/architecture/Linux/drivers/BCM/Wire.cpp similarity index 100% rename from drivers/BCM/Wire.cpp rename to hal/architecture/Linux/drivers/BCM/Wire.cpp diff --git a/drivers/BCM/Wire.h b/hal/architecture/Linux/drivers/BCM/Wire.h similarity index 100% rename from drivers/BCM/Wire.h rename to hal/architecture/Linux/drivers/BCM/Wire.h diff --git a/drivers/BCM/bcm2835.c b/hal/architecture/Linux/drivers/BCM/bcm2835.c similarity index 71% rename from drivers/BCM/bcm2835.c rename to hal/architecture/Linux/drivers/BCM/bcm2835.c index efb8c0b64..425d1e01b 100644 --- a/drivers/BCM/bcm2835.c +++ b/hal/architecture/Linux/drivers/BCM/bcm2835.c @@ -5,9 +5,7 @@ // // Author: Mike McCauley // Copyright (C) 2011-2013 Mike McCauley -// $Id: bcm2835.c,v 1.23 2015/03/31 04:55:41 mikem Exp mikem $ -// -// Modified September 2016 by Marcelo Aquino +// $Id: bcm2835.c,v 1.27 2019/07/22 23:04:24 mikem Exp mikem $ */ @@ -20,8 +18,6 @@ #include #include #include -#include // For PRIu64 -#include "log.h" #define BCK2835_LIBRARY_BUILD #include "bcm2835.h" @@ -59,18 +55,77 @@ volatile uint32_t *bcm2835_spi0 = (uint32_t *)MAP_FAILED; volatile uint32_t *bcm2835_bsc0 = (uint32_t *)MAP_FAILED; volatile uint32_t *bcm2835_bsc1 = (uint32_t *)MAP_FAILED; volatile uint32_t *bcm2835_st = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_aux = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_spi1 = (uint32_t *)MAP_FAILED; + /* This variable allows us to test on hardware other than RPi. // It prevents access to the kernel memory, and does not do any peripheral access // Instead it prints out what it _would_ do if debug were 0 -*/ + */ static uint8_t debug = 0; +/* RPI 4 has different pullup registers - we need to know if we have that type */ + +static uint8_t pud_type_rpi4 = 0; + +/* RPI 4 has different pullup operation - make backwards compat */ + +static uint8_t pud_compat_setting = BCM2835_GPIO_PUD_OFF; + /* I2C The time needed to transmit one byte. In microseconds. */ static int i2c_byte_wait_us = 0; +/* SPI bit order. BCM2835 SPI0 only supports MSBFIRST, so we instead + * have a software based bit reversal, based on a contribution by Damiano Benedetti + */ +static uint8_t bcm2835_spi_bit_order = BCM2835_SPI_BIT_ORDER_MSBFIRST; +static uint8_t bcm2835_byte_reverse_table[] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +static uint8_t bcm2835_correct_order(uint8_t b) +{ + if (bcm2835_spi_bit_order == BCM2835_SPI_BIT_ORDER_LSBFIRST) { + return bcm2835_byte_reverse_table[b]; + } else { + return b; + } +} + /* // Low level register access functions */ @@ -95,6 +150,11 @@ uint32_t* bcm2835_regbase(uint8_t regbase) return (uint32_t *)bcm2835_bsc0; case BCM2835_REGBASE_BSC1: return (uint32_t *)bcm2835_st; + case BCM2835_REGBASE_AUX: + return (uint32_t *)bcm2835_aux; + case BCM2835_REGBASE_SPI1: + return (uint32_t *)bcm2835_spi1; + } return (uint32_t *)MAP_FAILED; } @@ -114,11 +174,11 @@ unsigned int bcm2835_version(void) */ uint32_t bcm2835_peri_read(volatile uint32_t* paddr) { + uint32_t ret; if (debug) { - printf("bcm2835_peri_read paddr %08X\n", (unsigned) paddr); + printf("bcm2835_peri_read paddr %p\n", (void *) paddr); return 0; } else { - uint32_t ret; __sync_synchronize(); ret = *paddr; __sync_synchronize(); @@ -135,7 +195,7 @@ uint32_t bcm2835_peri_read(volatile uint32_t* paddr) uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) { if (debug) { - printf("bcm2835_peri_read_nb paddr %08X\n", (unsigned) paddr); + printf("bcm2835_peri_read_nb paddr %p\n", paddr); return 0; } else { return *paddr; @@ -148,7 +208,7 @@ uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value) { if (debug) { - printf("bcm2835_peri_write paddr %08X, value %08X\n", (unsigned) paddr, value); + printf("bcm2835_peri_write paddr %p, value %08X\n", paddr, value); } else { __sync_synchronize(); *paddr = value; @@ -160,8 +220,8 @@ void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value) void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value) { if (debug) { - printf("bcm2835_peri_write_nb paddr %08X, value %08X\n", - (unsigned) paddr, value); + printf("bcm2835_peri_write_nb paddr %p, value %08X\n", + paddr, value); } else { *paddr = value; } @@ -380,8 +440,12 @@ void bcm2835_gpio_clr_afen(uint8_t pin) /* Set pullup/down */ void bcm2835_gpio_pud(uint8_t pud) { - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; - bcm2835_peri_write(paddr, pud); + if( pud_type_rpi4 ) { + pud_compat_setting = pud; + } else { + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; + bcm2835_peri_write(paddr, pud); + } } /* Pullup/down clock @@ -389,9 +453,15 @@ void bcm2835_gpio_pud(uint8_t pud) */ void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on) { - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUDCLK0/4 + pin/32; - uint8_t shift = pin % 32; - bcm2835_peri_write(paddr, (on ? 1 : 0) << shift); + if( pud_type_rpi4 ) { + if( on ) { + bcm2835_gpio_set_pud( pin, pud_compat_setting); + } + } else { + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUDCLK0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, (on ? 1 : 0) << shift); + } } /* Read GPIO pad behaviour for groups of GPIOs */ @@ -439,7 +509,7 @@ void bcm2835_delayMicroseconds(uint64_t micros) if (debug) { /* Cant access sytem timers in debug mode */ - printf("bcm2835_delayMicroseconds %" PRIu64 "\n", micros); + printf("bcm2835_delayMicroseconds %lld\n", (long long int) micros); return; } @@ -448,6 +518,14 @@ void bcm2835_delayMicroseconds(uint64_t micros) */ start = bcm2835_st_read(); + /* Not allowed to access timer registers (result is not as precise)*/ + if (start==0) { + t1.tv_sec = 0; + t1.tv_nsec = 1000 * (long)(micros); + nanosleep(&t1, NULL); + return; + } + if (micros > 450) { t1.tv_sec = 0; t1.tv_nsec = 1000 * (long)(micros - 200); @@ -505,17 +583,79 @@ void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) // 6. Write to GPPUDCLK0/1 to remove the clock // // RPi has P1-03 and P1-05 with 1k8 pullup resistor +// +// RPI 4 uses a different PUD method - no clock + */ void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud) { - bcm2835_gpio_pud(pud); - delayMicroseconds(10); - bcm2835_gpio_pudclk(pin, 1); - delayMicroseconds(10); - bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF); - bcm2835_gpio_pudclk(pin, 0); + if( pud_type_rpi4 ) { + int shiftbits = (pin & 0xf) << 1; + uint32_t bits; + uint32_t pull; + + switch (pud) { + case BCM2835_GPIO_PUD_OFF: + pull = 0; + break; + case BCM2835_GPIO_PUD_UP: + pull = 1; + break; + case BCM2835_GPIO_PUD_DOWN: + pull = 2; + break; + default: + return; + } + + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4); + + bits = bcm2835_peri_read_nb( paddr ); + bits &= ~(3 << shiftbits); + bits |= (pull << shiftbits); + + bcm2835_peri_write_nb( paddr, bits ); + + } else { + bcm2835_gpio_pud(pud); + delayMicroseconds(10); + bcm2835_gpio_pudclk(pin, 1); + delayMicroseconds(10); + bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF); + bcm2835_gpio_pudclk(pin, 0); + } + +} + + +uint8_t bcm2835_gpio_get_pud(uint8_t pin) +{ + uint8_t ret = BCM2835_GPIO_PUD_ERROR; + + if( pud_type_rpi4 ) { + uint32_t bits; + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4); + bits = (bcm2835_peri_read_nb( paddr ) >> ((pin & 0xf)<<1)) & 0x3; + + switch (bits) { + case 0: + ret = BCM2835_GPIO_PUD_OFF; + break; + case 1: + ret = BCM2835_GPIO_PUD_UP; + break; + case 2: + ret = BCM2835_GPIO_PUD_DOWN; + break; + default: + ret = BCM2835_GPIO_PUD_ERROR; + } + } + + return ret; } + int bcm2835_spi_begin(void) { volatile uint32_t* paddr; @@ -551,9 +691,9 @@ void bcm2835_spi_end(void) bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); /* CLK */ } -void bcm2835_spi_setBitOrder(uint8_t __attribute__((unused)) order) +void bcm2835_spi_setBitOrder(uint8_t order) { - /* BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one supported by SPI0 */ + bcm2835_spi_bit_order = order; } /* defaults to 0, which means a divider of 65536. @@ -567,6 +707,13 @@ void bcm2835_spi_setClockDivider(uint16_t divider) bcm2835_peri_write(paddr, divider); } +void bcm2835_spi_set_speed_hz(uint32_t speed_hz) +{ + uint16_t divider = (uint16_t) ((uint32_t) BCM2835_CORE_CLK_HZ / speed_hz); + divider &= 0xFFFE; + bcm2835_spi_setClockDivider(divider); +} + void bcm2835_spi_setDataMode(uint8_t mode) { volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; @@ -596,14 +743,14 @@ uint8_t bcm2835_spi_transfer(uint8_t value) ; /* Write to FIFO, no barrier */ - bcm2835_peri_write_nb(fifo, value); + bcm2835_peri_write_nb(fifo, bcm2835_correct_order(value)); /* Wait for DONE to be set */ while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) ; /* Read any byte that was sent back by the slave while we sere sending to it */ - ret = bcm2835_peri_read_nb(fifo); + ret = bcm2835_correct_order(bcm2835_peri_read_nb(fifo)); /* Set TA = 0, and also set the barrier */ bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); @@ -634,12 +781,12 @@ void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) while((TXCnt < len)||(RXCnt < len)) { /* TX fifo not full, so add some more bytes */ while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))&&(TXCnt < len )) { - bcm2835_peri_write_nb(fifo, tbuf[TXCnt]); + bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[TXCnt])); TXCnt++; } /* Rx fifo not empty, so get the next received bytes */ while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) { - rbuf[RXCnt] = bcm2835_peri_read_nb(fifo); + rbuf[RXCnt] = bcm2835_correct_order(bcm2835_peri_read_nb(fifo)); RXCnt++; } } @@ -652,7 +799,7 @@ void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) } /* Writes an number of bytes to SPI */ -void bcm2835_spi_writenb(char* tbuf, uint32_t len) +void bcm2835_spi_writenb(const char* tbuf, uint32_t len) { volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; @@ -676,7 +823,7 @@ void bcm2835_spi_writenb(char* tbuf, uint32_t len) ; /* Write to FIFO, no barrier */ - bcm2835_peri_write_nb(fifo, tbuf[i]); + bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[i])); /* Read from FIFO to prevent stalling */ while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) { @@ -718,6 +865,279 @@ void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active) bcm2835_peri_set_bits(paddr, active << shift, 1 << shift); } +void bcm2835_spi_write(uint16_t data) +{ +#if 0 + char buf[2]; + + buf[0] = data >> 8; + buf[1] = data & 0xFF; + + bcm2835_spi_transfern(buf, 2); +#else + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + + /* Clear TX and RX fifos */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); + + /* Set TA = 1 */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); + + /* Maybe wait for TXD */ + while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) + ; + + /* Write to FIFO */ + bcm2835_peri_write_nb(fifo, (uint32_t) data >> 8); + bcm2835_peri_write_nb(fifo, data & 0xFF); + + + /* Wait for DONE to be set */ + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) + ; + + /* Set TA = 0, and also set the barrier */ + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); +#endif +} + +int bcm2835_aux_spi_begin(void) +{ + volatile uint32_t* enable = bcm2835_aux + BCM2835_AUX_ENABLE/4; + volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; + volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; + + if (bcm2835_spi1 == MAP_FAILED) { + return 0; /* bcm2835_init() failed, or not root */ + } + + /* Set the SPI pins to the Alt 4 function to enable SPI1 access on them */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_ALT4); /* SPI1_CE2_N */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_ALT4); /* SPI1_MISO */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_ALT4); /* SPI1_MOSI */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_ALT4); /* SPI1_SCLK */ + + bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(1000000)); // Default 1MHz SPI + + bcm2835_peri_write(enable, BCM2835_AUX_ENABLE_SPI0); + bcm2835_peri_write(cntl1, 0); + bcm2835_peri_write(cntl0, BCM2835_AUX_SPI_CNTL0_CLEARFIFO); + + return 1; /* OK */ +} + +void bcm2835_aux_spi_end(void) +{ + /* Set all the SPI1 pins back to input */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_INPT); /* SPI1_CE2_N */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_INPT); /* SPI1_MISO */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_INPT); /* SPI1_MOSI */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_INPT); /* SPI1_SCLK */ +} + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz) +{ + uint16_t divider; + + if (speed_hz < (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN) { + speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN; + } else if (speed_hz > (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX) { + speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX; + } + + divider = (uint16_t) DIV_ROUND_UP(BCM2835_CORE_CLK_HZ, 2 * speed_hz) - 1; + + if (divider > (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX) { + return (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX; + } + + return divider; +} + +static uint32_t spi1_speed; + +void bcm2835_aux_spi_setClockDivider(uint16_t divider) +{ + spi1_speed = (uint32_t) divider; +} + +void bcm2835_aux_spi_write(uint16_t data) +{ + volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; + volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; + volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4; + volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4; + + uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT); + _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT; + _cntl0 |= 16; // Shift length + + bcm2835_peri_write(cntl0, _cntl0); + bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN); + + while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) + ; + + bcm2835_peri_write(io, (uint32_t) data << 16); +} + +void bcm2835_aux_spi_writenb(const char *tbuf, uint32_t len) +{ + volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; + volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; + volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4; + volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4; + volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4; + + char *tx = (char *) tbuf; + uint32_t tx_len = len; + uint32_t count; + uint32_t data; + uint32_t i; + uint8_t byte; + + uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT); + _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH; + + bcm2835_peri_write(cntl0, _cntl0); + bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN); + + while (tx_len > 0) { + + while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) + ; + + count = MIN(tx_len, 3); + data = 0; + + for (i = 0; i < count; i++) { + byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0; + data |= byte << (8 * (2 - i)); + } + + data |= (count * 8) << 24; + tx_len -= count; + + if (tx_len != 0) { + bcm2835_peri_write(txhold, data); + } else { + bcm2835_peri_write(io, data); + } + + while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) + ; + + (void) bcm2835_peri_read(io); + } +} + +void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len) +{ + volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; + volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; + volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4; + volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4; + volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4; + + char *tx = (char *)tbuf; + char *rx = (char *)rbuf; + uint32_t tx_len = len; + uint32_t rx_len = len; + uint32_t count; + uint32_t data; + uint32_t i; + uint8_t byte; + + uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT); + _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT; + _cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH; + + bcm2835_peri_write(cntl0, _cntl0); + bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN); + + while ((tx_len > 0) || (rx_len > 0)) { + + while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) && (tx_len > 0)) { + count = MIN(tx_len, 3); + data = 0; + + for (i = 0; i < count; i++) { + byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0; + data |= byte << (8 * (2 - i)); + } + + data |= (count * 8) << 24; + tx_len -= count; + + if (tx_len != 0) { + bcm2835_peri_write(txhold, data); + } else { + bcm2835_peri_write(io, data); + } + + } + + while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_RX_EMPTY) && (rx_len > 0)) { + count = MIN(rx_len, 3); + data = bcm2835_peri_read(io); + + if (rbuf != NULL) { + switch (count) { + case 3: + *rx++ = (char)((data >> 16) & 0xFF); + /*@fallthrough@*/ + /* no break */ + case 2: + *rx++ = (char)((data >> 8) & 0xFF); + /*@fallthrough@*/ + /* no break */ + case 1: + *rx++ = (char)((data >> 0) & 0xFF); + } + } + + rx_len -= count; + } + + while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) && (rx_len > 0)) { + count = MIN(rx_len, 3); + data = bcm2835_peri_read(io); + + if (rbuf != NULL) { + switch (count) { + case 3: + *rx++ = (char)((data >> 16) & 0xFF); + /*@fallthrough@*/ + /* no break */ + case 2: + *rx++ = (char)((data >> 8) & 0xFF); + /*@fallthrough@*/ + /* no break */ + case 1: + *rx++ = (char)((data >> 0) & 0xFF); + } + } + + rx_len -= count; + } + } +} + +void bcm2835_aux_spi_transfern(char *buf, uint32_t len) +{ + bcm2835_aux_spi_transfernb(buf, buf, len); +} + int bcm2835_i2c_begin(void) { uint16_t cdiv; @@ -822,7 +1242,7 @@ uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) uint8_t reason = BCM2835_I2C_REASON_OK; /* Clear FIFO */ - bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1, BCM2835_BSC_C_CLEAR_1 ); /* Clear Status */ bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); /* Set Data Length */ @@ -862,7 +1282,7 @@ uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) reason = BCM2835_I2C_REASON_ERROR_DATA; } - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE, BCM2835_BSC_S_DONE); return reason; } @@ -887,7 +1307,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) uint8_t reason = BCM2835_I2C_REASON_OK; /* Clear FIFO */ - bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1, BCM2835_BSC_C_CLEAR_1 ); /* Clear Status */ bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); /* Set Data Length */ @@ -898,7 +1318,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) /* wait for transfer to complete */ while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) { /* we must empty the FIFO as it is populated and not use any delay */ - while (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) { + while (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) { /* Read from FIFO, no barrier */ buf[i] = bcm2835_peri_read_nb(fifo); i++; @@ -929,7 +1349,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) reason = BCM2835_I2C_REASON_ERROR_DATA; } - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE, BCM2835_BSC_S_DONE); return reason; } @@ -955,7 +1375,7 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) uint8_t reason = BCM2835_I2C_REASON_OK; /* Clear FIFO */ - bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1, BCM2835_BSC_C_CLEAR_1 ); /* Clear Status */ bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); /* Set Data Length */ @@ -1014,7 +1434,7 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) reason = BCM2835_I2C_REASON_ERROR_DATA; } - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE, BCM2835_BSC_S_DONE); return reason; } @@ -1041,7 +1461,7 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint uint8_t reason = BCM2835_I2C_REASON_OK; /* Clear FIFO */ - bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1, BCM2835_BSC_C_CLEAR_1 ); /* Clear Status */ bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); @@ -1111,7 +1531,7 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint reason = BCM2835_I2C_REASON_ERROR_DATA; } - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE, BCM2835_BSC_S_DONE); return reason; } @@ -1122,6 +1542,11 @@ uint64_t bcm2835_st_read(void) volatile uint32_t* paddr; uint32_t hi, lo; uint64_t st; + + if (bcm2835_st==MAP_FAILED) { + return 0; + } + paddr = bcm2835_st + BCM2835_ST_CHI/4; hi = bcm2835_peri_read(paddr); @@ -1259,7 +1684,7 @@ static void *mapmem(const char *msg, size_t size, int fd, off_t off) { void *map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off); if (map == MAP_FAILED) { - logError("bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); + fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); } return map; } @@ -1291,22 +1716,58 @@ int bcm2835_init(void) bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4; + bcm2835_aux = bcm2835_peripherals + BCM2835_AUX_BASE/4; + bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4; + return 1; /* Success */ } /* Figure out the base and size of the peripheral address block - // using the device-tree. Required for RPi2, optional for RPi 1 + // using the device-tree. Required for RPi2/3/4, optional for RPi 1 */ - if ((fp = fopen(BMC2835_RPI2_DT_FILENAME , "rb"))) { - unsigned char buf[4]; - fseek(fp, BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET, SEEK_SET); - if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf)) { - bcm2835_peripherals_base = (uint32_t *)(buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0); - } - fseek(fp, BMC2835_RPI2_DT_PERI_SIZE_OFFSET, SEEK_SET); - if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf)) { - bcm2835_peripherals_size = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0); + if ((fp = fopen(BMC2835_RPI2_DT_FILENAME, "rb"))) { + unsigned char buf[16]; + uint32_t base_address; + uint32_t peri_size; + if (fread(buf, 1, sizeof(buf), fp) >= 8) { + base_address = (buf[4] << 24) | + (buf[5] << 16) | + (buf[6] << 8) | + (buf[7] << 0); + + peri_size = (buf[8] << 24) | + (buf[9] << 16) | + (buf[10] << 8) | + (buf[11] << 0); + + if (!base_address) { + /* looks like RPI 4 */ + base_address = (buf[8] << 24) | + (buf[9] << 16) | + (buf[10] << 8) | + (buf[11] << 0); + + peri_size = (buf[12] << 24) | + (buf[13] << 16) | + (buf[14] << 8) | + (buf[15] << 0); + } + /* check for valid known range formats */ + if ((buf[0] == 0x7e) && + (buf[1] == 0x00) && + (buf[2] == 0x00) && + (buf[3] == 0x00) && + ((base_address == BCM2835_PERI_BASE) || (base_address == BCM2835_RPI2_PERI_BASE) || + (base_address == BCM2835_RPI4_PERI_BASE))) { + bcm2835_peripherals_base = (uint32_t *)base_address; + bcm2835_peripherals_size = peri_size; + if( base_address == BCM2835_RPI4_PERI_BASE ) { + pud_type_rpi4 = 1; + } + } + } + fclose(fp); } /* else we are prob on RPi 1 with BCM2835, and use the hardwired defaults */ @@ -1321,14 +1782,14 @@ int bcm2835_init(void) if (geteuid() == 0) { /* Open the master /dev/mem device */ if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) { - logError("bcm2835_init: Unable to open /dev/mem: %s\n", - strerror(errno)) ; + fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", + strerror(errno)) ; goto exit; } /* Base of the peripherals block is mapped to VM */ bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, - (uint32_t)bcm2835_peripherals_base); + (off_t)bcm2835_peripherals_base); if (bcm2835_peripherals == MAP_FAILED) { goto exit; } @@ -1345,21 +1806,23 @@ int bcm2835_init(void) bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; /* I2C */ bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */ bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4; + bcm2835_aux = bcm2835_peripherals + BCM2835_AUX_BASE/4; + bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4; ok = 1; } else { /* Not root, try /dev/gpiomem */ /* Open the master /dev/mem device */ if ((memfd = open("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0) { - logError("bcm2835_init: Unable to open /dev/gpiomem: %s\n", - strerror(errno)) ; + fprintf(stderr, "bcm2835_init: Unable to open /dev/gpiomem: %s\n", + strerror(errno)) ; goto exit; } /* Base of the peripherals block is mapped to VM */ bcm2835_peripherals_base = 0; bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, - (uint32_t)bcm2835_peripherals_base); + (off_t)bcm2835_peripherals_base); if (bcm2835_peripherals == MAP_FAILED) { goto exit; } @@ -1396,6 +1859,8 @@ int bcm2835_close(void) bcm2835_bsc0 = MAP_FAILED; bcm2835_bsc1 = MAP_FAILED; bcm2835_st = MAP_FAILED; + bcm2835_aux = MAP_FAILED; + bcm2835_spi1 = MAP_FAILED; return 1; /* Success */ } @@ -1478,3 +1943,6 @@ int main(int argc, char **argv) return 0; } #endif + + + diff --git a/drivers/BCM/bcm2835.h b/hal/architecture/Linux/drivers/BCM/bcm2835.h similarity index 80% rename from drivers/BCM/bcm2835.h rename to hal/architecture/Linux/drivers/BCM/bcm2835.h index 4dc081624..3a8f78ce1 100644 --- a/drivers/BCM/bcm2835.h +++ b/hal/architecture/Linux/drivers/BCM/bcm2835.h @@ -4,14 +4,13 @@ Author: Mike McCauley Copyright (C) 2011-2013 Mike McCauley - $Id: bcm2835.h,v 1.20 2015/03/31 04:55:41 mikem Exp mikem $ + $Id: bcm2835.h,v 1.25 2019/07/22 23:04:13 mikem Exp $ */ /*! \defgroup BCM2835grp C library for Broadcom BCM 2835 as used in Raspberry Pi - \ingroup internals This is a C library for Raspberry Pi (RPi). It provides access to - GPIO and other IO functions on the Broadcom BCM 2835 chip, + GPIO and other IO functions on the Broadcom BCM 2835 chip, as used in the RaspberryPi, allowing access to the GPIO pins on the 26 pin IDE plug on the RPi board so you can control and interface with various external devices. @@ -19,12 +18,15 @@ and for accessing the system timers. Pin event detection is supported by polling (interrupts are not supported). + Works on all versions upt to and including RPI 4. + Works with all versions of Debian up to and including Debian Buster 10. + It is C++ compatible, and installs as a header file and non-shared library on any Linux-based distro (but clearly is no use except on Raspberry Pi or another board with BCM 2835). The version of the package that this documentation refers to can be downloaded - from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.50.tar.gz + from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.60.tar.gz You can find the latest version at http://www.airspayce.com/mikem/bcm2835 Several example programs are provided. @@ -36,7 +38,10 @@ You can also find online help and discussion at http://groups.google.com/group/bcm2835 Please use that group for all questions and discussions on this topic. Do not contact the author directly, unless it is to discuss commercial licensing. - Before asking a question or reporting a bug, please read http://www.catb.org/esr/faqs/smart-questions.html + Before asking a question or reporting a bug, please read + - http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question + - http://www.catb.org/esr/faqs/smart-questions.html + - http://www.chiark.greenend.org.uk/~shgtatham/bugs.html Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian and Occidentalisv01, 2016-02-09 Raspbian Jessie. @@ -107,6 +112,8 @@ bcm2835_st bcm2835_bsc0 bcm2835_bsc1 + bcm2835_aux + bcm2835_spi1 \par Raspberry Pi 2 (RPI2) @@ -161,6 +168,22 @@ - P1-24 (CE0) - P1-26 (CE1) + Although it is possible to select high speeds for the SPI interface, up to 125MHz (see bcm2835_spi_setClockDivider()) + you should not expect to actually achieve those sorts of speeds with the RPi wiring. Our tests on RPi 2 show that the + SPI CLK line when unloaded has a resonant frequency of about 40MHz, and when loaded, the MOSI and MISO lines + ring at an even lower frequency. Measurements show that SPI waveforms are very poor and unusable at 62 and 125MHz. + Dont expect any speed faster than 31MHz to work reliably. + + The bcm2835_aux_spi_* functions allow you to control the BCM 2835 SPI1 interface, + allowing you to send and received data by SPI (Serial Peripheral Interface). + + The Raspberry Pi GPIO pins used for AUX SPI (SPI1) are: + + - P1-38 (MOSI) + - P1-35 (MISO) + - P1-40 (CLK) + - P1-36 (CE2) + \par I2C Pins The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface, @@ -216,7 +239,7 @@ clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be 1.2MHz/1024 = 1171.875Hz. - \par SPI + \par Interactions with other systems In order for bcm2835 library SPI to work, you may need to disable the SPI kernel module using: @@ -227,6 +250,20 @@ Reboot. \endcode + Since bcm2835 accesses the lowest level hardware interfaces (in eh intererests of speed and flexibility) + there can be intercations with other low level software trying to do similar things. + + It seems that with "latest" 8.0 Jessie 4.9.24-v7+ kernel PWM just won't + work unless you disable audio. There's a line + \code + dtparam=audio=on + \endcode + in the /boot/config.txt. + Comment it out like this: + \code + #dtparam=audio=on + \endcode + \par Real Time performance constraints The bcm2835 is a library for user programs (i.e. they run in 'userland'). @@ -241,6 +278,9 @@ Arjan reports that you can prevent swapping on Linux with the following code fragment: \code + #define + #define + struct sched_param sp; memset(&sp, 0, sizeof(sp)); sp.sched_priority = sched_get_priority_max(SCHED_FIFO); @@ -248,6 +288,15 @@ mlockall(MCL_CURRENT | MCL_FUTURE); \endcode + \par Crashing on some versions of Raspbian + Some people have reported that various versions of Rasbian will crash or hang + if certain GPIO pins are toggled: https://github.com/raspberrypi/linux/issues/2550 + when using bcm2835. + A workaround is to add this line to your /boot/config.txt: + \code + dtoverlay=gpio-no-irq + \endcode + \par Bindings to other languages mikem has made Perl bindings available at CPAN: @@ -266,7 +315,13 @@ the right to share who uses it. If you wish to use this software under Open Source Licensing, you must contribute all your source code to the open source community in accordance with the GPL Version 2 when your application is - distributed. See http://www.gnu.org/copyleft/gpl.html and COPYING + distributed. See https://www.gnu.org/licenses/gpl-2.0.html and COPYING + + \par Commercial Licensing + + This is the appropriate option if you are creating proprietary applications + and you are not prepared to distribute and share the source code of your + application. To purchase a commercial license, contact info@airspayce.com \par Acknowledgements @@ -368,7 +423,7 @@ \version 1.27 bcm2835_gpio_set_pad() no longer needs BCM2835_PAD_PASSWRD: it is now automatically included. - Added suport for PWM mode with bcm2835_pwm_* functions. + Added support for PWM mode with bcm2835_pwm_* functions. \version 1.28 Fixed a problem where bcm2835_spi_writenb() would have problems with transfers of more than 64 bytes dues to read buffer filling. Patched by Peter Würtz. @@ -403,7 +458,7 @@ \version 1.39 Beta version of RPi2 compatibility. Not tested here on RPi2 hardware. Testers please confirm correct operation on RPi2.
- Unneccessary 'volatile' qualifiers removed from all variables and signatures.
+ Unnecessary 'volatile' qualifiers removed from all variables and signatures.
Removed unsupportable PWM dividers, based on a report from Christophe Cecillon.
Minor improvements to spi.c example.
@@ -425,13 +480,13 @@ Testing on RPI 2, with ArchLinuxARM-rpi-2-latest and 2015-02-16-raspbian-wheezy.
\version 1.44 Added documention about the need for device tree to be enabled on RPI2.
- Improvements to detection of availablity of DMB instruction based on value of __ARM_ARCH macro.
+ Improvements to detection of availability of DMB instruction based on value of __ARM_ARCH macro.
\version 1.45 Fixed an error in the pad group offsets that would prevent bcm2835_gpio_set_pad() and bcm2835_gpio_pad() working correctly with non-0 pad groups. Reported by Guido. \version 1.46 2015-09-18 - Added symbolic definitions for remaining pins on 40 pin GPIO header on RPi 2.
+ Added symbolic definitions for remaining pins on 40 pin GPIO header on RPi 2.
\version 1.47 2015-11-18 Fixed possibly incorrect reads in bcm2835_i2c_read_register_rs, patch from Eckhardt Ulrich.
@@ -449,10 +504,45 @@ (which prevents access to the SPI and I2C peripherals, amongst others). Testing on Raspbian Jessie. - \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS + \version 1.51 2016-11-03 + Added documentation about SPI clock divider and resulting SPI speeds on RPi3. + Fixed a problem where seg fault could occur in bcm2835_delayMicroseconds() if not running as root. Patch from Pok. + + \version 1.52 2017-02-03 + Added link to commercial license purchasing. + + \version 1.53 2018-01-14 + Added support for AUX SPI (SPI1) + Contributed by Arjan van Vught (http://www.raspberrypi-dmx.org/) + + \version 1.54 2018-01-17 + Fixed compile errors in new AUX spi code under some circumstances. + + \version 1.55 2018-01-20 + Fixed version numbers. + Fixed some warnings. + + \version 1.56 2018-06-10 + Supports bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST), after which SPI bytes are reversed on read or write. + Based on a suggestion by Damiano Benedetti. + + \version 1.57 2018-08-28 + Added SPI function bcm2835_spi_set_speed_hz(uint32_t speed_hz); + Contributed by Arjan van Vught (http://www.raspberrypi-dmx.org/) + + \version 1.58 2018-11-29 + Added examples/spiram, which shows how to use the included little library (spiram.c and spiram.h) + to read and write SPI RAM chips such as 23K256-I/P + + \version 1.59 2019-05-22 + Fixed a bug in bcm2835_i2c_read reported by Charles Hayward where a noisy I2C line cold cause a seg fault by + reading too many characters. + \version 1.60 2019-07-23 + Applied patch from Mark Dootson for RPi 4 compatibility. Thanks Mark. Not tested here on RPi4, but others report it works. + Tested as still working correctly on earlier RPi models. Tested with Debian Buster on earlier models - Modified September 2016 by Marcelo Aquino + \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS */ @@ -462,7 +552,7 @@ #include -#define BCM2835_VERSION 10050 /* Version 1.50 */ +#define BCM2835_VERSION 10060 /* Version 1.60 */ /* RPi 2 is ARM v7, and has DMB instruction for memory barriers. Older RPis are ARM v6 and don't, so a coprocessor instruction must be used instead. @@ -473,8 +563,7 @@ #define BCM2835_HAVE_DMB #endif -/*! \defgroup BCM2835constantsgrp Constants for passing to and from library functions - \ingroup BCM2835grp +/*! \defgroup constants Constants for passing to and from library functions The values here are designed to be passed to various functions in the bcm2835 library. @{ */ @@ -484,15 +573,16 @@ /*! This means pin LOW, false, 0volts on a pin. */ #define LOW 0x0 +/*! Return the minimum of 2 numbers */ +#ifndef MIN +#define MIN(a, b) (a < b ? a : b) +#endif + /*! Speed of the core clock core_clk */ #define BCM2835_CORE_CLK_HZ 250000000 /*!< 250 MHz */ -/*! On RPi2 with BCM2836, and all recent OSs, the base of the peripherals is read from a /proc file */ +/*! On all recent OSs, the base of the peripherals is read from a /proc file */ #define BMC2835_RPI2_DT_FILENAME "/proc/device-tree/soc/ranges" -/*! Offset into BMC2835_RPI2_DT_FILENAME for the peripherals base address */ -#define BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET 4 -/*! Offset into BMC2835_RPI2_DT_FILENAME for the peripherals size address */ -#define BMC2835_RPI2_DT_PERI_SIZE_OFFSET 8 /*! Physical addresses for various peripheral register sets Base Physical Address of the BCM 2835 peripheral registers @@ -504,11 +594,17 @@ #define BCM2835_PERI_BASE 0x20000000 /*! Size of the peripherals block on RPi 1 */ #define BCM2835_PERI_SIZE 0x01000000 +/*! Alternate base address for RPI 2 / 3 */ +#define BCM2835_RPI2_PERI_BASE 0x3F000000 +/*! Alternate base address for RPI 4 */ +#define BCM2835_RPI4_PERI_BASE 0xFE000000 +/*! Alternate size for RPI 4 */ +#define BCM2835_RPI4_PERI_SIZE 0x01800000 /*! Offsets for the bases of various peripherals within the peripherals block / Base Address of the System Timer registers */ -#define BCM2835_ST_BASE 0x3000 +#define BCM2835_ST_BASE 0x3000 /*! Base Address of the Pads registers */ #define BCM2835_GPIO_PADS 0x100000 /*! Base Address of the Clock/timer registers */ @@ -518,11 +614,18 @@ /*! Base Address of the SPI0 registers */ #define BCM2835_SPI0_BASE 0x204000 /*! Base Address of the BSC0 registers */ -#define BCM2835_BSC0_BASE 0x205000 +#define BCM2835_BSC0_BASE 0x205000 /*! Base Address of the PWM registers */ #define BCM2835_GPIO_PWM 0x20C000 +/*! Base Address of the AUX registers */ +#define BCM2835_AUX_BASE 0x215000 +/*! Base Address of the AUX_SPI1 registers */ +#define BCM2835_SPI1_BASE 0x215080 +/*! Base Address of the AUX_SPI2 registers */ +#define BCM2835_SPI2_BASE 0x2150C0 /*! Base Address of the BSC1 registers */ -#define BCM2835_BSC1_BASE 0x804000 +#define BCM2835_BSC1_BASE 0x804000 + /*! Physical address and size of the peripherals block May be overridden on RPi2 @@ -574,6 +677,17 @@ extern volatile uint32_t *bcm2835_bsc0; */ extern volatile uint32_t *bcm2835_bsc1; +/*! Base of the AUX registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_aux; + +/*! Base of the SPI1 registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_spi1; + + /*! \brief bcm2835RegisterBase Register bases for bcm2835_regbase() */ @@ -585,7 +699,9 @@ typedef enum { BCM2835_REGBASE_PADS = 5, /*!< Base of the PADS registers. */ BCM2835_REGBASE_SPI0 = 6, /*!< Base of the SPI0 registers. */ BCM2835_REGBASE_BSC0 = 7, /*!< Base of the BSC0 registers. */ - BCM2835_REGBASE_BSC1 = 8 /*!< Base of the BSC1 registers. */ + BCM2835_REGBASE_BSC1 = 8, /*!< Base of the BSC1 registers. */ + BCM2835_REGBASE_AUX = 9, /*!< Base of the AUX registers. */ + BCM2835_REGBASE_SPI1 = 10 /*!< Base of the SPI1 registers. */ } bcm2835RegisterBase; /*! Size of memory page on RPi */ @@ -631,6 +747,12 @@ typedef enum { #define BCM2835_GPPUDCLK0 0x0098 /*!< GPIO Pin Pull-up/down Enable Clock 0 */ #define BCM2835_GPPUDCLK1 0x009c /*!< GPIO Pin Pull-up/down Enable Clock 1 */ +/* 2711 has a different method for pin pull-up/down/enable */ +#define BCM2835_GPPUPPDN0 0x00e4 /* Pin pull-up/down for pins 15:0 */ +#define BCM2835_GPPUPPDN1 0x00e8 /* Pin pull-up/down for pins 31:16 */ +#define BCM2835_GPPUPPDN2 0x00ec /* Pin pull-up/down for pins 47:32 */ +#define BCM2835_GPPUPPDN3 0x00f0 /* Pin pull-up/down for pins 57:48 */ + /*! \brief bcm2835PortFunction Port function select modes for bcm2835_gpio_fsel() */ @@ -655,6 +777,9 @@ typedef enum { BCM2835_GPIO_PUD_UP = 0x02 /*!< Enable Pull Up control 0b10 */ } bcm2835PUDControl; +/* need a value for pud functions that can't work unless RPI 4 */ +#define BCM2835_GPIO_PUD_ERROR 0x08 + /*! Pad control register offsets from BCM2835_GPIO_PADS */ #define BCM2835_PADS_GPIO_0_27 0x002c /*!< Pad control register for pads 0 to 27 */ #define BCM2835_PADS_GPIO_28_45 0x0030 /*!< Pad control register for pads 28 to 45 */ @@ -737,7 +862,7 @@ typedef enum { RPI_V2_GPIO_P1_31 = 6, /*!< Version 2, Pin P1-31 */ RPI_V2_GPIO_P1_32 = 12, /*!< Version 2, Pin P1-32 */ RPI_V2_GPIO_P1_33 = 13, /*!< Version 2, Pin P1-33 */ - RPI_V2_GPIO_P1_35 = 19, /*!< Version 2, Pin P1-35 */ + RPI_V2_GPIO_P1_35 = 19, /*!< Version 2, Pin P1-35, can be PWM channel 1 in ALT FUN 5 */ RPI_V2_GPIO_P1_36 = 16, /*!< Version 2, Pin P1-36 */ RPI_V2_GPIO_P1_37 = 26, /*!< Version 2, Pin P1-37 */ RPI_V2_GPIO_P1_38 = 20, /*!< Version 2, Pin P1-38 */ @@ -771,13 +896,69 @@ typedef enum { RPI_BPLUS_GPIO_J8_31 = 6, /*!< B+, Pin J8-31, */ RPI_BPLUS_GPIO_J8_32 = 12, /*!< B+, Pin J8-32, */ RPI_BPLUS_GPIO_J8_33 = 13, /*!< B+, Pin J8-33, */ - RPI_BPLUS_GPIO_J8_35 = 19, /*!< B+, Pin J8-35, */ + RPI_BPLUS_GPIO_J8_35 = 19, /*!< B+, Pin J8-35, can be PWM channel 1 in ALT FUN 5 */ RPI_BPLUS_GPIO_J8_36 = 16, /*!< B+, Pin J8-36, */ RPI_BPLUS_GPIO_J8_37 = 26, /*!< B+, Pin J8-37, */ RPI_BPLUS_GPIO_J8_38 = 20, /*!< B+, Pin J8-38, */ RPI_BPLUS_GPIO_J8_40 = 21 /*!< B+, Pin J8-40, */ } RPiGPIOPin; +/* Defines for AUX + GPIO register offsets from BCM2835_AUX_BASE. +*/ +#define BCM2835_AUX_IRQ 0x0000 /*!< xxx */ +#define BCM2835_AUX_ENABLE 0x0004 /*!< */ + +#define BCM2835_AUX_ENABLE_UART1 0x01 /*!< */ +#define BCM2835_AUX_ENABLE_SPI0 0x02 /*!< SPI0 (SPI1 in the device) */ +#define BCM2835_AUX_ENABLE_SPI1 0x04 /*!< SPI1 (SPI2 in the device) */ + + +#define BCM2835_AUX_SPI_CNTL0 0x0000 /*!< */ +#define BCM2835_AUX_SPI_CNTL1 0x0004 /*!< */ +#define BCM2835_AUX_SPI_STAT 0x0008 /*!< */ +#define BCM2835_AUX_SPI_PEEK 0x000C /*!< Read but do not take from FF */ +#define BCM2835_AUX_SPI_IO 0x0020 /*!< Write = TX, read=RX */ +#define BCM2835_AUX_SPI_TXHOLD 0x0030 /*!< Write = TX keep CS, read=RX */ + +#define BCM2835_AUX_SPI_CLOCK_MIN 30500 /*!< 30,5kHz */ +#define BCM2835_AUX_SPI_CLOCK_MAX 125000000 /*!< 125Mhz */ + +#define BCM2835_AUX_SPI_CNTL0_SPEED 0xFFF00000 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_SPEED_MAX 0xFFF /*!< */ +#define BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT 20 /*!< */ + +#define BCM2835_AUX_SPI_CNTL0_CS0_N 0x000C0000 /*!< CS 0 low */ +#define BCM2835_AUX_SPI_CNTL0_CS1_N 0x000A0000 /*!< CS 1 low */ +#define BCM2835_AUX_SPI_CNTL0_CS2_N 0x00060000 /*!< CS 2 low */ + +#define BCM2835_AUX_SPI_CNTL0_POSTINPUT 0x00010000 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_VAR_CS 0x00008000 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH 0x00004000 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_DOUTHOLD 0x00003000 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_ENABLE 0x00000800 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_CPHA_IN 0x00000400 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_CLEARFIFO 0x00000200 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_CPHA_OUT 0x00000100 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_CPOL 0x00000080 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_MSBF_OUT 0x00000040 /*!< */ +#define BCM2835_AUX_SPI_CNTL0_SHIFTLEN 0x0000003F /*!< */ + +#define BCM2835_AUX_SPI_CNTL1_CSHIGH 0x00000700 /*!< */ +#define BCM2835_AUX_SPI_CNTL1_IDLE 0x00000080 /*!< */ +#define BCM2835_AUX_SPI_CNTL1_TXEMPTY 0x00000040 /*!< */ +#define BCM2835_AUX_SPI_CNTL1_MSBF_IN 0x00000002 /*!< */ +#define BCM2835_AUX_SPI_CNTL1_KEEP_IN 0x00000001 /*!< */ + +#define BCM2835_AUX_SPI_STAT_TX_LVL 0xFF000000 /*!< */ +#define BCM2835_AUX_SPI_STAT_RX_LVL 0x00FF0000 /*!< */ +#define BCM2835_AUX_SPI_STAT_TX_FULL 0x00000400 /*!< */ +#define BCM2835_AUX_SPI_STAT_TX_EMPTY 0x00000200 /*!< */ +#define BCM2835_AUX_SPI_STAT_RX_FULL 0x00000100 /*!< */ +#define BCM2835_AUX_SPI_STAT_RX_EMPTY 0x00000080 /*!< */ +#define BCM2835_AUX_SPI_STAT_BUSY 0x00000040 /*!< */ +#define BCM2835_AUX_SPI_STAT_BITCOUNT 0x0000003F /*!< */ + /* Defines for SPI GPIO register offsets from BCM2835_SPI0_BASE. Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map @@ -848,28 +1029,35 @@ typedef enum { /*! \brief bcm2835SPIClockDivider Specifies the divider used to generate the SPI clock from the system clock. Figures below give the divider, clock period and clock frequency. - Clock divided is based on nominal base clock rate of 250MHz + Clock divided is based on nominal core clock rate of 250MHz on RPi1 and RPi2, and 400MHz on RPi3. It is reported that (contrary to the documentation) any even divider may used. - The frequencies shown for each divider have been confirmed by measurement. + The frequencies shown for each divider have been confirmed by measurement on RPi1 and RPi2. + The system clock frequency on RPi3 is different, so the frequency you get from a given divider will be different. + See comments in 'SPI Pins' for information about reliable SPI speeds. + Note: it is possible to change the core clock rate of the RPi 3 back to 250MHz, by putting + \code + core_freq=250 + \endcode + in the config.txt */ typedef enum { - BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, /*!< 65536 = 262.144us = 3.814697260kHz */ - BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, /*!< 32768 = 131.072us = 7.629394531kHz */ - BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, /*!< 16384 = 65.536us = 15.25878906kHz */ - BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, /*!< 8192 = 32.768us = 30/51757813kHz */ - BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, /*!< 4096 = 16.384us = 61.03515625kHz */ - BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, /*!< 2048 = 8.192us = 122.0703125kHz */ - BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, /*!< 1024 = 4.096us = 244.140625kHz */ - BCM2835_SPI_CLOCK_DIVIDER_512 = 512, /*!< 512 = 2.048us = 488.28125kHz */ - BCM2835_SPI_CLOCK_DIVIDER_256 = 256, /*!< 256 = 1.024us = 976.5625kHz */ - BCM2835_SPI_CLOCK_DIVIDER_128 = 128, /*!< 128 = 512ns = = 1.953125MHz */ - BCM2835_SPI_CLOCK_DIVIDER_64 = 64, /*!< 64 = 256ns = 3.90625MHz */ - BCM2835_SPI_CLOCK_DIVIDER_32 = 32, /*!< 32 = 128ns = 7.8125MHz */ - BCM2835_SPI_CLOCK_DIVIDER_16 = 16, /*!< 16 = 64ns = 15.625MHz */ - BCM2835_SPI_CLOCK_DIVIDER_8 = 8, /*!< 8 = 32ns = 31.25MHz */ - BCM2835_SPI_CLOCK_DIVIDER_4 = 4, /*!< 4 = 16ns = 62.5MHz */ - BCM2835_SPI_CLOCK_DIVIDER_2 = 2, /*!< 2 = 8ns = 125MHz, fastest you can get */ - BCM2835_SPI_CLOCK_DIVIDER_1 = 1 /*!< 1 = 262.144us = 3.814697260kHz, same as 0/65536 */ + BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, /*!< 65536 = 3.814697260kHz on Rpi2, 6.1035156kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, /*!< 32768 = 7.629394531kHz on Rpi2, 12.20703125kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, /*!< 16384 = 15.25878906kHz on Rpi2, 24.4140625kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, /*!< 8192 = 30.51757813kHz on Rpi2, 48.828125kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, /*!< 4096 = 61.03515625kHz on Rpi2, 97.65625kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, /*!< 2048 = 122.0703125kHz on Rpi2, 195.3125kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, /*!< 1024 = 244.140625kHz on Rpi2, 390.625kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_512 = 512, /*!< 512 = 488.28125kHz on Rpi2, 781.25kHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_256 = 256, /*!< 256 = 976.5625kHz on Rpi2, 1.5625MHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_128 = 128, /*!< 128 = 1.953125MHz on Rpi2, 3.125MHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_64 = 64, /*!< 64 = 3.90625MHz on Rpi2, 6.250MHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_32 = 32, /*!< 32 = 7.8125MHz on Rpi2, 12.5MHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_16 = 16, /*!< 16 = 15.625MHz on Rpi2, 25MHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_8 = 8, /*!< 8 = 31.25MHz on Rpi2, 50MHz on RPI3 */ + BCM2835_SPI_CLOCK_DIVIDER_4 = 4, /*!< 4 = 62.5MHz on Rpi2, 100MHz on RPI3. Dont expect this speed to work reliably. */ + BCM2835_SPI_CLOCK_DIVIDER_2 = 2, /*!< 2 = 125MHz on Rpi2, 200MHz on RPI3, fastest you can get. Dont expect this speed to work reliably.*/ + BCM2835_SPI_CLOCK_DIVIDER_1 = 1 /*!< 1 = 3.814697260kHz on Rpi2, 6.1035156kHz on RPI3, same as 0/65536 */ } bcm2835SPIClockDivider; /* Defines for I2C @@ -945,19 +1133,22 @@ typedef enum { #define BCM2835_ST_CLO 0x0004 /*!< System Timer Counter Lower 32 bits */ #define BCM2835_ST_CHI 0x0008 /*!< System Timer Counter Upper 32 bits */ +/*! @} */ + + /* Defines for PWM, word offsets (ie 4 byte multiples) */ -#define BCM2835_PWM_CONTROL 0 /*!< PWM Control register */ -#define BCM2835_PWM_STATUS 1 /*!< PWM Status register */ -#define BCM2835_PWM_DMAC 2 /*!< PWM DMA Configuration */ -#define BCM2835_PWM0_RANGE 4 /*!< PWM Channel 0 Range */ -#define BCM2835_PWM0_DATA 5 /*!< PWM Channel 0 Data */ -#define BCM2835_PWM_FIF1 6 /*!< PWM FIFO Input */ -#define BCM2835_PWM1_RANGE 8 /*!< PWM Channel 1 Range */ -#define BCM2835_PWM1_DATA 9 /*!< PWM Channel 1 Data */ +#define BCM2835_PWM_CONTROL 0 +#define BCM2835_PWM_STATUS 1 +#define BCM2835_PWM_DMAC 2 +#define BCM2835_PWM0_RANGE 4 +#define BCM2835_PWM0_DATA 5 +#define BCM2835_PWM_FIF1 6 +#define BCM2835_PWM1_RANGE 8 +#define BCM2835_PWM1_DATA 9 /* Defines for PWM Clock, word offsets (ie 4 byte multiples) */ -#define BCM2835_PWMCLK_CNTL 40 /*!< PWM Clock Control */ -#define BCM2835_PWMCLK_DIV 41 /*!< PWM Clock Divider */ +#define BCM2835_PWMCLK_CNTL 40 +#define BCM2835_PWMCLK_DIV 41 #define BCM2835_PWM_PASSWRD (0x5A << 24) /*!< Password to enable setting PWM clock */ #define BCM2835_PWM1_MS_MODE 0x8000 /*!< Run in Mark/Space mode */ @@ -998,8 +1189,6 @@ typedef enum { BCM2835_PWM_CLOCK_DIVIDER_1 = 1 /*!< 1 = 4.6875kHz, same as divider 4096 */ } bcm2835PWMClockDivider; -/*! @} */ - /* Historical name compatibility */ #ifndef BCM2835_NO_DELAY_COMPATIBILITY #define delay(x) bcm2835_delay(x) @@ -1010,8 +1199,7 @@ typedef enum { extern "C" { #endif -/*! \defgroup BCM2835initgrp Library initialisation and management - \ingroup BCM2835grp +/*! \defgroup init Library initialisation and management These functions allow you to intialise and control the bcm2835 library @{ */ @@ -1052,8 +1240,7 @@ extern unsigned int bcm2835_version(void); /*! @} */ -/*! \defgroup BCM2835lowlevelgrp Low level register access - \ingroup BCM2835grp +/*! \defgroup lowlevel Low level register access These functions provide low level register access, and should not generally need to be used @@ -1133,8 +1320,7 @@ extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); /*! @} end of lowlevel */ -/*! \defgroup BCM2835gpiogrp GPIO register access - \ingroup BCM2835grp +/*! \defgroup gpio GPIO register access These functions allow you to control the GPIO interface. You can set the function of each GPIO pin, read the input state and set the output state. @{ @@ -1295,6 +1481,8 @@ extern void bcm2835_gpio_clr_afen(uint8_t pin); used with bcm2835_gpio_pudclk() to set the Pull-up/down resistor for the given pin. However, it is usually more convenient to use bcm2835_gpio_set_pud(). \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + On the RPI 4, although this function and bcm2835_gpio_pudclk() are supported for backward + compatibility, new code should always use bcm2835_gpio_set_pud(). \sa bcm2835_gpio_set_pud() */ extern void bcm2835_gpio_pud(uint8_t pud); @@ -1303,17 +1491,23 @@ extern void bcm2835_gpio_pud(uint8_t pud); \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. LOW to remove the clock. + + On the RPI 4, although this function and bcm2835_gpio_pud() are supported for backward + compatibility, new code should always use bcm2835_gpio_set_pud(). + \sa bcm2835_gpio_set_pud() */ extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on); /*! Reads and returns the Pad Control for the given GPIO group. + Caution: requires root access. \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* \return Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup */ extern uint32_t bcm2835_gpio_pad(uint8_t group); /*! Sets the Pad Control for the given GPIO group. + Caution: requires root access. \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* \param[in] control Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup. Note that it is not necessary to include BCM2835_PAD_PASSWRD in the mask as this @@ -1373,10 +1567,17 @@ extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask); */ extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); +/*! On the BCM2711 based RPI 4, gets the current Pull-up/down mode for the specified pin. + Returns one of BCM2835_GPIO_PUD_* from bcm2835PUDControl. + On earlier RPI versions not based on the BCM2711, returns BCM2835_GPIO_PUD_ERROR + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. +*/ + +extern uint8_t bcm2835_gpio_get_pud(uint8_t pin); + /*! @} */ -/*! \defgroup BCM2835spigrp SPI access - \ingroup BCM2835grp +/*! \defgroup spi SPI access These functions let you use SPI0 (Serial Peripheral Interface) to interface with an external SPI device. @{ @@ -1399,8 +1600,9 @@ extern int bcm2835_spi_begin(void); extern void bcm2835_spi_end(void); /*! Sets the SPI bit order - NOTE: has no effect. Not supported by SPI0. - Defaults to + Set the bit order to be used for transmit and receive. The bcm2835 SPI0 only supports BCM2835_SPI_BIT_ORDER_MSB, + so if you select BCM2835_SPI_BIT_ORDER_LSB, the bytes will be reversed in software. + The library defaults to BCM2835_SPI_BIT_ORDER_MSB. \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, see \ref bcm2835SPIBitOrder */ @@ -1413,6 +1615,12 @@ extern void bcm2835_spi_setBitOrder(uint8_t order); */ extern void bcm2835_spi_setClockDivider(uint16_t divider); +/*! Sets the SPI clock divider by converting the speed parameter to + the equivalent SPI clock divider. ( see \sa bcm2835_spi_setClockDivider) + \param[in] speed_hz The desired SPI clock speed in Hz +*/ +extern void bcm2835_spi_set_speed_hz(uint32_t speed_hz); + /*! Sets the SPI data mode Sets the clock polariy and phase \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, @@ -1476,14 +1684,83 @@ extern void bcm2835_spi_transfern(char* buf, uint32_t len); Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) during the transfer. \param[in] buf Buffer of bytes to send. + \param[in] len Number of bytes in the buf buffer, and the number of bytes to send +*/ +extern void bcm2835_spi_writenb(const char* buf, uint32_t len); + +/*! Transfers half-word to and from the currently selected SPI slave. + Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + during the transfer. + Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. + Returns the read data byte from the slave. + Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + \param[in] data The 8 bit data byte to write to MOSI + \sa bcm2835_spi_writenb() +*/ +extern void bcm2835_spi_write(uint16_t data); + +/*! Start AUX SPI operations. + Forces RPi AUX SPI pins P1-36 (MOSI), P1-38 (MISO), P1-40 (CLK) and P1-36 (CE2) + to alternate function ALT4, which enables those pins for SPI interface. + \return 1 if successful, 0 otherwise (perhaps because you are not running as root) +*/ +extern int bcm2835_aux_spi_begin(void); + +/*! End AUX SPI operations. + SPI1 pins P1-36 (MOSI), P1-38 (MISO), P1-40 (CLK) and P1-36 (CE2) + are returned to their default INPUT behaviour. + */ +extern void bcm2835_aux_spi_end(void); + +/*! Sets the AUX SPI clock divider and therefore the AUX SPI clock speed. + \param[in] divider The desired AUX SPI clock divider. +*/ +extern void bcm2835_aux_spi_setClockDivider(uint16_t divider); + +/*! + * Calculates the input for \sa bcm2835_aux_spi_setClockDivider + * @param speed_hz A value between \sa BCM2835_AUX_SPI_CLOCK_MIN and \sa BCM2835_AUX_SPI_CLOCK_MAX + * @return Input for \sa bcm2835_aux_spi_setClockDivider + */ +extern uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz); + +/*! Transfers half-word to and from the AUX SPI slave. + Asserts the currently selected CS pins during the transfer. + \param[in] data The 8 bit data byte to write to MOSI + \return The 8 bit byte simultaneously read from MISO + \sa bcm2835_spi_transfern() +*/ +extern void bcm2835_aux_spi_write(uint16_t data); + +/*! Transfers any number of bytes to the AUX SPI slave. + Asserts the CE2 pin during the transfer. + \param[in] buf Buffer of bytes to send. \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send */ -extern void bcm2835_spi_writenb(char* buf, uint32_t len); +extern void bcm2835_aux_spi_writenb(const char *buf, uint32_t len); + +/*! Transfers any number of bytes to and from the AUX SPI slave + using bcm2835_aux_spi_transfernb. + The returned data from the slave replaces the transmitted data in the buffer. + \param[in,out] buf Buffer of bytes to send. Received bytes will replace the contents + \param[in] len Number of bytes int eh buffer, and the number of bytes to send/received + \sa bcm2835_aux_spi_transfer() +*/ +extern void bcm2835_aux_spi_transfern(char *buf, uint32_t len); + +/*! Transfers any number of bytes to and from the AUX SPI slave. + Asserts the CE2 pin during the transfer. + Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO. + The data read read from the slave is placed into rbuf. rbuf must be at least len bytes long + \param[in] tbuf Buffer of bytes to send. + \param[out] rbuf Received bytes will by put in this buffer + \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send/received +*/ +extern void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len); /*! @} */ -/*! \defgroup BCM2835i2cgrp I2C access - \ingroup BCM2835grp +/*! \defgroup i2c I2C access These functions let you use I2C (The Broadcom Serial Control bus with the Philips I2C bus/interface version 2.1 January 2000.) to interface with an external I2C device. @{ @@ -1571,8 +1848,7 @@ extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* bu /*! @} */ -/*! \defgroup BCM2835stgrp System Timer access - \ingroup BCM2835grp +/*! \defgroup st System Timer access Allows access to and delays using the System Timer Counter. @{ */ @@ -1590,8 +1866,7 @@ extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); /*! @} */ -/*! \defgroup BCM2835pwmgrp Pulse Width Modulation - \ingroup BCM2835grp +/*! \defgroup pwm Pulse Width Modulation Allows control of 2 independent PWM channels. A limited subset of GPIO pins can be connected to one of these 2 channels, allowing PWM control of GPIO pins. You have to set the desired pin into a particular Alt Fun to PWM output. See the PWM @@ -1635,3 +1910,42 @@ extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data); #endif #endif /* BCM2835_H */ + +/*! @example blink.c + Blinks RPi GPIO pin 11 on and off +*/ + +/*! @example input.c + Reads the state of an RPi input pin +*/ + +/*! @example event.c + Shows how to use event detection on an input pin +*/ + +/*! @example spi.c + Shows how to use SPI interface to transfer a byte to and from an SPI device +*/ + +/*! @example spin.c + Shows how to use SPI interface to transfer a number of bytes to and from an SPI device +*/ + +/*! @example pwm.c + Shows how to use PWM to control GPIO pins +*/ + +/*! @example i2c.c +Command line utility for executing i2c commands with the +Broadcom bcm2835. Contributed by Shahrooz Shahparnia. +*/ + +/*! example gpio.c + Command line utility for executing gpio commands with the + Broadcom bcm2835. Contributed by Shahrooz Shahparnia. +*/ + +/*! example spimem_test.c + Shows how to use the included little library (spiram.c and spiram.h) + to read and write SPI RAM chips such as 23K256-I/P +*/ diff --git a/drivers/Linux/Arduino.h b/hal/architecture/Linux/drivers/core/Arduino.h similarity index 98% rename from drivers/Linux/Arduino.h rename to hal/architecture/Linux/drivers/core/Arduino.h index 8656379cf..d683f9c7f 100644 --- a/drivers/Linux/Arduino.h +++ b/hal/architecture/Linux/drivers/core/Arduino.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/Client.h b/hal/architecture/Linux/drivers/core/Client.h similarity index 100% rename from drivers/Linux/Client.h rename to hal/architecture/Linux/drivers/core/Client.h diff --git a/drivers/Linux/EthernetClient.cpp b/hal/architecture/Linux/drivers/core/EthernetClient.cpp similarity index 99% rename from drivers/Linux/EthernetClient.cpp rename to hal/architecture/Linux/drivers/core/EthernetClient.cpp index 831284196..41686b3c8 100644 --- a/drivers/Linux/EthernetClient.cpp +++ b/hal/architecture/Linux/drivers/core/EthernetClient.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/EthernetClient.h b/hal/architecture/Linux/drivers/core/EthernetClient.h similarity index 99% rename from drivers/Linux/EthernetClient.h rename to hal/architecture/Linux/drivers/core/EthernetClient.h index 1f65609bd..5fac695e9 100644 --- a/drivers/Linux/EthernetClient.h +++ b/hal/architecture/Linux/drivers/core/EthernetClient.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/EthernetServer.cpp b/hal/architecture/Linux/drivers/core/EthernetServer.cpp similarity index 99% rename from drivers/Linux/EthernetServer.cpp rename to hal/architecture/Linux/drivers/core/EthernetServer.cpp index 8a4a16150..b21493327 100644 --- a/drivers/Linux/EthernetServer.cpp +++ b/hal/architecture/Linux/drivers/core/EthernetServer.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/EthernetServer.h b/hal/architecture/Linux/drivers/core/EthernetServer.h similarity index 98% rename from drivers/Linux/EthernetServer.h rename to hal/architecture/Linux/drivers/core/EthernetServer.h index 4c98817d0..d0f03aef3 100644 --- a/drivers/Linux/EthernetServer.h +++ b/hal/architecture/Linux/drivers/core/EthernetServer.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/GPIO.cpp b/hal/architecture/Linux/drivers/core/GPIO.cpp similarity index 95% rename from drivers/Linux/GPIO.cpp rename to hal/architecture/Linux/drivers/core/GPIO.cpp index 84e9f4547..62e4ef582 100644 --- a/drivers/Linux/GPIO.cpp +++ b/hal/architecture/Linux/drivers/core/GPIO.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -50,7 +50,7 @@ GPIOClass::GPIOClass() } if (strncmp("gpiochip", de->d_name, 8) == 0) { - sprintf(file, "/sys/class/gpio/%s/base", de->d_name); + snprintf(file, sizeof(file), "/sys/class/gpio/%s/base", de->d_name); f = fopen(file, "r"); int base; if (fscanf(f, "%d", &base) == EOF) { @@ -59,7 +59,7 @@ GPIOClass::GPIOClass() } fclose(f); - sprintf(file, "/sys/class/gpio/%s/ngpio", de->d_name); + snprintf(file, sizeof(file), "/sys/class/gpio/%s/ngpio", de->d_name); f = fopen(file, "r"); int ngpio; if (fscanf(f, "%d", &ngpio) == EOF) { diff --git a/drivers/Linux/GPIO.h b/hal/architecture/Linux/drivers/core/GPIO.h similarity index 98% rename from drivers/Linux/GPIO.h rename to hal/architecture/Linux/drivers/core/GPIO.h index 3fc238ec1..48ecabd11 100644 --- a/drivers/Linux/GPIO.h +++ b/hal/architecture/Linux/drivers/core/GPIO.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/IPAddress.cpp b/hal/architecture/Linux/drivers/core/IPAddress.cpp similarity index 100% rename from drivers/Linux/IPAddress.cpp rename to hal/architecture/Linux/drivers/core/IPAddress.cpp diff --git a/drivers/Linux/IPAddress.h b/hal/architecture/Linux/drivers/core/IPAddress.h similarity index 100% rename from drivers/Linux/IPAddress.h rename to hal/architecture/Linux/drivers/core/IPAddress.h diff --git a/drivers/Linux/Print.cpp b/hal/architecture/Linux/drivers/core/Print.cpp similarity index 100% rename from drivers/Linux/Print.cpp rename to hal/architecture/Linux/drivers/core/Print.cpp diff --git a/drivers/Linux/Print.h b/hal/architecture/Linux/drivers/core/Print.h similarity index 100% rename from drivers/Linux/Print.h rename to hal/architecture/Linux/drivers/core/Print.h diff --git a/drivers/Linux/SPI.h b/hal/architecture/Linux/drivers/core/SPI.h similarity index 95% rename from drivers/Linux/SPI.h rename to hal/architecture/Linux/drivers/core/SPI.h index b7d373296..972484f64 100644 --- a/drivers/Linux/SPI.h +++ b/hal/architecture/Linux/drivers/core/SPI.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/SPIDEV.cpp b/hal/architecture/Linux/drivers/core/SPIDEV.cpp similarity index 99% rename from drivers/Linux/SPIDEV.cpp rename to hal/architecture/Linux/drivers/core/SPIDEV.cpp index abc3bf29e..061792842 100644 --- a/drivers/Linux/SPIDEV.cpp +++ b/hal/architecture/Linux/drivers/core/SPIDEV.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/SPIDEV.h b/hal/architecture/Linux/drivers/core/SPIDEV.h similarity index 99% rename from drivers/Linux/SPIDEV.h rename to hal/architecture/Linux/drivers/core/SPIDEV.h index 683f65ab9..29110c2a4 100644 --- a/drivers/Linux/SPIDEV.h +++ b/hal/architecture/Linux/drivers/core/SPIDEV.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/SerialPort.cpp b/hal/architecture/Linux/drivers/core/SerialPort.cpp similarity index 99% rename from drivers/Linux/SerialPort.cpp rename to hal/architecture/Linux/drivers/core/SerialPort.cpp index 0f96eb848..baa515821 100644 --- a/drivers/Linux/SerialPort.cpp +++ b/hal/architecture/Linux/drivers/core/SerialPort.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/SerialPort.h b/hal/architecture/Linux/drivers/core/SerialPort.h similarity index 98% rename from drivers/Linux/SerialPort.h rename to hal/architecture/Linux/drivers/core/SerialPort.h index a6e9cef95..540d9b288 100644 --- a/drivers/Linux/SerialPort.h +++ b/hal/architecture/Linux/drivers/core/SerialPort.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/Server.h b/hal/architecture/Linux/drivers/core/Server.h similarity index 100% rename from drivers/Linux/Server.h rename to hal/architecture/Linux/drivers/core/Server.h diff --git a/drivers/Linux/SoftEeprom.cpp b/hal/architecture/Linux/drivers/core/SoftEeprom.cpp similarity index 98% rename from drivers/Linux/SoftEeprom.cpp rename to hal/architecture/Linux/drivers/core/SoftEeprom.cpp index f8e3241ef..d57af53ab 100644 --- a/drivers/Linux/SoftEeprom.cpp +++ b/hal/architecture/Linux/drivers/core/SoftEeprom.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -98,9 +98,11 @@ void SoftEeprom::destroy() { if (_values) { delete[] _values; + _values = NULL; } if (_fileName) { free(_fileName); + _fileName = NULL; } _length = 0; } diff --git a/drivers/Linux/SoftEeprom.h b/hal/architecture/Linux/drivers/core/SoftEeprom.h similarity index 98% rename from drivers/Linux/SoftEeprom.h rename to hal/architecture/Linux/drivers/core/SoftEeprom.h index 6630255d9..cd32cd630 100644 --- a/drivers/Linux/SoftEeprom.h +++ b/hal/architecture/Linux/drivers/core/SoftEeprom.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/StdInOutStream.cpp b/hal/architecture/Linux/drivers/core/StdInOutStream.cpp similarity index 96% rename from drivers/Linux/StdInOutStream.cpp rename to hal/architecture/Linux/drivers/core/StdInOutStream.cpp index 0a8c7a875..905f3d3d9 100644 --- a/drivers/Linux/StdInOutStream.cpp +++ b/hal/architecture/Linux/drivers/core/StdInOutStream.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/StdInOutStream.h b/hal/architecture/Linux/drivers/core/StdInOutStream.h similarity index 97% rename from drivers/Linux/StdInOutStream.h rename to hal/architecture/Linux/drivers/core/StdInOutStream.h index f991181c9..8dab724c7 100644 --- a/drivers/Linux/StdInOutStream.h +++ b/hal/architecture/Linux/drivers/core/StdInOutStream.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/Stream.cpp b/hal/architecture/Linux/drivers/core/Stream.cpp similarity index 100% rename from drivers/Linux/Stream.cpp rename to hal/architecture/Linux/drivers/core/Stream.cpp diff --git a/drivers/Linux/Stream.h b/hal/architecture/Linux/drivers/core/Stream.h similarity index 100% rename from drivers/Linux/Stream.h rename to hal/architecture/Linux/drivers/core/Stream.h diff --git a/drivers/Linux/compatibility.cpp b/hal/architecture/Linux/drivers/core/compatibility.cpp similarity index 98% rename from drivers/Linux/compatibility.cpp rename to hal/architecture/Linux/drivers/core/compatibility.cpp index d15acec23..febd3d473 100644 --- a/drivers/Linux/compatibility.cpp +++ b/hal/architecture/Linux/drivers/core/compatibility.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/config.c b/hal/architecture/Linux/drivers/core/config.c similarity index 99% rename from drivers/Linux/config.c rename to hal/architecture/Linux/drivers/core/config.c index 6e50205ca..23c717e52 100644 --- a/drivers/Linux/config.c +++ b/hal/architecture/Linux/drivers/core/config.c @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/config.h b/hal/architecture/Linux/drivers/core/config.h similarity index 96% rename from drivers/Linux/config.h rename to hal/architecture/Linux/drivers/core/config.h index 027993a22..ddea21c3b 100644 --- a/drivers/Linux/config.h +++ b/hal/architecture/Linux/drivers/core/config.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/interrupt.cpp b/hal/architecture/Linux/drivers/core/interrupt.cpp similarity index 96% rename from drivers/Linux/interrupt.cpp rename to hal/architecture/Linux/drivers/core/interrupt.cpp index 395c2175c..f2efce2c5 100644 --- a/drivers/Linux/interrupt.cpp +++ b/hal/architecture/Linux/drivers/core/interrupt.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -49,10 +49,10 @@ static pthread_t *threadIds[64] = {NULL}; // Map a file descriptor from the /sys/class/gpio/gpioX/value static int sysFds[64] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -}; + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; /* * Part of wiringPi: Simple way to get your program running at high priority diff --git a/drivers/Linux/interrupt.h b/hal/architecture/Linux/drivers/core/interrupt.h similarity index 96% rename from drivers/Linux/interrupt.h rename to hal/architecture/Linux/drivers/core/interrupt.h index 13050e4c9..0a4fc01c1 100644 --- a/drivers/Linux/interrupt.h +++ b/hal/architecture/Linux/drivers/core/interrupt.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/log.c b/hal/architecture/Linux/drivers/core/log.c similarity index 98% rename from drivers/Linux/log.c rename to hal/architecture/Linux/drivers/core/log.c index 6834ccad7..7f6c6b407 100644 --- a/drivers/Linux/log.c +++ b/hal/architecture/Linux/drivers/core/log.c @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -140,6 +140,7 @@ void vlog(int level, const char *fmt, va_list args) if (_log_file_fp != NULL) { fprintf(_log_file_fp, "%s %-5s ", date, _log_level_names[level]); vfprintf(_log_file_fp, fmt, args); + fflush(_log_file_fp); } if (!_log_quiet) { diff --git a/drivers/Linux/log.h b/hal/architecture/Linux/drivers/core/log.h similarity index 98% rename from drivers/Linux/log.h rename to hal/architecture/Linux/drivers/core/log.h index 9498b14cf..584c4b8eb 100644 --- a/drivers/Linux/log.h +++ b/hal/architecture/Linux/drivers/core/log.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/drivers/Linux/noniso.cpp b/hal/architecture/Linux/drivers/core/noniso.cpp similarity index 100% rename from drivers/Linux/noniso.cpp rename to hal/architecture/Linux/drivers/core/noniso.cpp diff --git a/drivers/Linux/stdlib_noniso.h b/hal/architecture/Linux/drivers/core/stdlib_noniso.h similarity index 100% rename from drivers/Linux/stdlib_noniso.h rename to hal/architecture/Linux/drivers/core/stdlib_noniso.h diff --git a/hal/architecture/MyHwHAL.cpp b/hal/architecture/MyHwHAL.cpp new file mode 100644 index 000000000..19edd9ed4 --- /dev/null +++ b/hal/architecture/MyHwHAL.cpp @@ -0,0 +1,71 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 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 "MyHwHAL.h" + +void hwDebugPrint(const char *fmt, ...) +{ +#ifndef MY_DISABLED_SERIAL +#if !defined(__linux__) + char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; +#ifdef MY_GATEWAY_SERIAL + // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) + snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";"), C_INTERNAL, + I_LOG_MESSAGE); + MY_DEBUGDEVICE.print(fmtBuffer); +#endif + // prepend timestamp + MY_DEBUGDEVICE.print(hwMillis()); + MY_DEBUGDEVICE.print(" "); + va_list args; + va_start(args, fmt); + vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); +#ifdef MY_GATEWAY_SERIAL + // Truncate message if this is gateway node + fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; + fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; +#endif + va_end(args); + MY_DEBUGDEVICE.print(fmtBuffer); + MY_DEBUGDEVICE.flush(); +#else + va_list args; + va_start(args, fmt); + vlogDebug(fmt, args); + va_end(args); +#endif +#else + (void)fmt; +#endif +} + +#if defined(DEBUG_OUTPUT_ENABLED) +static char hwDebugPrintStr[65]; +static void hwDebugBuf2Str(const uint8_t *buf, size_t sz) +{ + if (sz > 32) { + sz = 32; //clamp to 32 bytes + } + for (uint8_t i = 0; i < sz; i++) { + hwDebugPrintStr[i * 2] = convertI2H(buf[i] >> 4); + hwDebugPrintStr[(i * 2) + 1] = convertI2H(buf[i]); + } + hwDebugPrintStr[sz * 2] = '\0'; +} +#endif diff --git a/hal/architecture/MyHwHAL.h b/hal/architecture/MyHwHAL.h index 2e709ae1f..f7e407a67 100644 --- a/hal/architecture/MyHwHAL.h +++ b/hal/architecture/MyHwHAL.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -27,11 +27,27 @@ #define MyHwHAL_h /** - * @def MY_HWID_PADDING_BYTE - * @brief HwID padding byte - */ +* @def INVALID_INTERRUPT_NUM +* @brief Invalid interrupt +*/ +#define INVALID_INTERRUPT_NUM (0xFFu) + +/** +* @def MY_HWID_PADDING_BYTE +* @brief HwID padding byte +*/ #define MY_HWID_PADDING_BYTE (0xAAu) +/** +* @def IRQ_HANDLER_ATTR +* @brief ESP8266/ESP32 IRQ handlers need to be stored in IRAM +*/ +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR +#else +#define IRQ_HANDLER_ATTR +#endif + // Implement these as functions or macros /* #define hwInit() MY_SERIALDEVICE.begin(BAUD_RATE) @@ -74,7 +90,7 @@ int8_t hwSleep(uint32_t ms); * @param ms Time to sleep, in [ms]. * @return MY_WAKE_UP_BY_TIMER when woken by timer, or interrupt number when woken by interrupt. */ -int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms); +int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms); /** * Sleep for a defined time, using minimum power, or until woken by one of the interrupts. @@ -85,7 +101,8 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms); * @param ms Time to sleep, in [ms]. * @return MY_WAKE_UP_BY_TIMER 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, +int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, + const uint8_t mode2, uint32_t ms); /** @@ -121,7 +138,17 @@ int8_t hwCPUTemperature(void); uint16_t hwFreeMem(void); #if defined(DEBUG_OUTPUT_ENABLED) -void hwDebugPrint(const char *fmt, ... ); +/** + * Debug print + * @param fmt + */ +void hwDebugPrint(const char *fmt, ...); +/** + * Convert buffer to hex string + * @param buf + * @param sz + */ +static void hwDebugBuf2Str(const uint8_t *buf, size_t sz) __attribute__((unused)); #endif /** diff --git a/hal/architecture/NRF5/MyHwNRF5.cpp b/hal/architecture/NRF5/MyHwNRF5.cpp index 17adf6e28..3b5dff398 100644 --- a/hal/architecture/NRF5/MyHwNRF5.cpp +++ b/hal/architecture/NRF5/MyHwNRF5.cpp @@ -7,7 +7,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Copyright (C) 2017 Frank Holtz * Full contributor list: * https://github.com/mysensors/MySensors/graphs/contributors @@ -21,11 +21,6 @@ */ #include "MyHwNRF5.h" -#include - -#define CRYPTO_LITTLE_ENDIAN - -#define INVALID_INTERRUPT_NUM (0xFFu) volatile uint8_t _wokeUpByInterrupt = INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu. volatile uint8_t _wakeUp1Interrupt = @@ -161,7 +156,7 @@ void hwRandomNumberInit(void) while (NRF_RNG->EVENTS_VALRDY == 0) { yield(); } - ecbstruct[i] = NRF_RNG->VALUE; + *(ecbstruct + i) = NRF_RNG->VALUE; NRF_RNG->EVENTS_VALRDY = 0; } hwRndDataReadPos = 0; @@ -209,8 +204,8 @@ ssize_t hwGetentropy(void *__buffer, size_t __length) } } hwRndDataReadPos=0; - for (uint8_t i=0; i - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Copyright (C) 2017 Frank Holtz * Full contributor list: * https://github.com/mysensors/MySensors/graphs/contributors @@ -51,10 +51,11 @@ #include "hal/architecture/NRF5/drivers/nrf5_wiring_digital.c" #include "hal/architecture/NRF5/drivers/wdt.h" #include "hal/architecture/NRF5/drivers/nrf_temp.h" -#include "drivers/NVM/NVRAM.h" -#include "drivers/NVM/VirtualPage.h" +#include "drivers/NVM/NVRAM.cpp" +#include "drivers/NVM/VirtualPage.cpp" #include #include +#include // mapping #ifndef strncpy_P @@ -113,6 +114,8 @@ #define hwDigitalRead(__pin) digitalRead(__pin) #define hwPinMode(__pin, __value) nrf5_pinMode(__pin, __value) #define hwMillis() millis() +// TODO: Can nrf5 determine time slept? +#define hwGetSleepRemaining() (0ul) bool hwInit(void); void hwWatchdogReset(void); @@ -125,6 +128,13 @@ void hwRandomNumberInit(void); ssize_t hwGetentropy(void *__buffer, size_t __length); #define MY_HW_HAS_GETENTROPY +// SOFTSPI +#ifdef MY_SOFTSPI +#error Soft SPI is not available on this architecture! +#endif +#define hwSPI SPI //!< hwSPI + + /** * Disable all interrupts. * Helper function for MY_CRITICAL_SECTION. diff --git a/hal/architecture/NRF5/MyMainNRF5.cpp b/hal/architecture/NRF5/MyMainNRF5.cpp index d2792d1a4..dd02675f6 100644 --- a/hal/architecture/NRF5/MyMainNRF5.cpp +++ b/hal/architecture/NRF5/MyMainNRF5.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/architecture/NRF5/drivers/Flash.cpp b/hal/architecture/NRF5/drivers/Flash.cpp index bc7720edd..0e7965c04 100644 --- a/hal/architecture/NRF5/drivers/Flash.cpp +++ b/hal/architecture/NRF5/drivers/Flash.cpp @@ -143,6 +143,7 @@ void FlashClass::write_block(uint32_t *dst_address, uint32_t *src_address, wait_for_ready(); while (word_count > 0) { + // cppcheck-suppress duplicateConditionalAssign if (*dst_address != *src_address) { *dst_address = *src_address; } diff --git a/hal/architecture/NRF5/drivers/nrf_temp.h b/hal/architecture/NRF5/drivers/nrf_temp.h index 007fc7d9a..ae5c2ecf7 100644 --- a/hal/architecture/NRF5/drivers/nrf_temp.h +++ b/hal/architecture/NRF5/drivers/nrf_temp.h @@ -36,13 +36,6 @@ extern "C" { #endif -/** -* @defgroup nrf_temp_hal TEMP HAL -* @{ -* @ingroup nrf_temp temperature_example -* @brief Temperature module init and read functions. -*/ - #define MASK_SIGN (0x00000200UL) //!< MASK_SIGN #define MASK_SIGN_EXTENSION (0xFFFFFC00UL) //!< MASK_SIGN_EXTENSION @@ -69,7 +62,6 @@ static __INLINE int32_t nrf_temp_read(void) (NRF_TEMP->TEMP); } -/** @} */ #ifdef __cplusplus } diff --git a/hal/architecture/SAMD/MyHwSAMD.cpp b/hal/architecture/SAMD/MyHwSAMD.cpp index 45cc3709c..c225ddbb8 100644 --- a/hal/architecture/SAMD/MyHwSAMD.cpp +++ b/hal/architecture/SAMD/MyHwSAMD.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -106,7 +106,7 @@ int8_t hwSleep(uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) +int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms) { // TODO: Not supported! (void)interrupt; @@ -115,7 +115,8 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, +int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, + const uint8_t mode2, uint32_t ms) { // TODO: Not supported! @@ -253,35 +254,3 @@ uint16_t hwFreeMem(void) // TODO: Not supported! return FUNCTION_NOT_SUPPORTED; } - -void hwDebugPrint(const char *fmt, ... ) -{ -#ifndef MY_DISABLED_SERIAL - if (MY_DEBUGDEVICE) { - char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; -#ifdef MY_GATEWAY_SERIAL - // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) - snprintf(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), - C_INTERNAL, I_LOG_MESSAGE, hwMillis()); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - // prepend timestamp - MY_DEBUGDEVICE.print(hwMillis()); - MY_DEBUGDEVICE.print(" "); -#endif - va_list args; - va_start(args, fmt); - vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args); -#ifdef MY_GATEWAY_SERIAL - // Truncate message if this is gateway node - fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; - fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; -#endif - va_end(args); - MY_DEBUGDEVICE.print(fmtBuffer); - // MY_SERIALDEVICE.flush(); - } -#else - (void)fmt; -#endif -} diff --git a/hal/architecture/SAMD/MyHwSAMD.h b/hal/architecture/SAMD/MyHwSAMD.h index 09f26e0ce..3b294001a 100644 --- a/hal/architecture/SAMD/MyHwSAMD.h +++ b/hal/architecture/SAMD/MyHwSAMD.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,12 +19,13 @@ #ifndef MyHwSAMD_h #define MyHwSAMD_h +#include +#include + #ifdef __cplusplus #include #endif -#include - #define CRYPTO_LITTLE_ENDIAN #ifndef MY_SERIALDEVICE @@ -56,12 +57,51 @@ extEEPROM eep(MY_EXT_EEPROM_SIZE, 1, MY_EXT_EEPROM_PAGE_SIZE, #define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__) #define vsnprintf_P(s, n, f, ...) vsnprintf((s), (n), (f), __VA_ARGS__) +// redefine 8 bit types of inttypes.h (as of SAMD board defs 1.8.1) +#undef PRId8 +#undef PRIi8 +#undef PRIo8 +#undef PRIu8 +#undef PRIx8 +#undef PRIX8 +#undef PRIdLEAST8 +#undef PRIiLEAST8 +#undef PRIoLEAST8 +#undef PRIuLEAST8 +#undef PRIxLEAST8 +#undef PRIXLEAST8 +#undef PRIdFAST8 +#undef PRIiFAST8 +#undef PRIoFAST8 +#undef PRIuFAST8 +#undef PRIxFAST8 +#undef PRIXFAST8 +#define PRId8 "d" +#define PRIi8 "i" +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + // 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)) +#define hwGetSleepRemaining() (0ul) bool hwInit(void); void hwWatchdogReset(void); @@ -71,6 +111,13 @@ void hwWriteConfigBlock(void *buf, void *addr, size_t length); void hwWriteConfig(const int addr, uint8_t value); uint8_t hwReadConfig(const int addr); +// SOFTSPI +#ifdef MY_SOFTSPI +#error Soft SPI is not available on this architecture! +#endif +#define hwSPI SPI //!< hwSPI + + /** * Disable all interrupts. * Helper function for MY_CRITICAL_SECTION. diff --git a/hal/architecture/SAMD/MyMainSAMD.cpp b/hal/architecture/SAMD/MyMainSAMD.cpp index ed4537a6d..cf734e62e 100644 --- a/hal/architecture/SAMD/MyMainSAMD.cpp +++ b/hal/architecture/SAMD/MyMainSAMD.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/architecture/STM32F1/MyHwSTM32F1.cpp b/hal/architecture/STM32F1/MyHwSTM32F1.cpp index 9934cc8f1..3065ee73f 100644 --- a/hal/architecture/STM32F1/MyHwSTM32F1.cpp +++ b/hal/architecture/STM32F1/MyHwSTM32F1.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -99,7 +99,7 @@ int8_t hwSleep(uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) +int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms) { // TODO: Not supported! (void)interrupt; @@ -108,7 +108,8 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, +int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, + const uint8_t mode2, uint32_t ms) { // TODO: Not supported! @@ -162,7 +163,7 @@ uint16_t hwCPUVoltage(void) const uint16_t vdd = adc_read(ADC1, 17); regs->CR2 &= ~ADC_CR2_TSVREFE; // disable VREFINT and temp sensor - return 1200 * 4096 / vdd; + return (uint16_t)(1200u * 4096u / vdd); } uint16_t hwCPUFrequency(void) @@ -191,35 +192,3 @@ uint16_t hwFreeMem(void) //Not yet implemented return FUNCTION_NOT_SUPPORTED; } - -void hwDebugPrint(const char *fmt, ...) -{ -#ifndef MY_DISABLED_SERIAL - char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; -#ifdef MY_GATEWAY_SERIAL - // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) - snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), - C_INTERNAL, I_LOG_MESSAGE, hwMillis()); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - // prepend timestamp - MY_DEBUGDEVICE.print(hwMillis()); - MY_DEBUGDEVICE.print(F(" ")); -#endif - va_list args; - va_start(args, fmt); - vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); -#ifdef MY_GATEWAY_SERIAL - // Truncate message if this is gateway node - fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; - fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; -#endif - va_end(args); - MY_DEBUGDEVICE.print(fmtBuffer); - // Disable flush since current STM32duino implementation performs a reset - // instead of an actual flush - //MY_DEBUGDEVICE.flush(); -#else - (void)fmt; -#endif -} diff --git a/hal/architecture/STM32F1/MyHwSTM32F1.h b/hal/architecture/STM32F1/MyHwSTM32F1.h index 54822734e..b6e8617cd 100644 --- a/hal/architecture/STM32F1/MyHwSTM32F1.h +++ b/hal/architecture/STM32F1/MyHwSTM32F1.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -23,6 +23,8 @@ #include #include #include +#include + #ifdef __cplusplus #include #endif @@ -67,6 +69,7 @@ #define hwWatchdogReset() iwdg_feed() #define hwReboot() nvic_sys_reset() #define hwMillis() millis() +#define hwGetSleepRemaining() (0ul) extern void serialEventRun(void) __attribute__((weak)); bool hwInit(void); @@ -76,6 +79,13 @@ void hwWriteConfigBlock(void *buf, void *addr, size_t length); void hwWriteConfig(const int addr, uint8_t value); uint8_t hwReadConfig(const int addr); +// SOFTSPI +#ifdef MY_SOFTSPI +#error Soft SPI is not available on this architecture! +#endif +#define hwSPI SPI //!< hwSPI + + #ifndef DOXYGEN #define MY_CRITICAL_SECTION #endif /* DOXYGEN */ diff --git a/hal/architecture/STM32F1/MyMainSTM32F1.cpp b/hal/architecture/STM32F1/MyMainSTM32F1.cpp index d688655ad..8531997f8 100644 --- a/hal/architecture/STM32F1/MyMainSTM32F1.cpp +++ b/hal/architecture/STM32F1/MyMainSTM32F1.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/architecture/Teensy3/MyHwTeensy3.cpp b/hal/architecture/Teensy3/MyHwTeensy3.cpp index 185d8238c..bb12daca3 100644 --- a/hal/architecture/Teensy3/MyHwTeensy3.cpp +++ b/hal/architecture/Teensy3/MyHwTeensy3.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -69,7 +69,7 @@ int8_t hwSleep(uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) +int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms) { // TODO: Not supported! (void)interrupt; @@ -78,7 +78,8 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms) return MY_SLEEP_NOT_POSSIBLE; } -int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, +int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, + const uint8_t mode2, uint32_t ms) { // TODO: Not supported! @@ -108,10 +109,10 @@ uint16_t hwCPUVoltage(void) analogReadAveraging(32); #if defined(__MK20DX128__) || defined(__MK20DX256__) // Teensy 3.0/3.1/3.2 - return 1195 * 4096 / analogRead(39); + return (uint16_t)(1195u * 4096u / analogRead(39)); #elif defined(__MK64FX512__) || defined(__MK66FX1M0__) // Teensy 3.6 - return 1195 * 4096 / analogRead(71); + return (uint16_t)(1195u * 4096u / analogRead(71)); #elif defined(__MKL26Z64__) // Teensy LC // not supported @@ -171,32 +172,3 @@ void hwRandomNumberInit(void) randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)); #endif } - -void hwDebugPrint(const char *fmt, ...) -{ -#ifndef MY_DISABLED_SERIAL - char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; -#ifdef MY_GATEWAY_SERIAL - // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) - snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), - C_INTERNAL, I_LOG_MESSAGE, hwMillis()); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - // prepend timestamp - MY_DEBUGDEVICE.print(hwMillis()); - MY_DEBUGDEVICE.print(F(" ")); -#endif - va_list args; - va_start(args, fmt); - vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); -#ifdef MY_GATEWAY_SERIAL - // Truncate message if this is gateway node - fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; - fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; -#endif - va_end(args); - MY_DEBUGDEVICE.print(fmtBuffer); -#else - (void)fmt; -#endif -} diff --git a/hal/architecture/Teensy3/MyHwTeensy3.h b/hal/architecture/Teensy3/MyHwTeensy3.h index 3092dcadc..9d3446470 100644 --- a/hal/architecture/Teensy3/MyHwTeensy3.h +++ b/hal/architecture/Teensy3/MyHwTeensy3.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -28,10 +28,12 @@ #ifndef MyHwTeensy3_h #define MyHwTeensy3_h +#include +#include "util/atomic.h" + #ifdef __cplusplus #include #endif -#include "util/atomic.h" #define CRYPTO_LITTLE_ENDIAN @@ -60,6 +62,7 @@ #define hwDigitalRead(__pin) digitalReadFast(__pin) #define hwPinMode(__pin, __value) pinMode(__pin, __value) #define hwMillis() millis() +#define hwGetSleepRemaining() (0ul) void hwRandomNumberInit(void); bool hwInit(void); @@ -72,6 +75,13 @@ void hwReboot(void); #define hwReadConfigBlock(__buf, __pos, __length) eeprom_read_block((void *)__buf, (const void *)__pos, (uint32_t)__length) #define hwWriteConfigBlock(__buf, __pos, __length) eeprom_update_block((const void *)__buf, (void *)__pos, (uint32_t)__length) +// SOFTSPI +#ifdef MY_SOFTSPI +#error Soft SPI is not available on this architecture! +#endif +#define hwSPI SPI //!< hwSPI + + #if defined(__MK64FX512__) || defined(__MK66FX1M0__) #define MY_HW_HAS_GETENTROPY #endif diff --git a/hal/architecture/Teensy3/MyMainTeensy3.cpp b/hal/architecture/Teensy3/MyMainTeensy3.cpp index 630ebd27a..3fc29ded4 100644 --- a/hal/architecture/Teensy3/MyMainTeensy3.cpp +++ b/hal/architecture/Teensy3/MyMainTeensy3.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/crypto/AVR/MyCryptoAVR.cpp b/hal/crypto/AVR/MyCryptoAVR.cpp index 99732b18e..26176fcdf 100644 --- a/hal/crypto/AVR/MyCryptoAVR.cpp +++ b/hal/crypto/AVR/MyCryptoAVR.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -16,86 +16,42 @@ * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * -* ====================================================================== -* -* SHA256 and SHA256 implementation for AVR: -* -* This file is part of the AVR-Crypto-Lib. -* Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Author: Daniel Otte -* -* License: GPLv3 or later -* -* ====================================================================== */ #include "MyCryptoAVR.h" // SHA256 implementation in ASM, see MyASM.S - void SHA256(uint8_t *dest, const uint8_t *data, size_t dataLength) { sha256((sha256_hash_t *)dest, data, dataLength << 3); // data length in bits } // SHA256HMAC - -/* -* all lengths in bits! -*/ -void hmac_sha256(void *dest, const void *key, uint16_t keylength_b, const void *msg, - uint32_t msglength_b) +void SHA256HMAC(uint8_t *dest, const uint8_t *key, size_t keyLength, const uint8_t *data, + size_t dataLength) { - sha256_ctx_t s; - uint8_t buffer[HMAC_SHA256_BLOCK_BYTES]; + hmac_sha256(dest, key, keyLength << 3, data, dataLength << 3); +} - (void)memset((void *)buffer, 0x00, HMAC_SHA256_BLOCK_BYTES); - /* if key is larger than a block we have to hash it*/ - if (keylength_b > SHA256_BLOCK_BITS) { - sha256((sha256_hash_t *)buffer, key, keylength_b); - } else { - (void)memcpy((void *)buffer, (const void *)key, (keylength_b + 7) / 8); - } +// AES +AES_ctx aes_ctx; - for (uint8_t i = 0; i < SHA256_BLOCK_BYTES; ++i) { - buffer[i] ^= IPAD; - } - sha256_init(&s); - sha256_nextBlock(&s, buffer); - while (msglength_b >= HMAC_SHA256_BLOCK_BITS) { - sha256_nextBlock(&s, msg); - msg = (uint8_t *)msg + HMAC_SHA256_BLOCK_BYTES; - msglength_b -= HMAC_SHA256_BLOCK_BITS; - } - sha256_lastBlock(&s, msg, msglength_b); - /* since buffer still contains key xor ipad we can do ... */ - for (uint8_t i = 0; i < HMAC_SHA256_BLOCK_BYTES; ++i) { - buffer[i] ^= IPAD ^ OPAD; - } - sha256_ctx2hash((sha256_hash_t *)dest, &s); /* save inner hash temporary to dest */ - sha256_init(&s); - sha256_nextBlock(&s, buffer); - sha256_lastBlock(&s, dest, SHA256_HASH_BITS); - sha256_ctx2hash((sha256_hash_t *)dest, &s); +void AES128CBCInit(const uint8_t *key) +{ + AES_init_ctx(&aes_ctx, key); } -void SHA256HMAC(uint8_t *dest, const uint8_t *key, size_t keyLength, const uint8_t *data, - size_t dataLength) +void AES128CBCEncrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength) { - hmac_sha256(dest, key, keyLength << 3, data, dataLength << 3); + AES_ctx_set_iv(&aes_ctx, iv); + AES_CBC_encrypt_buffer(&aes_ctx, buffer, dataLength); +} + +void AES128CBCDecrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength) +{ + AES_ctx_set_iv(&aes_ctx, iv); + AES_CBC_decrypt_buffer(&aes_ctx, buffer, dataLength); } + + diff --git a/hal/crypto/AVR/MyCryptoAVR.h b/hal/crypto/AVR/MyCryptoAVR.h index 6711bffed..6ab384e0d 100644 --- a/hal/crypto/AVR/MyCryptoAVR.h +++ b/hal/crypto/AVR/MyCryptoAVR.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -16,142 +16,14 @@ * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * -* ====================================================================== -* -* SHA256 and SHA256 implementation for AVR: -* -* This file is part of the AVR-Crypto-Lib. -* Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Author: Daniel Otte -* -* License: GPLv3 or later -* -* ====================================================================== */ #ifndef MyCryptoGeneric_h #define MyCryptoGeneric_h #include "hal/crypto/MyCryptoHAL.h" - -#define MY_CRYPTO_SHA256_ASM //!< Switch to define correct variable scope for ASM SHA256 implementation - -#define SHA256_HASH_BITS 256 //!< Defines the size of a SHA-256 hash value in bits -#define SHA256_HASH_BYTES (SHA256_HASH_BITS/8) //!< Defines the size of a SHA-256 hash value in bytes -#define SHA256_BLOCK_BITS 512 //!< Defines the size of a SHA-256 input block in bits -#define SHA256_BLOCK_BYTES (SHA256_BLOCK_BITS/8) //!< Defines the size of a SHA-256 input block in bytes - -/** -* @brief SHA-256 context type -* -* A variable of this type may hold the state of a SHA-256 hashing process -*/ -typedef struct { - uint32_t h[8]; //!< h - uint64_t length; //!< length -} sha256_ctx_t; - -/** -* @brief SHA-256 hash value type -* -* A variable of this type may hold the hash value produced by the -* sha256_ctx2hash(sha256_hash_t *dest, const sha256_ctx_t *state) function. -*/ -typedef uint8_t sha256_hash_t[SHA256_HASH_BYTES]; - -extern "C" { // ASM implementation, see MyASM.S - -/** -* @brief initialise a SHA-256 context -* -* This function sets a ::sha256_ctx_t to the initial values for hashing. -* @param state pointer to the SHA-256 hashing context -*/ -void sha256_init(sha256_ctx_t *state); - -/** -* @brief update the context with a given block -* -* This function updates the SHA-256 hash context by processing the given block -* of fixed length. -* @param state pointer to the SHA-256 hash context -* @param block pointer to the block of fixed length (512 bit = 64 byte) -*/ -void sha256_nextBlock(sha256_ctx_t *state, const void *block); - -/** -* @brief finalize the context with the given block -* -* This function finalizes the SHA-256 hash context by processing the given block -* of variable length. -* @param state pointer to the SHA-256 hash context -* @param block pointer to the block of fixed length (512 bit = 64 byte) -* @param length_b the length of the block in bits -*/ -void sha256_lastBlock(sha256_ctx_t *state, const void *block, uint16_t length_b); - -/** -* @brief convert the hash state into the hash value -* -* This function reads the context and writes the hash value to the destination -* @param dest pointer to the location where the hash value should be written -* @param state pointer to the SHA-256 hash context -*/ -void sha256_ctx2hash(sha256_hash_t *dest, const sha256_ctx_t *state); - -/** -* @brief simple SHA-256 hashing function for direct hashing -* -* This function automaticaly hashes a given message of arbitary length with -* the SHA-256 hashing algorithm. -* @param dest pointer to the location where the hash value is going to be written to -* @param msg pointer to the message thats going to be hashed -* @param length_b length of the message in bits -*/ -void sha256(sha256_hash_t *dest, const void *msg, uint32_t length_b); -} -// MHAC SHA256 - -#define IPAD 0x36 //!< HMAC, inner hash, xor byte -#define OPAD 0x5C //!< HMAC, outer hash, xor byte - -#define HMAC_SHA256_BITS SHA256_HASH_BITS //!< Defines the size of a SHA-256 HMAC hash value in bits -#define HMAC_SHA256_BYTES SHA256_HASH_BYTES //!< Defines the size of a SHA-256 HMAC hash value in bytes -#define HMAC_SHA256_BLOCK_BITS SHA256_BLOCK_BITS //!< Defines the size of a SHA-256 HMAC input block in bits -#define HMAC_SHA256_BLOCK_BYTES SHA256_BLOCK_BYTES //!< Defines the size of a SHA-256 HMAC input block in bytes - -/** -* @brief hash context structure -*/ -typedef struct { - sha256_ctx_t a; //!< a - sha256_ctx_t b; //!< b -} hmac_sha256_ctx_t; - -/** -* @brief SHA256 HMAC function -* -* @param dest pointer to the location where the hash value is going to be written to -* @param key pointer to the key that's is needed for the HMAC calculation -* @param keylength_b length of the key -* @param msg pointer to the message thats going to be hashed -* @param msglength_b length of the message -*/ -void hmac_sha256(void *dest, const void *key, uint16_t keylength_b, const void *msg, - uint32_t msglength_b); +#include "hal/crypto/AVR/drivers/AES/aes.cpp" +#include "hal/crypto/AVR/drivers/SHA256/sha256.cpp" +#include "hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.cpp" #endif diff --git a/hal/crypto/AVR/drivers/AES/aes.cpp b/hal/crypto/AVR/drivers/AES/aes.cpp new file mode 100644 index 000000000..60b30596a --- /dev/null +++ b/hal/crypto/AVR/drivers/AES/aes.cpp @@ -0,0 +1,577 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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. +* +* AES implementation: https://github.com/kokke/tiny-AES-c +*/ + +/* + +This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. +Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. + +The implementation is verified against the test vectors in: + National Institute of Standards and Technology Special Publication 800-38A 2001 ED + +ECB-AES128 +---------- + + plain-text: + 6bc1bee22e409f96e93d7e117393172a + ae2d8a571e03ac9c9eb76fac45af8e51 + 30c81c46a35ce411e5fbc1191a0a52ef + f69f2445df4f9b17ad2b417be66c3710 + + key: + 2b7e151628aed2a6abf7158809cf4f3c + + resulting cipher + 3ad77bb40d7a3660a89ecaf32466ef97 + f5d3d58503b9699de785895a96fdbaaf + 43b1cd7f598ece23881b00e3ed030688 + 7b0c785e27e8ad3f8223207104725dd4 + + +NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) + You should pad the end of the string with zeros if this is not the case. + For AES192/256 the key size is proportionally larger. + +*/ + + +/*****************************************************************************/ +/* Includes: */ +/*****************************************************************************/ + +#include "aes.h" + +/*****************************************************************************/ +/* Defines: */ +/*****************************************************************************/ +// The number of columns comprising a state in AES. This is a constant in AES. Value=4 +#define Nb 4 + +#if defined(AES256) && (AES256 == 1) +#define Nk 8 +#define Nr 14 +#elif defined(AES192) && (AES192 == 1) +#define Nk 6 +#define Nr 12 +#else +#define Nk 4 // The number of 32 bit words in a key. +#define Nr 10 // The number of rounds in AES Cipher. +#endif + +// jcallan@github points out that declaring Multiply as a function +// reduces code size considerably with the Keil ARM compiler. +// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 +#ifndef MULTIPLY_AS_A_FUNCTION +#define MULTIPLY_AS_A_FUNCTION 0 +#endif + +/*****************************************************************************/ +/* Private variables: */ +/*****************************************************************************/ +// state - array holding the intermediate results during decryption. +typedef uint8_t state_t[4][4]; + +// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM +// The numbers below can be computed dynamically trading ROM for RAM - +// This can be useful in (embedded) bootloader applications, where ROM is often limited. +static const uint8_t sbox[256] PROGMEM = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +static const uint8_t rsbox[256] PROGMEM = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d +}; + +// The round constant word array, Rcon[i], contains the values given by +// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +static const uint8_t Rcon[11] PROGMEM = { + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 +}; + +/* + * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), + * that you can remove most of the elements in the Rcon array, because they are unused. + * + * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon + * + * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), + * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." + */ + + +/*****************************************************************************/ +/* Private functions: */ +/*****************************************************************************/ +/* +static uint8_t getSBoxValue(uint8_t num) +{ + return sbox[num]; +} +*/ +#define getSBoxValue(num) pgm_read_byte(&sbox[(num)]) +/* +static uint8_t getSBoxInvert(uint8_t num) +{ + return rsbox[num]; +} +*/ +#define getSBoxInvert(num) pgm_read_byte(&rsbox[(num)]) + +// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. +static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) +{ + unsigned i; + uint8_t tempa[4]; // Used for the column/row operations + + // The first round key is the key itself. + for (i = 0; i < Nk; ++i) { + RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; + RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; + RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; + RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; + } + + // All other round keys are found from the previous round keys. + for (i = Nk; i < Nb * (Nr + 1); ++i) { + unsigned j, k; + { + k = (i - 1) * 4; + tempa[0] = RoundKey[k + 0]; + tempa[1] = RoundKey[k + 1]; + tempa[2] = RoundKey[k + 2]; + tempa[3] = RoundKey[k + 3]; + } + + if (i % Nk == 0) { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + { + const uint8_t u8tmp = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = u8tmp; + } + + // SubWord() is a function that takes a four-byte input word and + // applies the S-box to each of the four bytes to produce an output word. + + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + + tempa[0] = tempa[0] ^ pgm_read_byte(&Rcon[i / Nk]); + } +#if defined(AES256) && (AES256 == 1) + if (i % Nk == 4) { + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + } +#endif + j = i * 4; + k = (i - Nk) * 4; + RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; + RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; + RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; + RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; + } +} + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) +{ + KeyExpansion(ctx->RoundKey, key); +} +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) +{ + KeyExpansion(ctx->RoundKey, key); + memcpy(ctx->Iv, iv, AES_BLOCKLEN); +} +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) +{ + memcpy(ctx->Iv, iv, AES_BLOCKLEN); +} +#endif + +// This function adds the round key to state. +// The round key is added to the state by an XOR function. +static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; + } + } +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void SubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + (*state)[j][i] = getSBoxValue((*state)[j][i]); + } + } +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +static void ShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to left + temp = (*state)[0][1]; + (*state)[0][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[3][1]; + (*state)[3][1] = temp; + + // Rotate second row 2 columns to left + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to left + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[3][3]; + (*state)[3][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[1][3]; + (*state)[1][3] = temp; +} + +static uint8_t xtime(uint8_t x) +{ + return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)); +} + +// MixColumns function mixes the columns of the state matrix +static void MixColumns(state_t* state) +{ + uint8_t i; + for (i = 0; i < 4; ++i) { + uint8_t Tmp, Tm, t; + t = (*state)[i][0]; + Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3]; + Tm = (*state)[i][0] ^ (*state)[i][1]; + Tm = xtime(Tm); + (*state)[i][0] ^= Tm ^ Tmp; + Tm = (*state)[i][1] ^ (*state)[i][2]; + Tm = xtime(Tm); + (*state)[i][1] ^= Tm ^ Tmp; + Tm = (*state)[i][2] ^ (*state)[i][3]; + Tm = xtime(Tm); + (*state)[i][2] ^= Tm ^ Tmp; + Tm = (*state)[i][3] ^ t; + Tm = xtime(Tm); + (*state)[i][3] ^= Tm ^ Tmp; + } +} + +// Multiply is used to multiply numbers in the field GF(2^8) +// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary +// The compiler seems to be able to vectorize the operation better this way. +// See https://github.com/kokke/tiny-AES-c/pull/34 +#if MULTIPLY_AS_A_FUNCTION +static uint8_t Multiply(uint8_t x, uint8_t y) +{ + return (((y & 1) * x) ^ + ((y >> 1 & 1) * xtime(x)) ^ + ((y >> 2 & 1) * xtime(xtime(x))) ^ + ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ +} +#else +#define Multiply(x, y) \ + ( ((y & 1) * x) ^ \ + ((y>>1 & 1) * xtime(x)) ^ \ + ((y>>2 & 1) * xtime(xtime(x))) ^ \ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ + +#endif + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +static void InvMixColumns(state_t* state) +{ + int i; + for (i = 0; i < 4; ++i) { + uint8_t a, b, c, d; + a = (*state)[i][0]; + b = (*state)[i][1]; + c = (*state)[i][2]; + d = (*state)[i][3]; + + (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + } +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void InvSubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + (*state)[j][i] = getSBoxInvert((*state)[j][i]); + } + } +} + +static void InvShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to right + temp = (*state)[3][1]; + (*state)[3][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[0][1]; + (*state)[0][1] = temp; + + // Rotate second row 2 columns to right + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to right + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[1][3]; + (*state)[1][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[3][3]; + (*state)[3][3] = temp; +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +// Cipher is the main function that encrypts the PlainText. +static void Cipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr-1 rounds are executed in the loop below. + for (round = 1; round < Nr; ++round) { + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(round, state, RoundKey); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + SubBytes(state); + ShiftRows(state); + AddRoundKey(Nr, state, RoundKey); +} + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static void InvCipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(Nr, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr-1 rounds are executed in the loop below. + for (round = (Nr - 1); round > 0; --round) { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(round, state, RoundKey); + InvMixColumns(state); + } + + // The last round is given below. + // The MixColumns function is not here in the last round. + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(0, state, RoundKey); +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +/*****************************************************************************/ +/* Public functions: */ +/*****************************************************************************/ +#if defined(ECB) && (ECB == 1) + + +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call encrypts the PlainText with the Key using AES algorithm. + Cipher((state_t*)buf, ctx->RoundKey); +} + +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call decrypts the PlainText with the Key using AES algorithm. + InvCipher((state_t*)buf, ctx->RoundKey); +} + + +#endif // #if defined(ECB) && (ECB == 1) + + + + + +#if defined(CBC) && (CBC == 1) + + +static void XorWithIv(uint8_t* buf, const uint8_t* Iv) +{ + uint8_t i; + for (i = 0; i < AES_BLOCKLEN; ++i) { // The block in AES is always 128bit no matter the key size + buf[i] ^= Iv[i]; + } +} + +void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, uint32_t length) +{ + uintptr_t i; + uint8_t *Iv = ctx->Iv; + for (i = 0; i < length; i += AES_BLOCKLEN) { + XorWithIv(buf, Iv); + Cipher((state_t*)buf, ctx->RoundKey); + Iv = buf; + buf += AES_BLOCKLEN; + //printf("Step %d - %d", i/16, i); + } + /* store Iv in ctx for next call */ + memcpy(ctx->Iv, Iv, AES_BLOCKLEN); +} + +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) +{ + uintptr_t i; + uint8_t storeNextIv[AES_BLOCKLEN]; + for (i = 0; i < length; i += AES_BLOCKLEN) { + memcpy(storeNextIv, buf, AES_BLOCKLEN); + InvCipher((state_t*)buf, ctx->RoundKey); + XorWithIv(buf, ctx->Iv); + memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); + buf += AES_BLOCKLEN; + } + +} + +#endif // #if defined(CBC) && (CBC == 1) + + + +#if defined(CTR) && (CTR == 1) + +/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) +{ + uint8_t buffer[AES_BLOCKLEN]; + + unsigned i; + int bi; + for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) { + if (bi == AES_BLOCKLEN) { /* we need to regen xor compliment in buffer */ + + memcpy(buffer, ctx->Iv, AES_BLOCKLEN); + Cipher((state_t*)buffer, ctx->RoundKey); + + /* Increment Iv and handle overflow */ + for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) { + /* inc will overflow */ + if (ctx->Iv[bi] == 255) { + ctx->Iv[bi] = 0; + continue; + } + ctx->Iv[bi] += 1; + break; + } + bi = 0; + } + + buf[i] = (buf[i] ^ buffer[bi]); + } +} + +#endif // #if defined(CTR) && (CTR == 1) diff --git a/hal/crypto/AVR/drivers/AES/aes.h b/hal/crypto/AVR/drivers/AES/aes.h new file mode 100644 index 000000000..294e321eb --- /dev/null +++ b/hal/crypto/AVR/drivers/AES/aes.h @@ -0,0 +1,111 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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. +* +* AES implementation: https://github.com/kokke/tiny-AES-c +*/ + +#ifndef _AES_H_ +#define _AES_H_ + +// #define the macros below to 1/0 to enable/disable the mode of operation. +// +// CBC enables AES encryption in CBC-mode of operation. +// CTR enables encryption in counter-mode. +// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. + +// The #ifndef-guard allows it to be configured before #include'ing or at compile time. +#ifndef CBC +#define CBC 1 +#endif + +#ifndef ECB +#define ECB 0 +#endif + +#ifndef CTR +#define CTR 0 +#endif + + +#define AES128 1 +//#define AES192 1 +//#define AES256 1 + +#define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only + +#if defined(AES256) && (AES256 == 1) +#define AES_KEYLEN 32 +#define AES_keyExpSize 240 +#elif defined(AES192) && (AES192 == 1) +#define AES_KEYLEN 24 +#define AES_keyExpSize 208 +#else +#define AES_KEYLEN 16 // Key length in bytes +#define AES_keyExpSize 176 +#endif + +/** + * @brief AES state structure + */ +struct AES_ctx { + uint8_t RoundKey[AES_keyExpSize]; //!< RoundKey +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) + uint8_t Iv[AES_BLOCKLEN]; //!< Iv +#endif +}; + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); +#endif + +#if defined(ECB) && (ECB == 1) +// buffer size is exactly AES_BLOCKLEN bytes; +// you need only AES_init_ctx as IV is not used in ECB +// NB: ECB is considered insecure for most uses +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); + +#endif // #if defined(ECB) && (ECB == !) + + +#if defined(CBC) && (CBC == 1) +// buffer size MUST be mutile of AES_BLOCKLEN; +// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); + +#endif // #if defined(CBC) && (CBC == 1) + + +#if defined(CTR) && (CTR == 1) + +// Same function for encrypting as for decrypting. +// IV is incremented for every block, and used after encryption as XOR-compliment for output +// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); + +#endif // #if defined(CTR) && (CTR == 1) + + +#endif //_AES_H_ diff --git a/hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.cpp b/hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.cpp new file mode 100644 index 000000000..f2bc6c2d9 --- /dev/null +++ b/hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.cpp @@ -0,0 +1,61 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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 "hmac_sha256.h" + +/* +* all lengths in bits! +*/ +void hmac_sha256(void *dest, const void *key, uint16_t keylength_b, const void *msg, + uint32_t msglength_b) +{ + sha256_ctx_t s; + uint8_t buffer[HMAC_SHA256_BLOCK_BYTES]; + + (void)memset((void *)buffer, 0x00, HMAC_SHA256_BLOCK_BYTES); + + /* if key is larger than a block we have to hash it*/ + if (keylength_b > SHA256_BLOCK_BITS) { + sha256((sha256_hash_t *)buffer, key, keylength_b); + } else { + (void)memcpy((void *)buffer, (const void *)key, (keylength_b + 7) / 8); + } + + for (uint8_t i = 0; i < SHA256_BLOCK_BYTES; ++i) { + buffer[i] ^= IPAD; + } + sha256_init(&s); + sha256_nextBlock(&s, buffer); + while (msglength_b >= HMAC_SHA256_BLOCK_BITS) { + sha256_nextBlock(&s, msg); + msg = (uint8_t *)msg + HMAC_SHA256_BLOCK_BYTES; + msglength_b -= HMAC_SHA256_BLOCK_BITS; + } + sha256_lastBlock(&s, msg, msglength_b); + /* since buffer still contains key xor ipad we can do ... */ + for (uint8_t i = 0; i < HMAC_SHA256_BLOCK_BYTES; ++i) { + buffer[i] ^= IPAD ^ OPAD; + } + sha256_ctx2hash((sha256_hash_t *)dest, &s); /* save inner hash temporary to dest */ + sha256_init(&s); + sha256_nextBlock(&s, buffer); + sha256_lastBlock(&s, dest, SHA256_HASH_BITS); + sha256_ctx2hash((sha256_hash_t *)dest, &s); +} diff --git a/hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.h b/hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.h new file mode 100644 index 000000000..4e5ae80ac --- /dev/null +++ b/hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.h @@ -0,0 +1,78 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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. +* +* ====================================================================== +* +* HMAC SHA256 implementation for AVR: +* +* This file is part of the AVR-Crypto-Lib. +* Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Author: Daniel Otte +* +* License: GPLv3 or later +* +* ====================================================================== +*/ + + +#ifndef _HMAC_SHA256_ +#define _HMAC_SHA256_ + +#define IPAD 0x36 //!< HMAC, inner hash, xor byte +#define OPAD 0x5C //!< HMAC, outer hash, xor byte + +#define HMAC_SHA256_BITS SHA256_HASH_BITS //!< Defines the size of a SHA-256 HMAC hash value in bits +#define HMAC_SHA256_BYTES SHA256_HASH_BYTES //!< Defines the size of a SHA-256 HMAC hash value in bytes +#define HMAC_SHA256_BLOCK_BITS SHA256_BLOCK_BITS //!< Defines the size of a SHA-256 HMAC input block in bits +#define HMAC_SHA256_BLOCK_BYTES SHA256_BLOCK_BYTES //!< Defines the size of a SHA-256 HMAC input block in bytes + +/** +* @brief hash context structure +*/ +typedef struct { + sha256_ctx_t a; //!< a + sha256_ctx_t b; //!< b +} hmac_sha256_ctx_t; + +/** +* @brief SHA256 HMAC function +* +* @param dest pointer to the location where the hash value is going to be written to +* @param key pointer to the key that's is needed for the HMAC calculation +* @param keylength_b length of the key +* @param msg pointer to the message that's going to be hashed +* @param msglength_b length of the message +*/ +void hmac_sha256(void *dest, const void *key, uint16_t keylength_b, const void *msg, + uint32_t msglength_b); + +#endif diff --git a/hal/crypto/AVR/drivers/SHA256/SHA256.S b/hal/crypto/AVR/drivers/SHA256/SHA256.S new file mode 100644 index 000000000..d591dfb27 --- /dev/null +++ b/hal/crypto/AVR/drivers/SHA256/SHA256.S @@ -0,0 +1,1038 @@ +/* + * This file is part of the AVR-Crypto-Lib. + * Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Daniel Otte + * + * License: GPLv3 or later + */ +; sha-256 implementation in assembler +SHA256_BLOCK_BITS = 512 +SHA256_HASH_BITS = 256 + + +.macro precall + /* push r18 - r27, r30 - r31*/ + push r0 + push r1 + push r18 + push r19 + push r20 + push r21 + push r22 + push r23 + push r24 + push r25 + push r26 + push r27 + push r30 + push r31 + clr r1 +.endm + +.macro postcall + pop r31 + pop r30 + pop r27 + pop r26 + pop r25 + pop r24 + pop r23 + pop r22 + pop r21 + pop r20 + pop r19 + pop r18 + pop r1 + pop r0 +.endm + + +.macro hexdump length + push r27 + push r26 + ldi r25, '\r' + mov r24, r25 + call uart_putc + ldi r25, '\n' + mov r24, r25 + call uart_putc + pop r26 + pop r27 + movw r24, r26 +.if \length > 16 + ldi r22, lo8(16) + ldi r23, hi8(16) + push r27 + push r26 + call uart_hexdump + pop r26 + pop r27 + adiw r26, 16 + hexdump \length-16 +.else + ldi r22, lo8(\length) + ldi r23, hi8(\length) + call uart_hexdump +.endif +.endm + +/* X points to Block */ +.macro dbg_hexdump length + precall + hexdump \length + postcall +.endm + +.section .text + +SPL = 0x3D +SPH = 0x3E +SREG = 0x3F + + +; +;sha256_ctx_t is: +; +; [h0][h1][h2][h3][h4][h5][h6][h7][length] +; hn is 32 bit large, length is 64 bit large + +;########################################################### + +.global sha256_ctx2hash +; === sha256_ctx2hash === +; this function converts a state into a normal hash (bytestring) +; param1: the 16-bit destination pointer +; given in r25,r24 (r25 is most significant) +; param2: the 16-bit pointer to sha256_ctx structure +; given in r23,r22 +sha256_ctx2hash: + movw r26, r22 + movw r30, r24 + ldi r21, 8 + sbiw r26, 4 +1: + ldi r20, 4 + adiw r26, 8 +2: + ld r0, -X + st Z+, r0 + dec r20 + brne 2b + + dec r21 + brne 1b + + ret + +;########################################################### + +.global sha256 +; === sha256 === +; this function calculates SHA-256 hashes from messages in RAM +; param1: the 16-bit hash destination pointer +; given in r25,r24 (r25 is most significant) +; param2: the 16-bit pointer to message +; given in r23,r22 +; param3: 32-bit length value (length of message in bits) +; given in r21,r20,r19,r18 +sha256: +sha256_prolog: + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r16 + push r17 + in r30, SPL + in r31, SPH + sbiw r30, 8*4+8 + in r0, SREG + cli + out SPL, r30 + out SREG, r0 + out SPH, r31 + + push r25 + push r24 + adiw r30, 1 + movw r16, r30 + movw r8, r18 /* backup of length*/ + movw r10, r20 + + movw r12, r22 /* backup pf msg-ptr */ + + movw r24, r16 + rcall sha256_init + /* if length > 0xffff */ +1: + tst r11 + brne 2f + tst r10 + breq 4f +2: + movw r24, r16 + movw r22, r12 + rcall sha256_nextBlock + ldi r19, 64 + add r12, r19 + adc r13, r1 + /* length -= 512 */ + ldi r19, 0x02 + sub r9, r19 + sbc r10, r1 + sbc r11, r1 + rjmp 1b + +4: + movw r24, r16 + movw r22, r12 + movw r20, r8 + rcall sha256_lastBlock + + pop r24 + pop r25 + movw r22, r16 + rcall sha256_ctx2hash + +sha256_epilog: + in r30, SPL + in r31, SPH + adiw r30, 8*4+8 + in r0, SREG + cli + out SPL, r30 + out SREG, r0 + out SPH, r31 + pop r17 + pop r16 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + ret + +;########################################################### + + +; block MUST NOT be larger than 64 bytes + +.global sha256_lastBlock +; === sha256_lastBlock === +; this function does padding & Co. for calculating SHA-256 hashes +; param1: the 16-bit pointer to sha256_ctx structure +; given in r25,r24 (r25 is most significant) +; param2: an 16-bit pointer to 64 byte block to hash +; given in r23,r22 +; param3: an 16-bit integer specifing length of block in bits +; given in r21,r20 +sha256_lastBlock_localSpace = (SHA256_BLOCK_BITS/8+1) + + +sha256_lastBlock: + cpi r21, 0x02 + brlo sha256_lastBlock_prolog + push r25 + push r24 + push r23 + push r22 + push r21 + push r20 + rcall sha256_nextBlock + pop r20 + pop r21 + pop r22 + pop r23 + pop r24 + pop r25 + subi r21, 0x02 + ldi r19, 64 + add r22, r19 + adc r23, r1 + rjmp sha256_lastBlock +sha256_lastBlock_prolog: + /* allocate space on stack */ + in r30, SPL + in r31, SPH + in r0, SREG + subi r30, lo8(64) + sbci r31, hi8(64) + cli + out SPL, r30 + out SREG,r0 + out SPH, r31 + + adiw r30, 1 /* SP points to next free byte on stack */ + mov r18, r20 /* r20 = LSB(length) */ + lsr r18 + lsr r18 + lsr r18 + bst r21, 0 /* may be we should explain this ... */ + bld r18, 5 /* now: r18 == length/8 (aka. length in bytes) */ + + + movw r26, r22 /* X points to begin of msg */ + tst r18 + breq sha256_lastBlock_post_copy + mov r1, r18 +sha256_lastBlock_copy_loop: + ld r0, X+ + st Z+, r0 + dec r1 + brne sha256_lastBlock_copy_loop +sha256_lastBlock_post_copy: +sha256_lastBlock_insert_stuffing_bit: + ldi r19, 0x80 + mov r0,r19 + ldi r19, 0x07 + and r19, r20 /* if we are in bitmode */ + breq 2f /* no bitmode */ +1: + lsr r0 + dec r19 + brne 1b + ld r19, X +/* maybe we should do some ANDing here, just for safety */ + or r0, r19 +2: + st Z+, r0 + inc r18 + +/* checking stuff here */ + cpi r18, 64-8+1 + brsh 0f + rjmp sha256_lastBlock_insert_zeros +0: + /* oh shit, we landed here */ + /* first we have to fill it up with zeros */ + ldi r19, 64 + sub r19, r18 + breq 2f +1: + st Z+, r1 + dec r19 + brne 1b +2: + sbiw r30, 63 + sbiw r30, 1 + movw r22, r30 + + push r31 + push r30 + push r25 + push r24 + push r21 + push r20 + rcall sha256_nextBlock + pop r20 + pop r21 + pop r24 + pop r25 + pop r30 + pop r31 + + /* now we should subtract 512 from length */ + movw r26, r24 + adiw r26, 4*8+1 /* we can skip the lowest byte */ + ld r19, X + subi r19, hi8(512) + st X+, r19 + ldi r18, 6 +1: + ld r19, X + sbci r19, 0 + st X+, r19 + dec r18 + brne 1b + +; clr r18 /* not neccessary ;-) */ + /* reset Z pointer to begin of block */ + +sha256_lastBlock_insert_zeros: + ldi r19, 64-8 + sub r19, r18 + breq sha256_lastBlock_insert_length + clr r1 +1: + st Z+, r1 /* r1 is still zero */ + dec r19 + brne 1b + +; rjmp sha256_lastBlock_epilog +sha256_lastBlock_insert_length: + movw r26, r24 /* X points to state */ + adiw r26, 8*4 /* X points to (state.length) */ + adiw r30, 8 /* Z points one after the last byte of block */ + ld r0, X+ + add r0, r20 + st -Z, r0 + ld r0, X+ + adc r0, r21 + st -Z, r0 + ldi r19, 6 +1: + ld r0, X+ + adc r0, r1 + st -Z, r0 + dec r19 + brne 1b + + sbiw r30, 64-8 + movw r22, r30 + rcall sha256_nextBlock + +sha256_lastBlock_epilog: + in r30, SPL + in r31, SPH + in r0, SREG + adiw r30, 63 ; lo8(64) + adiw r30, 1 ; hi8(64) + cli + out SPL, r30 + out SREG,r0 + out SPH, r31 + clr r1 + ret + +/**/ +;########################################################### + +.global sha256_nextBlock +; === sha256_nextBlock === +; this is the core function for calculating SHA-256 hashes +; param1: the 16-bit pointer to sha256_ctx structure +; given in r25,r24 (r25 is most significant) +; param2: an 16-bit pointer to 64 byte block to hash +; given in r23,r22 +sha256_nextBlock_localSpace = (64+8)*4 ; 64 32-bit values for w array and 8 32-bit values for a array (total 288 byte) + +Bck1 = 12 +Bck2 = 13 +Bck3 = 14 +Bck4 = 15 +Func1 = 22 +Func2 = 23 +Func3 = 24 +Func4 = 25 +Accu1 = 16 +Accu2 = 17 +Accu3 = 18 +Accu4 = 19 +XAccu1 = 8 +XAccu2 = 9 +XAccu3 = 10 +XAccu4 = 11 +T1 = 4 +T2 = 5 +T3 = 6 +T4 = 7 +LoopC = 1 +/* byteorder: high number <--> high significance */ +sha256_nextBlock: + ; initial, let's make some space ready for local vars + push r4 /* replace push & pop by mem ops? */ + push r5 + push r6 + push r7 + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + push r16 + push r17 + push r28 + push r29 + in r20, SPL + in r21, SPH + movw r18, r20 ;backup SP +; movw r26, r20 ; X points to free space on stack + movw r30, r22 ; Z points to message + subi r20, lo8(sha256_nextBlock_localSpace) ;sbiw can do only up to 63 + sbci r21, hi8(sha256_nextBlock_localSpace) + movw r26, r20 ; X points to free space on stack + in r0, SREG + cli ; we want to be uninterrupted while updating SP + out SPL, r20 + out SREG, r0 + out SPH, r21 + push r18 + push r19 + push r24 + push r25 /* param1 will be needed later */ + ; now we fill the w array with message (think about endianess) + adiw r26, 1 ; X++ + ldi r20, 16 +sha256_nextBlock_wcpyloop: + ld r23, Z+ + ld r22, Z+ + ld r19, Z+ + ld r18, Z+ + st X+, r18 + st X+, r19 + st X+, r22 + st X+, r23 + dec r20 + brne sha256_nextBlock_wcpyloop +/* for (i=16; i<64; ++i){ + w[i] = SIGMA_b(w[i-2]) + w[i-7] + SIGMA_a(w[i-15]) + w[i-16]; + } */ + /* r25,r24,r23,r24 (r21,r20) are function values + r19,r18,r17,r16 are the accumulator + r15,r14,r13,rBck1 are backup1 + r11,r10,r9 ,r8 are xor accu + r1 is round counter */ + + ldi r20, 64-16 + mov LoopC, r20 +sha256_nextBlock_wcalcloop: + movw r30, r26 ; cp X to Z + sbiw r30, 63 + sbiw r30, 1 ; substract 64 = 16*4 + ld Accu1, Z+ + ld Accu2, Z+ + ld Accu3, Z+ + ld Accu4, Z+ /* w[i] = w[i-16] */ + ld Bck1, Z+ + ld Bck2, Z+ + ld Bck3, Z+ + ld Bck4, Z+ /* backup = w[i-15] */ + /* now sigma 0 */ + mov Func1, Bck2 + mov Func2, Bck3 + mov Func3, Bck4 + mov Func4, Bck1 /* prerotated by 8 */ + ldi r20, 1 + rcall bitrotl + movw XAccu1, Func1 + movw XAccu3, Func3 /* store ROTR(w[i-15],7) in xor accu */ + movw Func1, Bck3 + movw Func3, Bck1 /* prerotated by 16 */ + ldi r20, 2 + rcall bitrotr + eor XAccu1, Func1 /* xor ROTR(w[i-15], 18)*/ + eor XAccu2, Func2 + eor XAccu3, Func3 + eor XAccu4, Func4 + ldi Func2, 3 /* now shr3 */ /*we can destroy backup now*/ +sigma0_shr: + lsr Bck4 + ror Bck3 + ror Bck2 + ror Bck1 + dec Func2 + brne sigma0_shr + eor XAccu1, Bck1 + eor XAccu2, Bck2 + eor XAccu3, Bck3 + eor XAccu4, Bck4 /* xor SHR(w[i-15], 3)*/ /* xor accu == sigma1(w[i-15]) */ + add Accu1, XAccu1 + adc Accu2, XAccu2 + adc Accu3, XAccu3 + adc Accu4, XAccu4 /* finished with sigma0 */ + ldd Func1, Z+7*4 /* now accu += w[i-7] */ + ldd Func2, Z+7*4+1 + ldd Func3, Z+7*4+2 + ldd Func4, Z+7*4+3 + add Accu1, Func1 + adc Accu2, Func2 + adc Accu3, Func3 + adc Accu4, Func4 + ldd Bck1, Z+12*4 /* now backup = w[i-2]*/ + ldd Bck2, Z+12*4+1 + ldd Bck3, Z+12*4+2 + ldd Bck4, Z+12*4+3 + /* now sigma 1 */ + movw Func1, Bck3 + movw Func3, Bck1 /* prerotated by 16 */ + ldi r20, 1 + rcall bitrotr + movw XAccu3, Func3 + movw XAccu1, Func1 /* store in ROTR(w[i-2], 17) xor accu */ +; movw Func1, Bck3 +; movw Func3, Bck1 /* prerotated by 16 */ + ldi r20, 2 + rcall bitrotr + eor XAccu1, Func1 /* xor ROTR(w[i-2], 19)*/ + eor XAccu2, Func2 + eor XAccu3, Func3 + eor XAccu4, Func4 + ldi Func2, 2 /* now shr10 (dirty trick, skipping a byte) */ /*we can destroy backup now*/ +sigma1_shr: + lsr Bck4 + ror Bck3 + ror Bck2 + dec Func2 + brne sigma1_shr + eor XAccu1, Bck2 + eor XAccu2, Bck3 + eor XAccu3, Bck4 /* xor SHR(w[i-2], 10)*/ /* xor accu == sigma1(w[i-15]) */ + add Accu1, XAccu1 + adc Accu2, XAccu2 + adc Accu3, XAccu3 + adc Accu4, XAccu4 /* finished with sigma0 */ + /* now let's store the shit */ + st X+, Accu1 + st X+, Accu2 + st X+, Accu3 + st X+, Accu4 + dec LoopC + breq 3f ; skip if zero + rjmp sha256_nextBlock_wcalcloop +3: + /* we are finished with w array X points one byte post w */ +/* init a array */ + pop r31 + pop r30 + push r30 + push r31 + ldi r25, 8*4 /* 8 32-bit values to copy from ctx to a array */ +init_a_array: + ld r1, Z+ + st X+, r1 + dec r25 + brne init_a_array + +/* now the real fun begins */ +/* for (i=0; i<64; ++i){ + t1 = a[7] + SIGMA1(a[4]) + CH(a[4],a[5],a[6]) + k[i] + w[i]; + t2 = SIGMA0(a[0]) + MAJ(a[0],a[1],a[2]); + memmove(&(a[1]), &(a[0]), 7*4); // a[7]=a[6]; a[6]=a[5]; a[5]=a[4]; a[4]=a[3]; a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; + a[4] += t1; + a[0] = t1 + t2; + } */ + /* Y points to a[0], Z ('cause lpm wants it) points to k[i], X points to w[i] */ + sbiw r26, 8*4 /* X still points at a[7]+1*/ + movw r28, r26 + ldi r30, lo8(sha256_kv) + ldi r31, hi8(sha256_kv) + dec r27 /* X - (64*4 == 256) */ + ldi r25, 64 + mov LoopC, r25 +sha256_main_loop: + /* now calculate t1 */ + /*CH(x,y,z) = (x&y)^((~x)&z)*/ + ldd T1, Y+5*4 + ldd T2, Y+5*4+1 + ldd T3, Y+5*4+2 + ldd T4, Y+5*4+3 /* y in T */ + ldd Func1, Y+4*4 + ldd Func2, Y+4*4+1 + ldd Func3, Y+4*4+2 + ldd Func4, Y+4*4+3 /* x in Func */ + ldd Bck1, Y+6*4 + ldd Bck2, Y+6*4+1 + ldd Bck3, Y+6*4+2 + ldd Bck4, Y+6*4+3 /* z in Bck */ + and T1, Func1 + and T2, Func2 + and T3, Func3 + and T4, Func4 + com Func1 + com Func2 + com Func3 + com Func4 + and Bck1, Func1 + and Bck2, Func2 + and Bck3, Func3 + and Bck4, Func4 + eor T1, Bck1 + eor T2, Bck2 + eor T3, Bck3 + eor T4, Bck4 /* done, CH(x,y,z) is in T */ + /* now SIGMA1(a[4]) */ + ldd Bck4, Y+4*4 /* think about using it from Func reg above*/ + ldd Bck1, Y+4*4+1 + ldd Bck2, Y+4*4+2 + ldd Bck3, Y+4*4+3 /* load prerotate by 8-bit */ + movw Func1, Bck1 + movw Func3, Bck3 + ldi r20, 2 + rcall bitrotl /* rotr(x,6) */ + movw XAccu1, Func1 + movw XAccu3, Func3 + movw Func1, Bck1 + movw Func3, Bck3 + ldi r20, 3 + rcall bitrotr /* rotr(x,11) */ + eor XAccu1, Func1 + eor XAccu2, Func2 + eor XAccu3, Func3 + eor XAccu4, Func4 + movw Func1, Bck3 /* this prerotates furteh 16 bits*/ + movw Func3, Bck1 /* so we have now prerotated by 24 bits*/ + ldi r20, 1 + rcall bitrotr /* rotr(x,11) */ + eor XAccu1, Func1 + eor XAccu2, Func2 + eor XAccu3, Func3 + eor XAccu4, Func4 /* finished with SIGMA1, add it to T */ + add T1, XAccu1 + adc T2, XAccu2 + adc T3, XAccu3 + adc T4, XAccu4 + /* now we've to add a[7], w[i] and k[i] */ + ldd XAccu1, Y+4*7 + ldd XAccu2, Y+4*7+1 + ldd XAccu3, Y+4*7+2 + ldd XAccu4, Y+4*7+3 + add T1, XAccu1 + adc T2, XAccu2 + adc T3, XAccu3 + adc T4, XAccu4 /* add a[7] */ + ld XAccu1, X+ + ld XAccu2, X+ + ld XAccu3, X+ + ld XAccu4, X+ + add T1, XAccu1 + adc T2, XAccu2 + adc T3, XAccu3 + adc T4, XAccu4 /* add w[i] */ + lpm XAccu1, Z+ + lpm XAccu2, Z+ + lpm XAccu3, Z+ + lpm XAccu4, Z+ + add T1, XAccu1 + adc T2, XAccu2 + adc T3, XAccu3 + adc T4, XAccu4 /* add k[i] */ /* finished with t1 */ + /*now t2 = SIGMA0(a[0]) + MAJ(a[0],a[1],a[2]) */ /*i did to much x86 asm, i always see 4 32bit regs*/ + /* starting with MAJ(x,y,z) */ + ldd Func1, Y+4*0+0 + ldd Func2, Y+4*0+1 + ldd Func3, Y+4*0+2 + ldd Func4, Y+4*0+3 /* load x=a[0] */ + ldd XAccu1, Y+4*1+0 + ldd XAccu2, Y+4*1+1 + ldd XAccu3, Y+4*1+2 + ldd XAccu4, Y+4*1+3 /* load y=a[1] */ + and XAccu1, Func1 + and XAccu2, Func2 + and XAccu3, Func3 + and XAccu4, Func4 /* XAccu == (x & y) */ + ldd Bck1, Y+4*2+0 + ldd Bck2, Y+4*2+1 + ldd Bck3, Y+4*2+2 + ldd Bck4, Y+4*2+3 /* load z=a[2] */ + and Func1, Bck1 + and Func2, Bck2 + and Func3, Bck3 + and Func4, Bck4 + eor XAccu1, Func1 + eor XAccu2, Func2 + eor XAccu3, Func3 + eor XAccu4, Func4 /* XAccu == (x & y) ^ (x & z) */ + ldd Func1, Y+4*1+0 + ldd Func2, Y+4*1+1 + ldd Func3, Y+4*1+2 + ldd Func4, Y+4*1+3 /* load y=a[1] */ + and Func1, Bck1 + and Func2, Bck2 + and Func3, Bck3 + and Func4, Bck4 + eor XAccu1, Func1 + eor XAccu2, Func2 + eor XAccu3, Func3 + eor XAccu4, Func4 /* XAccu == Maj(x,y,z) == (x & y) ^ (x & z) ^ (y & z) */ + /* SIGMA0(a[0]) */ + ldd Bck1, Y+4*0+0 /* we should combine this with above */ + ldd Bck2, Y+4*0+1 + ldd Bck3, Y+4*0+2 + ldd Bck4, Y+4*0+3 + movw Func1, Bck1 + movw Func3, Bck3 + ldi r20, 2 + rcall bitrotr + movw Accu1, Func1 + movw Accu3, Func3 /* Accu = shr(a[0], 2) */ + movw Func1, Bck3 + movw Func3, Bck1 /* prerotate by 16 bits */ + ldi r20, 3 + rcall bitrotl + eor Accu1, Func1 + eor Accu2, Func2 + eor Accu3, Func3 + eor Accu4, Func4 /* Accu ^= shr(a[0], 13) */ + mov Func1, Bck4 + mov Func2, Bck1 + mov Func3, Bck2 + mov Func4, Bck3 /* prerotate by 24 bits */ + ldi r20, 2 + rcall bitrotl + eor Accu1, Func1 + eor Accu2, Func2 + eor Accu3, Func3 + eor Accu4, Func4 /* Accu ^= shr(a[0], 22) */ + add Accu1, XAccu1 /* add previous result (MAJ)*/ + adc Accu2, XAccu2 + adc Accu3, XAccu3 + adc Accu4, XAccu4 + /* now we are finished with the computing stuff (t1 in T, t2 in Accu)*/ + /* a[7]=a[6]; a[6]=a[5]; a[5]=a[4]; a[4]=a[3]; a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; */ + + ldi r21, 7*4 + adiw r28, 7*4 +a_shift_loop: + ld r25, -Y /* warning: this is PREdecrement */ + std Y+4, r25 + dec r21 + brne a_shift_loop + + ldd Bck1, Y+4*4+0 + ldd Bck2, Y+4*4+1 + ldd Bck3, Y+4*4+2 + ldd Bck4, Y+4*4+3 + add Bck1, T1 + adc Bck2, T2 + adc Bck3, T3 + adc Bck4, T4 + std Y+4*4+0, Bck1 + std Y+4*4+1, Bck2 + std Y+4*4+2, Bck3 + std Y+4*4+3, Bck4 + add Accu1, T1 + adc Accu2, T2 + adc Accu3, T3 + adc Accu4, T4 + std Y+4*0+0, Accu1 + std Y+4*0+1, Accu2 + std Y+4*0+2, Accu3 + std Y+4*0+3, Accu4 /* a array updated */ + + + dec LoopC + breq update_state + rjmp sha256_main_loop ;brne sha256_main_loop +update_state: + /* update state */ + /* pointers to state should still exist on the stack ;-) */ + pop r31 + pop r30 + ldi r21, 8 +update_state_loop: + ldd Accu1, Z+0 + ldd Accu2, Z+1 + ldd Accu3, Z+2 + ldd Accu4, Z+3 + ld Func1, Y+ + ld Func2, Y+ + ld Func3, Y+ + ld Func4, Y+ + add Accu1, Func1 + adc Accu2, Func2 + adc Accu3, Func3 + adc Accu4, Func4 + st Z+, Accu1 + st Z+, Accu2 + st Z+, Accu3 + st Z+, Accu4 + dec r21 + brne update_state_loop + /* now we just have to update the length */ + adiw r30, 1 /* since we add 512, we can simply skip the LSB */ + ldi r21, 2 + ldi r22, 6 + ld r20, Z + add r20, r21 + st Z+, r20 + clr r21 +sha256_nextBlock_fix_length: + brcc sha256_nextBlock_epilog + ld r20, Z + adc r20, r21 + st Z+, r20 + dec r22 + brne sha256_nextBlock_fix_length + +; EPILOG +sha256_nextBlock_epilog: +/* now we should clean up the stack */ + + pop r21 + pop r20 + in r0, SREG + cli ; we want to be uninterrupted while updating SP + out SPL, r20 + out SREG, r0 + out SPH, r21 + clr r1 + pop r29 + pop r28 + pop r17 + pop r16 + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop r7 + pop r6 + pop r5 + pop r4 + ret + +sha256_kv: ; round-key-vector stored in ProgMem +.word 0x2f98, 0x428a, 0x4491, 0x7137, 0xfbcf, 0xb5c0, 0xdba5, 0xe9b5, 0xc25b, 0x3956, 0x11f1, 0x59f1, 0x82a4, 0x923f, 0x5ed5, 0xab1c +.word 0xaa98, 0xd807, 0x5b01, 0x1283, 0x85be, 0x2431, 0x7dc3, 0x550c, 0x5d74, 0x72be, 0xb1fe, 0x80de, 0x06a7, 0x9bdc, 0xf174, 0xc19b +.word 0x69c1, 0xe49b, 0x4786, 0xefbe, 0x9dc6, 0x0fc1, 0xa1cc, 0x240c, 0x2c6f, 0x2de9, 0x84aa, 0x4a74, 0xa9dc, 0x5cb0, 0x88da, 0x76f9 +.word 0x5152, 0x983e, 0xc66d, 0xa831, 0x27c8, 0xb003, 0x7fc7, 0xbf59, 0x0bf3, 0xc6e0, 0x9147, 0xd5a7, 0x6351, 0x06ca, 0x2967, 0x1429 +.word 0x0a85, 0x27b7, 0x2138, 0x2e1b, 0x6dfc, 0x4d2c, 0x0d13, 0x5338, 0x7354, 0x650a, 0x0abb, 0x766a, 0xc92e, 0x81c2, 0x2c85, 0x9272 +.word 0xe8a1, 0xa2bf, 0x664b, 0xa81a, 0x8b70, 0xc24b, 0x51a3, 0xc76c, 0xe819, 0xd192, 0x0624, 0xd699, 0x3585, 0xf40e, 0xa070, 0x106a +.word 0xc116, 0x19a4, 0x6c08, 0x1e37, 0x774c, 0x2748, 0xbcb5, 0x34b0, 0x0cb3, 0x391c, 0xaa4a, 0x4ed8, 0xca4f, 0x5b9c, 0x6ff3, 0x682e +.word 0x82ee, 0x748f, 0x636f, 0x78a5, 0x7814, 0x84c8, 0x0208, 0x8cc7, 0xfffa, 0x90be, 0x6ceb, 0xa450, 0xa3f7, 0xbef9, 0x78f2, 0xc671 + + +;########################################################### + +.global sha256_init +;uint32_t sha256_init_vector[]={ +; 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, +; 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; +; +;void sha256_init(sha256_ctx_t *state){ +; state->length=0; +; memcpy(state->h, sha256_init_vector, 8*4); +;} +; param1: (r23,r24) 16-bit pointer to sha256_ctx_t struct in ram +; modifys: Z(r30,r31), Func1, r22 +sha256_init: + movw r26, r24 ; (24,25) --> (26,27) load X with param1 + ldi r30, lo8((sha256_init_vector)) + ldi r31, hi8((sha256_init_vector)) + ldi r22, 32+8 +sha256_init_vloop: + lpm r23, Z+ + st X+, r23 + dec r22 + brne sha256_init_vloop + ret + +sha256_init_vector: +.word 0xE667, 0x6A09 +.word 0xAE85, 0xBB67 +.word 0xF372, 0x3C6E +.word 0xF53A, 0xA54F +.word 0x527F, 0x510E +.word 0x688C, 0x9B05 +.word 0xD9AB, 0x1F83 +.word 0xCD19, 0x5BE0 +.word 0x0000, 0x0000 +.word 0x0000, 0x0000 + +;########################################################### + +.global rotl32 +; === ROTL32 === +; function that rotates a 32 bit word to the left +; param1: the 32-bit word to rotate +; given in r25,r24,r23,r22 (r25 is most significant) +; param2: an 8-bit value telling how often to rotate +; given in r20 +; modifys: r21, r22 +rotl32: + cpi r20, 8 + brlo bitrotl + mov r21, r25 + mov r25, r24 + mov r24, r23 + mov r23, r22 + mov r22, r21 + subi r20, 8 + rjmp rotl32 +bitrotl: + clr r21 + clc +bitrotl_loop: + tst r20 + breq fixrotl +2: + rol r22 + rol r23 + rol r24 + rol r25 + rol r21 + dec r20 + brne 2b +fixrotl: + or r22, r21 + ret + + +;########################################################### + +.global rotr32 +; === ROTR32 === +; function that rotates a 32 bit word to the right +; param1: the 32-bit word to rotate +; given in r25,r24,r23,22 (r25 is most significant) +; param2: an 8-bit value telling how often to rotate +; given in r20 +; modifys: r21, r22 +rotr32: + cpi r20, 8 + brlo bitrotr + mov r21, r22 + mov r22, r23 + mov r23, r24 + mov r24, r25 + mov r25, r21 + subi r20, 8 + rjmp rotr32 +bitrotr: + clr r21 + clc +bitrotr_loop: + tst r20 + breq fixrotr +2: + ror r25 + ror r24 + ror r23 + ror r22 + ror r21 + dec r20 + brne 2b +fixrotr: + or r25, r21 + ret + + +;########################################################### + +.global change_endian32 +; === change_endian32 === +; function that changes the endianess of a 32-bit word +; param1: the 32-bit word +; given in r25,r24,r23,22 (r25 is most significant) +; modifys: r21, r22 +change_endian32: + movw r20, r22 ; (r22,r23) --> (r20,r21) + mov r22, r25 + mov r23, r24 + mov r24, r21 + mov r25, r20 + +;########################################################### diff --git a/hal/crypto/AVR/drivers/SHA256/sha256.cpp b/hal/crypto/AVR/drivers/SHA256/sha256.cpp new file mode 100644 index 000000000..89a01b4f2 --- /dev/null +++ b/hal/crypto/AVR/drivers/SHA256/sha256.cpp @@ -0,0 +1,46 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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. +* +* ====================================================================== +* +* SHA256 implementation for AVR: +* +* This file is part of the AVR-Crypto-Lib. +* Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Author: Daniel Otte +* +* License: GPLv3 or later +* +* ====================================================================== +*/ + +#include "sha256.h" diff --git a/hal/crypto/AVR/drivers/SHA256/sha256.h b/hal/crypto/AVR/drivers/SHA256/sha256.h new file mode 100644 index 000000000..8e2754923 --- /dev/null +++ b/hal/crypto/AVR/drivers/SHA256/sha256.h @@ -0,0 +1,128 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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. +* +* ====================================================================== +* +* SHA256 implementation for AVR: +* +* This file is part of the AVR-Crypto-Lib. +* Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Author: Daniel Otte +* +* License: GPLv3 or later +* +* ====================================================================== +*/ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#define MY_CRYPTO_SHA256_ASM //!< Switch to define correct variable scope for ASM SHA256 implementation + +#define IPAD 0x36 //!< HMAC, inner hash, xor byte +#define OPAD 0x5C //!< HMAC, outer hash, xor byte + +#define SHA256_HASH_BITS 256 //!< Defines the size of a SHA-256 hash value in bits +#define SHA256_HASH_BYTES (SHA256_HASH_BITS/8) //!< Defines the size of a SHA-256 hash value in bytes +#define SHA256_BLOCK_BITS 512 //!< Defines the size of a SHA-256 input block in bits +#define SHA256_BLOCK_BYTES (SHA256_BLOCK_BITS/8) //!< Defines the size of a SHA-256 input block in bytes + +/** +* @brief SHA-256 context type +* +* A variable of this type may hold the state of a SHA-256 hashing process +*/ +typedef struct { + uint32_t h[8]; //!< h + uint64_t length; //!< length +} sha256_ctx_t; + +/** +* @brief SHA-256 hash value type +* +* A variable of this type may hold the hash value produced by the +* sha256_ctx2hash(sha256_hash_t *dest, const sha256_ctx_t *state) function. +*/ +typedef uint8_t sha256_hash_t[SHA256_HASH_BYTES]; + +extern "C" { // ASM implementation, see MyASM.S + /** + * @brief initialise a SHA-256 context + * + * This function sets a ::sha256_ctx_t to the initial values for hashing. + * @param state pointer to the SHA-256 hashing context + */ + void sha256_init(sha256_ctx_t *state); + + /** + * @brief update the context with a given block + * + * This function updates the SHA-256 hash context by processing the given block + * of fixed length. + * @param state pointer to the SHA-256 hash context + * @param block pointer to the block of fixed length (512 bit = 64 byte) + */ + void sha256_nextBlock(sha256_ctx_t *state, const void *block); + + /** + * @brief finalize the context with the given block + * + * This function finalizes the SHA-256 hash context by processing the given block + * of variable length. + * @param state pointer to the SHA-256 hash context + * @param block pointer to the block of fixed length (512 bit = 64 byte) + * @param length_b the length of the block in bits + */ + void sha256_lastBlock(sha256_ctx_t *state, const void *block, uint16_t length_b); + + /** + * @brief convert the hash state into the hash value + * + * This function reads the context and writes the hash value to the destination + * @param dest pointer to the location where the hash value should be written + * @param state pointer to the SHA-256 hash context + */ + void sha256_ctx2hash(sha256_hash_t *dest, const sha256_ctx_t *state); + + /** + * @brief simple SHA-256 hashing function for direct hashing + * + * This function automaticaly hashes a given message of arbitary length with + * the SHA-256 hashing algorithm. + * @param dest pointer to the location where the hash value is going to be written to + * @param msg pointer to the message thats going to be hashed + * @param length_b length of the message in bits + */ + void sha256(sha256_hash_t *dest, const void *msg, uint32_t length_b); +} + +#endif diff --git a/hal/crypto/ESP32/MyCryptoESP32.cpp b/hal/crypto/ESP32/MyCryptoESP32.cpp index 03999be00..587a13530 100644 --- a/hal/crypto/ESP32/MyCryptoESP32.cpp +++ b/hal/crypto/ESP32/MyCryptoESP32.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -44,30 +44,23 @@ void SHA256HMAC(uint8_t *dest, const uint8_t *key, size_t keyLength, const uint8 mbedtls_md_hmac_finish(&ctx, dest); } -/* not tested yet // ESP32 AES128 CBC -esp_aes_context aes_ctx; +static mbedtls_aes_context aes_ctx; void AES128CBCInit(const uint8_t *key) { - esp_aes_init(&aes_ctx); - (void)esp_aes_setkey(&aes_ctx, key, 128); + mbedtls_aes_init(&aes_ctx); + (void)mbedtls_aes_setkey_enc(&aes_ctx, key, 128); } -void AES128CBCEncrypt(uint8_t *dest, const uint8_t *data, size_t dataLength) +void AES128CBCEncrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength) { - uint8_t iv[16] = { 0 }; - esp_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, dataLength, iv, (const unsigned char *)data, (unsigned char *)dest); + mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, dataLength, iv, (const unsigned char *)buffer, + (unsigned char *)buffer); } -void AES128CBCDecrypt(uint8_t *dest, const uint8_t *data, size_t dataLength) +void AES128CBCDecrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength) { - uint8_t iv[16] = { 0 }; - esp_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, dataLength, iv, (const unsigned char *)data, (unsigned char *)dest); + mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, dataLength, iv, (const unsigned char *)buffer, + (unsigned char *)buffer); } - -void AES128CBCFree(void) -{ - esp_aes_free(&aes_ctx); -} -*/ diff --git a/hal/crypto/ESP32/MyCryptoESP32.h b/hal/crypto/ESP32/MyCryptoESP32.h index 41555caeb..bef76b218 100644 --- a/hal/crypto/ESP32/MyCryptoESP32.h +++ b/hal/crypto/ESP32/MyCryptoESP32.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/crypto/MyCryptoHAL.h b/hal/crypto/MyCryptoHAL.h index 334fe4f4b..fd3741ea1 100644 --- a/hal/crypto/MyCryptoHAL.h +++ b/hal/crypto/MyCryptoHAL.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -20,8 +20,6 @@ #ifndef MyCryptoHAL_h #define MyCryptoHAL_h -// Implement these as functions or macros - /** * @brief SHA256 calculation * @@ -46,4 +44,25 @@ void SHA256(uint8_t *dest, const uint8_t *data, size_t dataLength); */ void SHA256HMAC(uint8_t *dest, const uint8_t *key, size_t keyLength, const uint8_t *data, size_t dataLength); + +/** +* @brief AES128CBCInit +* @param key AES encryption key, 16 bytes +*/ +void AES128CBCInit(const uint8_t *key); +/** +* @brief AES128CBCEncrypt +* @param iv Initialization vector, 16 bytes +* @param buffer Buffer to enctypt +* @param dataLength Buffer length +*/ +void AES128CBCEncrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength); +/** +* @brief AES128CBCDecrypt +* @param iv Initialization vector, 16 bytes +* @param buffer Buffer to decrypt +* @param dataLength Buffer length +*/ +void AES128CBCDecrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength); + #endif diff --git a/hal/crypto/generic/MyCryptoGeneric.cpp b/hal/crypto/generic/MyCryptoGeneric.cpp index f2aa2fc99..4b24238b6 100644 --- a/hal/crypto/generic/MyCryptoGeneric.cpp +++ b/hal/crypto/generic/MyCryptoGeneric.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,207 +19,27 @@ #include "MyCryptoGeneric.h" -const uint32_t SHA256K[] PROGMEM = { - 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, - 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, - 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, - 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, - 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, - 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, - 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, - 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 -}; - -const uint8_t SHA256InitState[] PROGMEM = { - 0x67,0xe6,0x09,0x6a, // H0 - 0x85,0xae,0x67,0xbb, // H1 - 0x72,0xf3,0x6e,0x3c, // H2 - 0x3a,0xf5,0x4f,0xa5, // H3 - 0x7f,0x52,0x0e,0x51, // H4 - 0x8c,0x68,0x05,0x9b, // H5 - 0xab,0xd9,0x83,0x1f, // H6 - 0x19,0xcd,0xe0,0x5b // H7 -}; - -_SHA256buffer_t SHA256buffer; -uint8_t SHA256bufferOffset; -_SHA256state_t SHA256state; -uint32_t SHA256byteCount; -uint8_t SHA256keyBuffer[BLOCK_LENGTH]; - -void SHA256Init(void) -{ - (void)memcpy_P((void *)&SHA256state.b, (const void *)&SHA256InitState, 32); - SHA256byteCount = 0; - SHA256bufferOffset = 0; -} - -uint32_t SHA256ror32(const uint32_t number, const uint8_t bits) -{ - return ((number << (32 - bits)) | (number >> bits)); -} - -void SHA256hashBlock(void) -{ - uint32_t a, b, c, d, e, f, g, h, t1, t2; - - a = SHA256state.w[0]; - b = SHA256state.w[1]; - c = SHA256state.w[2]; - d = SHA256state.w[3]; - e = SHA256state.w[4]; - f = SHA256state.w[5]; - g = SHA256state.w[6]; - h = SHA256state.w[7]; - - for (uint8_t i = 0; i < 64; i++) { - if (i >= 16) { - t1 = SHA256buffer.w[i & 15] + SHA256buffer.w[(i - 7) & 15]; - t2 = SHA256buffer.w[(i - 2) & 15]; - t1 += SHA256ror32(t2, 17) ^ SHA256ror32(t2, 19) ^ (t2 >> 10); - t2 = SHA256buffer.w[(i - 15) & 15]; - t1 += SHA256ror32(t2, 7) ^ SHA256ror32(t2, 18) ^ (t2 >> 3); - SHA256buffer.w[i & 15] = t1; - } - t1 = h; - t1 += SHA256ror32(e, 6) ^ SHA256ror32(e, 11) ^ SHA256ror32(e, 25); // ∑1(e) - t1 += g ^ (e & (g ^ f)); // Ch(e,f,g) - t1 += pgm_read_dword(SHA256K + i); // Ki - t1 += SHA256buffer.w[i & 15]; // Wi - t2 = SHA256ror32(a, 2) ^ SHA256ror32(a, 13) ^ SHA256ror32(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; - } - SHA256state.w[0] += a; - SHA256state.w[1] += b; - SHA256state.w[2] += c; - SHA256state.w[3] += d; - SHA256state.w[4] += e; - SHA256state.w[5] += f; - SHA256state.w[6] += g; - SHA256state.w[7] += h; -} - -void SHA256addUncounted(const uint8_t data) -{ - SHA256buffer.b[SHA256bufferOffset ^ 3] = data; - SHA256bufferOffset++; - if (SHA256bufferOffset == BLOCK_LENGTH) { - SHA256hashBlock(); - SHA256bufferOffset = 0; - } -} - - -void SHA256Add(const uint8_t data) -{ - SHA256byteCount++; - SHA256addUncounted(data); -} - -void SHA256Add(const uint8_t *data, size_t dataLength) -{ - while (dataLength--) { - SHA256Add(*data++); - } -} - -void SHA256Result(uint8_t *dest) -{ - // Pad to complete the last block - SHA256addUncounted(0x80); - while (SHA256bufferOffset != 56) { - SHA256addUncounted(0x00); - } - - // Append length in the last 8 bytes - SHA256addUncounted(0); // We're only using 32 bit lengths - SHA256addUncounted(0); // But SHA-1 supports 64 bit lengths - SHA256addUncounted(0); // So zero pad the top bits - SHA256addUncounted(SHA256byteCount >> 29); // Shifting to multiply by 8 - SHA256addUncounted(SHA256byteCount >> 21); // as SHA-1 supports bitstreams as well as - SHA256addUncounted(SHA256byteCount >> 13); // byte. - SHA256addUncounted(SHA256byteCount >> 5); - SHA256addUncounted(SHA256byteCount << 3); - - // Swap byte order back - for (uint8_t i = 0; i<8; i++) { - uint32_t a, b; - a = SHA256state.w[i]; - b = a << 24; - b |= (a << 8) & 0x00ff0000; - b |= (a >> 8) & 0x0000ff00; - b |= a >> 24; - SHA256state.w[i] = b; - } - (void)memcpy((void *)dest, (const void *)SHA256state.b, 32); - // Return pointer to hash (20 characters) - //return SHA256state.b; -} - -void SHA256(uint8_t *dest, const uint8_t *data, size_t dataLength) -{ - SHA256Init(); - SHA256Add(data, dataLength); - SHA256Result(dest); -} - -// SHA256HMAC - -void SHA256HMACInit(const uint8_t *key, size_t keyLength) +void SHA256HMAC(uint8_t *dest, const uint8_t *key, size_t keyLength, const uint8_t *data, + size_t dataLength) { - (void)memset((void *)&SHA256keyBuffer, 0x00, BLOCK_LENGTH); - if (keyLength > BLOCK_LENGTH) { - // Hash long keys - SHA256Init(); - SHA256Add(key, keyLength); - SHA256Result(SHA256keyBuffer); - } else { - // Block length keys are used as is - (void)memcpy((void *)SHA256keyBuffer, (const void *)key, keyLength); - } - // Start inner hash - SHA256Init(); - for (uint8_t i = 0; i < BLOCK_LENGTH; i++) { - SHA256Add(SHA256keyBuffer[i] ^ HMAC_IPAD); - } + SHA256HMACInit(key, keyLength); + SHA256HMACAdd(data, dataLength); + SHA256HMACResult(dest); } -void SHA256HMACAdd(const uint8_t data) -{ - SHA256Add(data); -} +AES _aes; -void SHA256HMACAdd(const uint8_t *data, size_t dataLength) +void AES128CBCInit(const uint8_t *key) { - SHA256Add(data, dataLength); + _aes.set_key((byte *)key, 16); } -void SHA256HMACResult(uint8_t *dest) +void AES128CBCEncrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength) { - uint8_t innerHash[HASH_LENGTH]; - // Complete inner hash - SHA256Result(innerHash); - // Calculate outer hash - SHA256Init(); - for (uint8_t i = 0; i < BLOCK_LENGTH; i++) { - SHA256Add(SHA256keyBuffer[i] ^ HMAC_OPAD); - } - SHA256Add(innerHash, HASH_LENGTH); - SHA256Result(dest); + _aes.cbc_encrypt((byte *)buffer, (byte *)buffer, dataLength / 16, iv); } -void SHA256HMAC(uint8_t *dest, const uint8_t *key, size_t keyLength, const uint8_t *data, - size_t dataLength) +void AES128CBCDecrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength) { - SHA256HMACInit(key, keyLength); - SHA256HMACAdd(data, dataLength); - SHA256HMACResult(dest); + _aes.cbc_decrypt((byte *)buffer, (byte *)buffer, dataLength / 16, iv); } diff --git a/hal/crypto/generic/MyCryptoGeneric.h b/hal/crypto/generic/MyCryptoGeneric.h index 75dd57fbe..a10ee7a5f 100644 --- a/hal/crypto/generic/MyCryptoGeneric.h +++ b/hal/crypto/generic/MyCryptoGeneric.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -17,34 +17,12 @@ * version 2 as published by the Free Software Foundation. */ -// This code is taken from https://github.com/Cathedrow/Cryptosuite (Peter Knight) and is lightly modified for MySensors use - -#ifndef MyCryptoESP32_h -#define MyCryptoESP32_h +#ifndef MyCryptoGeneric_h +#define MyCryptoGeneric_h #include "hal/crypto/MyCryptoHAL.h" - -#define HASH_LENGTH 32 //!< HASH_LENGTH -#define BLOCK_LENGTH 64 //!< BLOCK_LENGTH - -/** -* @brief buffer for SHA256 calculator -*/ -union _SHA256buffer_t { - uint8_t b[BLOCK_LENGTH]; //!< SHA256 b - uint32_t w[BLOCK_LENGTH / 4]; //!< SHA256 w -}; - -/** -* @brief state variables for SHA256 calculator -*/ -union _SHA256state_t { - uint8_t b[HASH_LENGTH]; //!< SHA256 b - uint32_t w[HASH_LENGTH / 4]; //!< SHA256 w -}; - -// SHA256 HMAC padding bytes -#define HMAC_IPAD 0x36 //!< HMAC_IPAD -#define HMAC_OPAD 0x5c //!< HMAC_OPAD +#include "hal/crypto/generic/drivers/AES/AES.cpp" +#include "hal/crypto/generic/drivers/SHA256/sha256.cpp" +#include "hal/crypto/generic/drivers/HMAC_SHA256/hmac_sha256.cpp" #endif diff --git a/drivers/AES/AES.cpp b/hal/crypto/generic/drivers/AES/AES.cpp similarity index 100% rename from drivers/AES/AES.cpp rename to hal/crypto/generic/drivers/AES/AES.cpp diff --git a/drivers/AES/AES.h b/hal/crypto/generic/drivers/AES/AES.h similarity index 100% rename from drivers/AES/AES.h rename to hal/crypto/generic/drivers/AES/AES.h diff --git a/drivers/AES/AES_config.h b/hal/crypto/generic/drivers/AES/AES_config.h similarity index 100% rename from drivers/AES/AES_config.h rename to hal/crypto/generic/drivers/AES/AES_config.h diff --git a/drivers/AES/README.MD b/hal/crypto/generic/drivers/AES/README.MD similarity index 100% rename from drivers/AES/README.MD rename to hal/crypto/generic/drivers/AES/README.MD diff --git a/drivers/AES/keywords.txt b/hal/crypto/generic/drivers/AES/keywords.txt similarity index 100% rename from drivers/AES/keywords.txt rename to hal/crypto/generic/drivers/AES/keywords.txt diff --git a/hal/crypto/generic/drivers/HMAC_SHA256/hmac_sha256.cpp b/hal/crypto/generic/drivers/HMAC_SHA256/hmac_sha256.cpp new file mode 100644 index 000000000..f581a8782 --- /dev/null +++ b/hal/crypto/generic/drivers/HMAC_SHA256/hmac_sha256.cpp @@ -0,0 +1,64 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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 "hmac_sha256.h" + +void SHA256HMACInit(const uint8_t *key, size_t keyLength) +{ + (void)memset((void *)&SHA256keyBuffer, 0x00, BLOCK_LENGTH); + if (keyLength > BLOCK_LENGTH) { + // Hash long keys + SHA256Init(); + SHA256Add(key, keyLength); + SHA256Result(SHA256keyBuffer); + } else { + // Block length keys are used as is + (void)memcpy((void *)SHA256keyBuffer, (const void *)key, keyLength); + } + // Start inner hash + SHA256Init(); + for (uint8_t i = 0; i < BLOCK_LENGTH; i++) { + SHA256Add(SHA256keyBuffer[i] ^ HMAC_IPAD); + } +} + +void SHA256HMACAdd(const uint8_t data) +{ + SHA256Add(data); +} + +void SHA256HMACAdd(const uint8_t *data, size_t dataLength) +{ + SHA256Add(data, dataLength); +} + +void SHA256HMACResult(uint8_t *dest) +{ + uint8_t innerHash[HASH_LENGTH]; + // Complete inner hash + SHA256Result(innerHash); + // Calculate outer hash + SHA256Init(); + for (uint8_t i = 0; i < BLOCK_LENGTH; i++) { + SHA256Add(SHA256keyBuffer[i] ^ HMAC_OPAD); + } + SHA256Add(innerHash, HASH_LENGTH); + SHA256Result(dest); +} \ No newline at end of file diff --git a/hal/crypto/generic/drivers/HMAC_SHA256/hmac_sha256.h b/hal/crypto/generic/drivers/HMAC_SHA256/hmac_sha256.h new file mode 100644 index 000000000..38dd256ee --- /dev/null +++ b/hal/crypto/generic/drivers/HMAC_SHA256/hmac_sha256.h @@ -0,0 +1,28 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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 _HMAC_SHA256_ +#define _HMAC_SHA256_ + +#define HMAC_IPAD 0x36 //!< HMAC_IPAD +#define HMAC_OPAD 0x5c //!< HMAC_OPAD + + +#endif diff --git a/hal/crypto/generic/drivers/SHA256/sha256.cpp b/hal/crypto/generic/drivers/SHA256/sha256.cpp new file mode 100644 index 000000000..b14f77a98 --- /dev/null +++ b/hal/crypto/generic/drivers/SHA256/sha256.cpp @@ -0,0 +1,173 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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 "sha256.h" + +const uint32_t SHA256K[] PROGMEM = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +const uint8_t SHA256InitState[] PROGMEM = { + 0x67,0xe6,0x09,0x6a, // H0 + 0x85,0xae,0x67,0xbb, // H1 + 0x72,0xf3,0x6e,0x3c, // H2 + 0x3a,0xf5,0x4f,0xa5, // H3 + 0x7f,0x52,0x0e,0x51, // H4 + 0x8c,0x68,0x05,0x9b, // H5 + 0xab,0xd9,0x83,0x1f, // H6 + 0x19,0xcd,0xe0,0x5b // H7 +}; + +_SHA256buffer_t SHA256buffer; +uint8_t SHA256bufferOffset; +_SHA256state_t SHA256state; +uint32_t SHA256byteCount; +uint8_t SHA256keyBuffer[BLOCK_LENGTH]; + +void SHA256Init(void) +{ + (void)memcpy_P((void *)&SHA256state.b, (const void *)&SHA256InitState, 32); + SHA256byteCount = 0; + SHA256bufferOffset = 0; +} + +uint32_t SHA256ror32(const uint32_t number, const uint8_t bits) +{ + return ((number << (32 - bits)) | (number >> bits)); +} + +void SHA256hashBlock(void) +{ + uint32_t a, b, c, d, e, f, g, h, t1, t2; + + a = SHA256state.w[0]; + b = SHA256state.w[1]; + c = SHA256state.w[2]; + d = SHA256state.w[3]; + e = SHA256state.w[4]; + f = SHA256state.w[5]; + g = SHA256state.w[6]; + h = SHA256state.w[7]; + + for (uint8_t i = 0; i < 64; i++) { + if (i >= 16) { + t1 = SHA256buffer.w[i & 15] + SHA256buffer.w[(i - 7) & 15]; + t2 = SHA256buffer.w[(i - 2) & 15]; + t1 += SHA256ror32(t2, 17) ^ SHA256ror32(t2, 19) ^ (t2 >> 10); + t2 = SHA256buffer.w[(i - 15) & 15]; + t1 += SHA256ror32(t2, 7) ^ SHA256ror32(t2, 18) ^ (t2 >> 3); + SHA256buffer.w[i & 15] = t1; + } + t1 = h; + t1 += SHA256ror32(e, 6) ^ SHA256ror32(e, 11) ^ SHA256ror32(e, 25); // ∑1(e) + t1 += g ^ (e & (g ^ f)); // Ch(e,f,g) + t1 += pgm_read_dword(SHA256K + i); // Ki + t1 += SHA256buffer.w[i & 15]; // Wi + t2 = SHA256ror32(a, 2) ^ SHA256ror32(a, 13) ^ SHA256ror32(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; + } + SHA256state.w[0] += a; + SHA256state.w[1] += b; + SHA256state.w[2] += c; + SHA256state.w[3] += d; + SHA256state.w[4] += e; + SHA256state.w[5] += f; + SHA256state.w[6] += g; + SHA256state.w[7] += h; +} + +void SHA256addUncounted(const uint8_t data) +{ + SHA256buffer.b[SHA256bufferOffset ^ 3] = data; + SHA256bufferOffset++; + if (SHA256bufferOffset == BLOCK_LENGTH) { + SHA256hashBlock(); + SHA256bufferOffset = 0; + } +} + + +void SHA256Add(const uint8_t data) +{ + SHA256byteCount++; + SHA256addUncounted(data); +} + +void SHA256Add(const uint8_t *data, size_t dataLength) +{ + while (dataLength--) { + SHA256Add(*data++); + } +} + +void SHA256Result(uint8_t *dest) +{ + // Pad to complete the last block + SHA256addUncounted(0x80); + while (SHA256bufferOffset != 56) { + SHA256addUncounted(0x00); + } + + // Append length in the last 8 bytes + SHA256addUncounted(0); // We're only using 32 bit lengths + SHA256addUncounted(0); // But SHA-1 supports 64 bit lengths + SHA256addUncounted(0); // So zero pad the top bits + SHA256addUncounted(SHA256byteCount >> 29); // Shifting to multiply by 8 + SHA256addUncounted(SHA256byteCount >> 21); // as SHA-1 supports bitstreams as well as + SHA256addUncounted(SHA256byteCount >> 13); // byte. + SHA256addUncounted(SHA256byteCount >> 5); + SHA256addUncounted(SHA256byteCount << 3); + + // Swap byte order back + for (uint8_t i = 0; i<8; i++) { + uint32_t a, b; + a = SHA256state.w[i]; + b = a << 24; + b |= (a << 8) & 0x00ff0000; + b |= (a >> 8) & 0x0000ff00; + b |= a >> 24; + SHA256state.w[i] = b; + } + (void)memcpy((void *)dest, (const void *)SHA256state.b, 32); + // Return pointer to hash (20 characters) + //return SHA256state.b; +} + +void SHA256(uint8_t *dest, const uint8_t *data, size_t dataLength) +{ + SHA256Init(); + SHA256Add(data, dataLength); + SHA256Result(dest); +} diff --git a/hal/crypto/generic/drivers/SHA256/sha256.h b/hal/crypto/generic/drivers/SHA256/sha256.h new file mode 100644 index 000000000..4081a8e1b --- /dev/null +++ b/hal/crypto/generic/drivers/SHA256/sha256.h @@ -0,0 +1,43 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 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 _SHA256_H_ +#define _SHA256_H_ + +#define HASH_LENGTH 32 //!< HASH_LENGTH +#define BLOCK_LENGTH 64 //!< BLOCK_LENGTH + +/** +* @brief buffer for SHA256 calculator +*/ +union _SHA256buffer_t { + uint8_t b[BLOCK_LENGTH]; //!< SHA256 b + uint32_t w[BLOCK_LENGTH / 4]; //!< SHA256 w +}; + +/** +* @brief state variables for SHA256 calculator +*/ +union _SHA256state_t { + uint8_t b[HASH_LENGTH]; //!< SHA256 b + uint32_t w[HASH_LENGTH / 4]; //!< SHA256 w +}; + +#endif diff --git a/hal/transport/MyTransportHAL.cpp b/hal/transport/MyTransportHAL.cpp new file mode 100644 index 000000000..9bf35c452 --- /dev/null +++ b/hal/transport/MyTransportHAL.cpp @@ -0,0 +1,241 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 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 "MyTransportHAL.h" + +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) +#define TRANSPORT_HAL_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug +#else +#define TRANSPORT_HAL_DEBUG(x,...) //!< debug NULL +#endif + +bool transportHALInit(void) +{ + TRANSPORT_HAL_DEBUG(PSTR("THA:INIT\n")); +#if defined(MY_TRANSPORT_ENCRYPTION) + uint8_t transportPSK[16]; +#if defined(MY_ENCRYPTION_SIMPLE_PASSWD) + (void)memset((void *)transportPSK, 0, sizeof(transportPSK)); + (void)memcpy((void *)transportPSK, MY_ENCRYPTION_SIMPLE_PASSWD, + strnlen(MY_ENCRYPTION_SIMPLE_PASSWD, sizeof(transportPSK))); +#else + hwReadConfigBlock((void *)transportPSK, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, + sizeof(transportPSK)); +#endif +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + hwDebugBuf2Str((const uint8_t *)transportPSK, sizeof(transportPSK)); + TRANSPORT_HAL_DEBUG(PSTR("THA:INIT:PSK=%s\n"),hwDebugPrintStr); +#endif +#endif + bool result = transportInit(); + +#if defined(MY_TRANSPORT_ENCRYPTION) +#if defined(MY_RADIO_RFM69) + transportEncrypt((const char *)transportPSK); +#else + //set up AES-key + AES128CBCInit(transportPSK); +#endif + // Make sure it is purged from memory when set + (void)memset((void *)transportPSK, 0, + sizeof(transportPSK)); +#endif + return result; +} + +void transportHALSetAddress(const uint8_t address) +{ + TRANSPORT_HAL_DEBUG(PSTR("THA:SAD:ADDR=%" PRIu8 "\n"), address); + transportSetAddress(address); +} + +uint8_t transportHALGetAddress(void) +{ + uint8_t result = transportGetAddress(); + TRANSPORT_HAL_DEBUG(PSTR("THA:GAD:ADDR=%" PRIu8 "\n"), result); + return result; +} + +bool transportHALDataAvailable(void) +{ + bool result = transportDataAvailable(); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + if (result) { + TRANSPORT_HAL_DEBUG(PSTR("THA:DATA:AVAIL\n")); + } +#endif + return result; +} + +bool transportHALSanityCheck(void) +{ + bool result = transportSanityCheck(); + TRANSPORT_HAL_DEBUG(PSTR("THA:SAN:RES=%" PRIu8 "\n"), result); + return result; +} + +bool transportHALReceive(MyMessage *inMsg, uint8_t *msgLength) +{ + // set pointer to first byte of data structure + uint8_t *rx_data = &inMsg->last; + uint8_t payloadLength = transportReceive((void *)rx_data); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + hwDebugBuf2Str((const uint8_t *)rx_data, payloadLength); + TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:MSG=%s\n"), hwDebugPrintStr); +#endif +#if defined(MY_TRANSPORT_ENCRYPTION) && !defined(MY_RADIO_RFM69) + TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:DECRYPT\n")); + // has to be adjusted, WIP! + uint8_t IV[16] = { 0 }; + // decrypt data + AES128CBCDecrypt(IV, (uint8_t *)rx_data, payloadLength); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + hwDebugBuf2Str((const uint8_t *)rx_data, payloadLength); + TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:PLAIN=%s\n"), hwDebugPrintStr); +#endif +#endif + // Reject messages with incorrect protocol version + MyMessage tmp = *inMsg; + if (!tmp.isProtocolVersionValid()) { + setIndication(INDICATION_ERR_VERSION); + TRANSPORT_HAL_DEBUG(PSTR("!THA:RCV:PVER=%" PRIu8 "\n"), + tmp.getVersion()); // protocol version mismatch + return false; + } + *msgLength = tmp.getLength(); +#if defined(MY_TRANSPORT_ENCRYPTION) && !defined(MY_RADIO_RFM69) + // payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check +#else + // Reject payloads with incorrect length + const uint8_t expectedMessageLength = tmp.getExpectedMessageSize(); + if (payloadLength != expectedMessageLength) { + setIndication(INDICATION_ERR_LENGTH); + TRANSPORT_HAL_DEBUG(PSTR("!THA:RCV:LEN=%" PRIu8 ",EXP=%" PRIu8 "\n"), payloadLength, + expectedMessageLength); // invalid payload length + return false; + } +#endif + TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:MSG LEN=%" PRIu8 "\n"), payloadLength); + return true; +} + +bool transportHALSend(const uint8_t nextRecipient, const MyMessage *outMsg, const uint8_t len, + const bool noACK) +{ + if (outMsg == NULL) { + + // nothing to send + return false; + } +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + hwDebugBuf2Str((const uint8_t *)&outMsg->last, len); + TRANSPORT_HAL_DEBUG(PSTR("THA:SND:MSG=%s\n"), hwDebugPrintStr); +#endif + +#if defined(MY_TRANSPORT_ENCRYPTION) && !defined(MY_RADIO_RFM69) + TRANSPORT_HAL_DEBUG(PSTR("THA:SND:ENCRYPT\n")); + uint8_t *tx_data[MAX_MESSAGE_SIZE]; + // copy input data because it is read-only + (void)memcpy((void *)tx_data, (void *)&outMsg->last, len); + // We us IV vector filled with zeros but randomize unused bytes in encryption block + uint8_t IV[16] = { 0 }; + const uint8_t finalLength = len > 16 ? 32 : 16; + // fill block with random data + for (uint8_t i = len; i < finalLength; i++) { + *((uint8_t *)tx_data + i) = random(256); + } + //encrypt data + AES128CBCEncrypt(IV, (uint8_t *)tx_data, finalLength); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + hwDebugBuf2Str((const uint8_t *)tx_data, finalLength); + TRANSPORT_HAL_DEBUG(PSTR("THA:SND:CIP=%s\n"), hwDebugPrintStr); +#endif + +#else + const uint8_t *tx_data = &outMsg->last; + const uint8_t finalLength = len; +#endif + + bool result = transportSend(nextRecipient, (void *)tx_data, finalLength, noACK); + TRANSPORT_HAL_DEBUG(PSTR("THA:SND:MSG LEN=%" PRIu8 ",RES=%" PRIu8 "\n"), finalLength, result); + return result; +} + +void transportHALPowerDown(void) +{ + transportPowerDown(); +} + +void transportHALPowerUp(void) +{ + transportPowerUp(); +} + +void transportHALSleep(void) +{ + transportSleep(); +} + +void transportHALStandBy(void) +{ + transportStandBy(); +} + +int16_t transportHALGetSendingRSSI(void) +{ + int16_t result = transportGetSendingRSSI(); + return result; +} + +int16_t transportHALGetReceivingRSSI(void) +{ + int16_t result = transportGetReceivingRSSI(); + return result; +} + +int16_t transportHALGetSendingSNR(void) +{ + int16_t result = transportGetSendingSNR(); + return result; +} + +int16_t transportHALGetReceivingSNR(void) +{ + int16_t result = transportGetReceivingSNR(); + return result; +} + +int16_t transportHALGetTxPowerPercent(void) +{ + int16_t result = transportGetTxPowerPercent(); + return result; +} + +bool transportHALSetTxPowerPercent(const uint8_t powerPercent) +{ + bool result = transportSetTxPowerPercent(powerPercent); + return result; +} + +int16_t transportHALGetTxPowerLevel(void) +{ + int16_t result = transportGetTxPowerLevel(); + return result; +} diff --git a/hal/transport/MyTransportHAL.h b/hal/transport/MyTransportHAL.h index 308e0dd46..a9ad6d841 100644 --- a/hal/transport/MyTransportHAL.h +++ b/hal/transport/MyTransportHAL.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -15,6 +15,29 @@ * 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. + * + * TransportHAL debug log messages: + * + * |E| SYS | SUB | Message | Comment + * |-|-----|-------|----------------------------|--------------------------------------------------------------------- + * | | THA | INIT | | Initialize transportHAL + * | | THA | INIT | PSK=%%s | Load PSK for transport encryption (%AES) + * | | THA | SAD | ADDR=%%d | Set transport address (ADDR) + * | | THA | GAD | ADDR=%%d | Get trnasport address (ADDR) + * | | THA | DATA | AVAIL | Message available + * | | THA | SAN | RES=%%d | Transport sanity check, result (RES) + * | | THA | RCV | MSG=%%s | Receive message (MSG) + * | | THA | RCV | DECRYPT | Decrypt received message + * | | THA | RCV | PLAIN=%%s | Decrypted message (PLAIN) + * |!| THA | RCV | PVER=%%d | Message protocol version (PVER) mismatch + * |!| THA | RCV | LEN=%%d,EXP=%%d | Invalid message length (LEN), exptected length (EXP) + * | | THA | RCV | MSG LEN=%%d | Length of received message (LEN) + * | | THA | SND | MSG=%%s | Send message (MSG) + * | | THA | SND | ENCRYPT | Encrypt message to send (%AES) + * | | THA | SND | CIP=%%s | Ciphertext of encypted message (CIP) + * | | THA | SND | MSG LEN=%%d,RES=%%d | Sending message with length (LEN), result (RES) + * + * */ #ifndef MyTransportHAL_h @@ -61,15 +84,15 @@ typedef enum { * @brief Initialize transport HW * @return true if initialization successful */ -bool transportInit(void); +bool transportHALInit(void); /** * @brief Set node address */ -void transportSetAddress(const uint8_t address); +void transportHALSetAddress(const uint8_t address); /** * @brief Retrieve node address */ -uint8_t transportGetAddress(void) __attribute__((unused)); +uint8_t transportHALGetAddress(void); /** * @brief Send message * @param to recipient @@ -78,74 +101,76 @@ uint8_t transportGetAddress(void) __attribute__((unused)); * @param noACK do not wait for ACK * @return true if message sent successfully */ -bool transportSend(const uint8_t to, const void *data, const uint8_t len, - const bool noACK = false); +bool transportHALSend(const uint8_t nextRecipient, const MyMessage *outMsg, const uint8_t len, + const bool noACK); /** * @brief Verify if RX FIFO has pending messages * @return true if message available in RX FIFO */ -bool transportAvailable(void); +bool transportHALDataAvailable(void); /** * @brief Sanity check for transport: is transport HW still responsive? * @return true if transport HW is ok */ -bool transportSanityCheck(void); +bool transportHALSanityCheck(void); /** * @brief Receive message from FIFO -* @return length of received message (header + payload) +* @param inMsg +* @param msgLength length of received message (header + payload) +* @return True if valid message received */ -uint8_t transportReceive(void *data); +bool transportHALReceive(MyMessage *inMsg, uint8_t *msgLength); /** * @brief Power down transport HW (if corresponding MY_XYZ_POWER_PIN defined) */ -void transportPowerDown(void); +void transportHALPowerDown(void); /** * @brief Power up transport HW (if corresponding MY_XYZ_POWER_PIN defined) */ -void transportPowerUp(void); +void transportHALPowerUp(void); /** * @brief Set transport HW to sleep (no power down) */ -void transportSleep(void); +void transportHALSleep(void); /** * @brief Set transport HW to standby */ -void transportStandBy(void); +void transportHALStandBy(void); /** * @brief transportGetSendingRSSI * @return RSSI of outgoing message (via ACK packet) */ -int16_t transportGetSendingRSSI(void); +int16_t transportHALGetSendingRSSI(void); /** * @brief transportGetReceivingRSSI * @return RSSI of incoming message */ -int16_t transportGetReceivingRSSI(void); +int16_t transportHALGetReceivingRSSI(void); /** * @brief transportGetSendingSNR * @return SNR of outgoing message (via ACK packet) */ -int16_t transportGetSendingSNR(void); +int16_t transportHALGetSendingSNR(void); /** * @brief transportGetReceivingSNR * @return SNR of incoming message */ -int16_t transportGetReceivingSNR(void); +int16_t transportHALGetReceivingSNR(void); /** * @brief transportGetTxPowerPercent * @return TX power level in percent */ -int16_t transportGetTxPowerPercent(void); +int16_t transportHALGetTxPowerPercent(void); /** * @brief transportSetTxPowerPercent * @param powerPercent power level in percent * @return True if power level set */ -bool transportSetTxPowerPercent(const uint8_t powerPercent) __attribute__((unused)); +bool transportHALSetTxPowerPercent(const uint8_t powerPercent); /** * @brief transportGetTxPowerLevel * @return TX power in dBm */ -int16_t transportGetTxPowerLevel(void); +int16_t transportHALGetTxPowerLevel(void); #endif // MyTransportHAL_h diff --git a/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp b/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp index 248f2b974..af403b07b 100644 --- a/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp +++ b/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Copyright (C) 2017 Frank Holtz * Full contributor list: * https://github.com/mysensors/MySensors/graphs/contributors @@ -24,28 +24,8 @@ #include "drivers/CircularBuffer/CircularBuffer.h" -#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) -#include "drivers/AES/AES.cpp" -AES NRF5_ESB_aes; -uint8_t NRF5_ESB_dataenc[32] = {0}; -#endif - bool transportInit(void) { -#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) - uint8_t NRF5_ESB_psk[16]; -#ifdef MY_ENCRYPTION_SIMPLE_PASSWD - (void)memset(NRF5_ESB_psk, 0, 16); - (void)memcpy(NRF5_ESB_psk, MY_ENCRYPTION_SIMPLE_PASSWD, strnlen(MY_ENCRYPTION_SIMPLE_PASSWD, 16)); -#else - hwReadConfigBlock((void*)NRF5_ESB_psk, (void*)EEPROM_RF_ENCRYPTION_KEY_ADDRESS, - 16); -#endif - // set up AES-key - NRF5_ESB_aes.set_key(NRF5_ESB_psk, 16); - // Make sure it is purged from memory when set - (void)memset(NRF5_ESB_psk, 0, 16); -#endif return NRF5_ESB_initialize(); } @@ -62,21 +42,10 @@ uint8_t transportGetAddress(void) bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) { -#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) - // copy input data because it is read-only - (void)memcpy(NRF5_ESB_dataenc, data, len); - // has to be adjusted, WIP! - NRF5_ESB_aes.set_IV(0); - const uint8_t finalLength = len > 16 ? 32 : 16; - // encrypt data - NRF5_ESB_aes.cbc_encrypt(NRF5_ESB_dataenc, NRF5_ESB_dataenc, finalLength / 16); - return NRF5_ESB_sendMessage(to, NRF5_ESB_dataenc, finalLength, noACK); -#else return NRF5_ESB_sendMessage(to, data, len, noACK); -#endif } -bool transportAvailable(void) +bool transportDataAvailable(void) { return NRF5_ESB_isDataAvailable(); } @@ -90,15 +59,6 @@ uint8_t transportReceive(void *data) { uint8_t len = 0; len = NRF5_ESB_readMessage(data); -#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) - // has to be adjusted, WIP! - NRF5_ESB_aes.set_IV(0); - // decrypt data - if (NRF5_ESB_aes.cbc_decrypt((uint8_t *)(data), (uint8_t *)(data), - len > 16 ? 2 : 1) != AES_SUCCESS) { - len = 0; - } -#endif return len; } diff --git a/hal/transport/NRF5_ESB/driver/Radio.cpp b/hal/transport/NRF5_ESB/driver/Radio.cpp index e420c8658..381064b3d 100644 --- a/hal/transport/NRF5_ESB/driver/Radio.cpp +++ b/hal/transport/NRF5_ESB/driver/Radio.cpp @@ -54,10 +54,12 @@ bool NRF5_setTxPowerPercent(const uint8_t powerPercent) #endif return true; } + /* duplicate condition if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) { NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm; return true; } + */ if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) { NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm; return true; diff --git a/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp b/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp index f927a815b..538b53d16 100644 --- a/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp +++ b/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp @@ -152,7 +152,7 @@ static bool NRF5_ESB_initialize() // Packet configuration NRF_RADIO->PCNF1 = - (MAX_MESSAGE_LENGTH << RADIO_PCNF1_MAXLEN_Pos) | // maximum length + (MAX_MESSAGE_SIZE << RADIO_PCNF1_MAXLEN_Pos) | // maximum length (0 << RADIO_PCNF1_STATLEN_Pos) | // minimum message length ((MY_NRF5_ESB_ADDR_WIDTH - 1) << RADIO_PCNF1_BALEN_Pos) | // Set base address length (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | // Big endian @@ -424,8 +424,8 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len } // check length and truncate data - if (len > MAX_MESSAGE_LENGTH) { - len = MAX_MESSAGE_LENGTH; + if (len > MAX_MESSAGE_SIZE) { + len = MAX_MESSAGE_SIZE; } // copy data to tx_buffer diff --git a/hal/transport/NRF5_ESB/driver/Radio_ESB.h b/hal/transport/NRF5_ESB/driver/Radio_ESB.h index 8380eb2c6..bcd37640b 100644 --- a/hal/transport/NRF5_ESB/driver/Radio_ESB.h +++ b/hal/transport/NRF5_ESB/driver/Radio_ESB.h @@ -25,9 +25,9 @@ #include "Radio.h" #include -// Check maximum messae length -#if MAX_MESSAGE_LENGTH > (32) -#error "Unsupported message length. (MAX_MESSAGE_LENGTH)" +// Check maximum message length +#if MAX_MESSAGE_SIZE > (32) +#error "Unsupported message size. (MAX_MESSAGE_SIZE)" #endif // check rx buffer size @@ -153,7 +153,7 @@ typedef struct nrf5_radio_packet_s { uint8_t pid : 2; }; }; - uint8_t data[MAX_MESSAGE_LENGTH]; + uint8_t data[MAX_MESSAGE_SIZE]; int8_t rssi; /** Debug data structure */ #ifdef MY_DEBUG_VERBOSE_NRF5_ESB diff --git a/hal/transport/RF24/MyTransportRF24.cpp b/hal/transport/RF24/MyTransportRF24.cpp index dbcb112c9..3f7349ad1 100644 --- a/hal/transport/RF24/MyTransportRF24.cpp +++ b/hal/transport/RF24/MyTransportRF24.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,18 +19,12 @@ #include "hal/transport/RF24/driver/RF24.h" -#if defined(MY_RF24_ENABLE_ENCRYPTION) -#include "drivers/AES/AES.cpp" -AES RF24_aes; -uint8_t RF24_dataenc[32] = {0}; -#endif - #if defined(MY_RX_MESSAGE_BUFFER_FEATURE) #include "drivers/CircularBuffer/CircularBuffer.h" typedef struct _transportQueuedMessage { uint8_t m_len; // Length of the data - uint8_t m_data[MAX_MESSAGE_LENGTH]; // The raw data + uint8_t m_data[MAX_MESSAGE_SIZE]; // The raw data } transportQueuedMessage; /** Buffer to store queued messages in. */ @@ -62,20 +56,6 @@ static void transportRxCallback(void) bool transportInit(void) { -#if defined(MY_RF24_ENABLE_ENCRYPTION) - uint8_t RF24_psk[16]; -#ifdef MY_ENCRYPTION_SIMPLE_PASSWD - (void)memset(RF24_psk, 0, 16); - (void)memcpy(RF24_psk, MY_ENCRYPTION_SIMPLE_PASSWD, strnlen(MY_ENCRYPTION_SIMPLE_PASSWD, 16)); -#else - hwReadConfigBlock((void *)RF24_psk, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); -#endif - //set up AES-key - RF24_aes.set_key(RF24_psk, 16); - // Make sure it is purged from memory when set - (void)memset(RF24_psk, 0, 16); -#endif - #if defined(MY_RX_MESSAGE_BUFFER_FEATURE) RF24_registerReceiveCallback( transportRxCallback ); #endif @@ -95,21 +75,10 @@ uint8_t transportGetAddress(void) bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) { -#if defined(MY_RF24_ENABLE_ENCRYPTION) - // copy input data because it is read-only - (void)memcpy(RF24_dataenc,data,len); - // has to be adjusted, WIP! - RF24_aes.set_IV(0); - const uint8_t finalLength = len > 16 ? 32 : 16; - //encrypt data - RF24_aes.cbc_encrypt(RF24_dataenc, RF24_dataenc, finalLength / 16); - return RF24_sendMessage(to, RF24_dataenc, finalLength, noACK); -#else return RF24_sendMessage(to, data, len, noACK); -#endif } -bool transportAvailable(void) +bool transportDataAvailable(void) { #if defined(MY_RX_MESSAGE_BUFFER_FEATURE) (void)RF24_isDataAvailable; // Prevent 'defined but not used' warning @@ -136,14 +105,6 @@ uint8_t transportReceive(void *data) } #else len = RF24_readMessage(data); -#endif -#if defined(MY_RF24_ENABLE_ENCRYPTION) - // has to be adjusted, WIP! - RF24_aes.set_IV(0); - // decrypt data - if (RF24_aes.cbc_decrypt((uint8_t *)data, (uint8_t *)data, len > 16 ? 2 : 1) != AES_SUCCESS) { - len = 0; - } #endif return len; } diff --git a/hal/transport/RF24/driver/RF24.cpp b/hal/transport/RF24/driver/RF24.cpp index b6165293b..f50a37f6f 100644 --- a/hal/transport/RF24/driver/RF24.cpp +++ b/hal/transport/RF24/driver/RF24.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -105,7 +105,7 @@ LOCAL uint8_t RF24_spiMultiByteTransfer(const uint8_t cmd, uint8_t *buf, uint8_t *current++ = status; } } else { - status = RF24_SPI.transfer(*current++); + (void)RF24_SPI.transfer(*current++); } } #endif @@ -231,9 +231,9 @@ LOCAL uint8_t RF24_getObserveTX(void) return RF24_readByteRegister(RF24_REG_OBSERVE_TX); } -LOCAL void RF24_setStatus(const uint8_t status) +LOCAL uint8_t RF24_setStatus(const uint8_t status) { - RF24_writeByteRegister(RF24_REG_STATUS, status); + return RF24_writeByteRegister(RF24_REG_STATUS, status); } LOCAL void RF24_enableFeatures(void) @@ -307,34 +307,38 @@ LOCAL void RF24_standBy(void) LOCAL bool RF24_sendMessage(const uint8_t recipient, const void *buf, const uint8_t len, const bool noACK) { - uint8_t RF24_status; RF24_stopListening(); - RF24_openWritingPipe( recipient ); - RF24_DEBUG(PSTR("RF24:TXM:TO=%" PRIu8 ",LEN=%" PRIu8 "\n"),recipient,len); // send message + RF24_openWritingPipe(recipient); + RF24_DEBUG(PSTR("RF24:TXM:TO=%" PRIu8 ",LEN=%" PRIu8 "\n"), recipient, len); // send message // flush TX FIFO RF24_flushTX(); + if (noACK) { + // noACK messages are only sent once + RF24_setRetries(RF24_SET_ARD, 0); + } // this command is affected in clones (e.g. Si24R1): flipped NoACK bit when using W_TX_PAYLOAD_NO_ACK / W_TX_PAYLOAD // AutoACK is disabled on the broadcasting pipe - NO_ACK prevents resending - RF24_spiMultiByteTransfer((recipient == RF24_BROADCAST_ADDRESS || - noACK) ? RF24_CMD_WRITE_TX_PAYLOAD_NO_ACK : - RF24_CMD_WRITE_TX_PAYLOAD, (uint8_t *)buf, len, false ); + (void)RF24_spiMultiByteTransfer(RF24_CMD_WRITE_TX_PAYLOAD, (uint8_t *)buf, len, false); // go, TX starts after ~10us, CE high also enables PA+LNA on supported HW RF24_ce(HIGH); // timeout counter to detect HW issues uint16_t timeout = 0xFFFF; - do { - RF24_status = RF24_getStatus(); - } while (!(RF24_status & ( _BV(RF24_MAX_RT) | _BV(RF24_TX_DS) )) && timeout--); + while (!(RF24_getStatus() & (_BV(RF24_MAX_RT) | _BV(RF24_TX_DS))) && timeout--) { + doYield(); + } // timeout value after successful TX on 16Mhz AVR ~ 65500, i.e. msg is transmitted after ~36 loop cycles RF24_ce(LOW); // reset interrupts - RF24_setStatus(_BV(RF24_TX_DS) | _BV(RF24_MAX_RT) ); + const uint8_t RF24_status = RF24_setStatus(_BV(RF24_RX_DR) | _BV(RF24_TX_DS) | _BV(RF24_MAX_RT)); // Max retries exceeded - if(RF24_status & _BV(RF24_MAX_RT)) { + if (RF24_status & _BV(RF24_MAX_RT)) { // flush packet - RF24_DEBUG(PSTR("!RF24:TXM:MAX_RT\n")); // max retries, no ACK + RF24_DEBUG(PSTR("?RF24:TXM:MAX_RT\n")); // max retries (normal messages) and noACK messages RF24_flushTX(); } + if (noACK) { + RF24_setRetries(RF24_SET_ARD, RF24_SET_ARC); + } RF24_startListening(); // true if message sent return (RF24_status & _BV(RF24_TX_DS) || noACK); @@ -354,17 +358,23 @@ LOCAL uint8_t RF24_getDynamicPayloadSize(void) LOCAL bool RF24_isDataAvailable(void) { - return (!(RF24_getFIFOStatus() & _BV(0)) ); + // prevent debug message flooding +#if defined(MY_DEBUG_VERBOSE_RF24) + const uint8_t value = RF24_spiMultiByteTransfer(RF24_CMD_READ_REGISTER | (RF24_REGISTER_MASK & + (RF24_REG_FIFO_STATUS)), NULL, 1, true); + return (bool)(!(value & _BV(RF24_RX_EMPTY))); +#else + return (bool)(!(RF24_getFIFOStatus() & _BV(RF24_RX_EMPTY)) ); +#endif } - LOCAL uint8_t RF24_readMessage(void *buf) { const uint8_t len = RF24_getDynamicPayloadSize(); RF24_DEBUG(PSTR("RF24:RXM:LEN=%" PRIu8 "\n"), len); // read message - RF24_spiMultiByteTransfer(RF24_CMD_READ_RX_PAYLOAD,(uint8_t *)buf,len,true); + RF24_spiMultiByteTransfer(RF24_CMD_READ_RX_PAYLOAD, (uint8_t *)buf, len, true); // clear RX interrupt - RF24_setStatus(_BV(RF24_RX_DR)); + (void)RF24_setStatus(_BV(RF24_RX_DR)); return len; } @@ -445,7 +455,7 @@ LOCAL bool RF24_getReceivedPowerDetector(void) } #if defined(MY_RX_MESSAGE_BUFFER_FEATURE) -LOCAL void RF24_irqHandler(void) +LOCAL void IRQ_HANDLER_ATTR RF24_irqHandler(void) { if (RF24_receiveCallback) { #if defined(MY_GATEWAY_SERIAL) && !defined(__linux__) @@ -502,7 +512,6 @@ LOCAL void RF24_registerReceiveCallback(RF24_receiveCallbackType cb) LOCAL bool RF24_initialize(void) { - RF24_DEBUG(PSTR("RF24:INIT\n")); RF24_DEBUG(PSTR("RF24:INIT:PIN,CE=%" PRIu8 ",CS=%" PRIu8 "\n"), MY_RF24_CE_PIN, MY_RF24_CS_PIN); // Initialize pins & HW #if defined(MY_RF24_POWER_PIN) diff --git a/hal/transport/RF24/driver/RF24.h b/hal/transport/RF24/driver/RF24.h index 9780a576f..cdf54192a 100644 --- a/hal/transport/RF24/driver/RF24.h +++ b/hal/transport/RF24/driver/RF24.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -36,8 +36,7 @@ * * |E| SYS | SUB | Message | Comment * |-|------|------|----------------------|--------------------------------------------------------------------- -* | | RF24 | INIT | | Initialise RF24 radio -* | | RF24 | INIT | PIN,CE=%%d,CS=%%d | Pin configuration: chip enable (CE), chip select (CS) +* | | RF24 | INIT | PIN,CE=%%d,CS=%%d | Initialise RF24 radio, pin configuration: chip enable (CE), chip select (CS) * |!| RF24 | INIT | SANCHK FAIL | Sanity check failed, check wiring or replace module * | | RF24 | SPP | PCT=%%d,TX LEVEL=%%d | Set TX level, input TX percent (PCT) * | | RF24 | RBR | REG=%%d,VAL=%%d | Read register (REG), value=(VAL) @@ -62,6 +61,10 @@ #include "RF24registers.h" +#if !defined(RF24_SPI) +#define RF24_SPI hwSPI //!< default SPI +#endif + #if defined(ARDUINO_ARCH_AVR) #define DEFAULT_RF24_CE_PIN (9) //!< DEFAULT_RF24_CE_PIN #elif defined(ARDUINO_ARCH_ESP8266) @@ -92,30 +95,6 @@ #define RF24_BROADCAST_ADDRESS (255u) //!< RF24_BROADCAST_ADDRESS -#if defined(ARDUINO) && !defined(__arm__) && !defined(RF24_SPI) -#include -#if defined(MY_SOFTSPI) -SoftSPI -RF24_SPI; -#else -#define RF24_SPI SPI //!< SPI -#endif -#else -#include -#include -#include - -#if defined(__arm__) || defined(__linux__) -#include -#else -extern HardwareSPI SPI; //!< SPI -#endif - -#if !defined(RF24_SPI) -#define RF24_SPI SPI //!< SPI -#endif -#endif - // verify RF24 IRQ defs #if defined(MY_RX_MESSAGE_BUFFER_FEATURE) #if !defined(MY_RF24_IRQ_PIN) @@ -218,7 +197,7 @@ LOCAL uint8_t RF24_getStatus(void); * @brief RF24_getFIFOStatus * @return */ -LOCAL uint8_t RF24_getFIFOStatus(void); +LOCAL uint8_t RF24_getFIFOStatus(void) __attribute__((unused)); /** * @brief RF24_openWritingPipe * @param recipient @@ -365,8 +344,9 @@ LOCAL uint8_t RF24_getObserveTX(void); /** * @brief RF24_setStatus * @param status +* @return status byte before setting new status */ -LOCAL void RF24_setStatus(const uint8_t status); +LOCAL uint8_t RF24_setStatus(const uint8_t status); /** * @brief RF24_enableFeatures */ diff --git a/hal/transport/RF24/driver/RF24registers.h b/hal/transport/RF24/driver/RF24registers.h index c2cf5942c..0691f4ed5 100644 --- a/hal/transport/RF24/driver/RF24registers.h +++ b/hal/transport/RF24/driver/RF24registers.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad -* Copyright (C) 2013-2018 Sensnology AB +* Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/transport/RFM69/MyTransportRFM69.cpp b/hal/transport/RFM69/MyTransportRFM69.cpp index d1f5371be..6ec2e5dbd 100644 --- a/hal/transport/RFM69/MyTransportRFM69.cpp +++ b/hal/transport/RFM69/MyTransportRFM69.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -59,14 +59,10 @@ uint8_t transportGetAddress(void) bool transportSend(const uint8_t to, const void *data, uint8_t len, const bool noACK) { - if (noACK) { - (void)RFM69_sendWithRetry(to, data, len, 0, 0); - return true; - } - return RFM69_sendWithRetry(to, data, len); + return RFM69_sendWithRetry(to, data, len, noACK); } -bool transportAvailable(void) +bool transportDataAvailable(void) { RFM69_handler(); return RFM69_available(); @@ -79,7 +75,12 @@ bool transportSanityCheck(void) uint8_t transportReceive(void *data) { - return RFM69_receive((uint8_t *)data, MAX_MESSAGE_LENGTH); + return RFM69_receive((uint8_t *)data, MAX_MESSAGE_SIZE); +} + +void transportEncrypt(const char *key) +{ + RFM69_encrypt(key); } void transportSleep(void) @@ -185,6 +186,11 @@ bool transportInit(void) return false; } +void transportEncrypt(const char *key) +{ + _radio.encrypt(key); +} + void transportSetAddress(const uint8_t address) { _address = address; @@ -205,7 +211,7 @@ bool transportSend(const uint8_t to, const void *data, const uint8_t len, const return _radio.sendWithRetry(to, data, len); } -bool transportAvailable(void) +bool transportDataAvailable(void) { return _radio.receiveDone(); } @@ -218,7 +224,7 @@ bool transportSanityCheck(void) uint8_t transportReceive(void *data) { // save payload length - const uint8_t dataLen = _radio.DATALEN < MAX_MESSAGE_LENGTH? _radio.DATALEN : MAX_MESSAGE_LENGTH; + const uint8_t dataLen = _radio.DATALEN < MAX_MESSAGE_SIZE ? _radio.DATALEN : MAX_MESSAGE_SIZE; (void)memcpy((void *)data, (void *)_radio.DATA, dataLen); // Send ack back if this message wasn't a broadcast if (_radio.ACKRequested()) { @@ -284,4 +290,12 @@ bool transportSetTxPowerLevel(const uint8_t powerLevel) return false; } +bool transportSetTxPowerPercent(const uint8_t powerPercent) +{ + // not implemented + (void)powerPercent; + return false; +} + + #endif diff --git a/hal/transport/RFM69/driver/new/RFM69_new.cpp b/hal/transport/RFM69/driver/new/RFM69_new.cpp index 7976267e1..cf4f0f02d 100644 --- a/hal/transport/RFM69/driver/new/RFM69_new.cpp +++ b/hal/transport/RFM69/driver/new/RFM69_new.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -242,7 +242,7 @@ LOCAL void RFM69_clearFIFO(void) (void)RFM69_writeReg(RFM69_REG_IRQFLAGS2, RFM69_IRQFLAGS2_FIFOOVERRUN); } // IRQ handler: PayloadReady (RX) & PacketSent (TX) mapped to DI0 -LOCAL void RFM69_interruptHandler(void) +LOCAL void IRQ_HANDLER_ATTR RFM69_interruptHandler(void) { // set flag RFM69_irq = true; @@ -417,7 +417,6 @@ LOCAL bool RFM69_send(const uint8_t recipient, uint8_t *data, const uint8_t len, packet.header.version = RFM69_PACKET_HEADER_VERSION; packet.header.sender = RFM69.address; packet.header.recipient = recipient; - packet.header.controlFlags = 0u; // reset packet.payloadLen = min(len, (uint8_t)RFM69_MAX_PAYLOAD_LEN); packet.header.controlFlags = flags; (void)memcpy((void *)&packet.payload, (void *)data, packet.payloadLen); // copy payload @@ -610,40 +609,40 @@ LOCAL void RFM69_ATCmode(const bool onOff, const int16_t targetRSSI) LOCAL bool RFM69_sendWithRetry(const uint8_t recipient, const void *buffer, - const uint8_t bufferSize, const uint8_t retries, const uint32_t retryWaitTimeMS) + const uint8_t bufferSize, const bool noACK) { - for (uint8_t retry = 0; retry <= retries; retry++) { + for (uint8_t retry = 0; retry < RFM69_RETRIES; retry++) { RFM69_DEBUG(PSTR("RFM69:SWR:SEND,TO=%" PRIu8 ",SEQ=%" PRIu16 ",RETRY=%" PRIu8 "\n"), recipient, RFM69.txSequenceNumber,retry); rfm69_controlFlags_t flags = 0u; // reset all flags - RFM69_setACKRequested(flags, (recipient != RFM69_BROADCAST_ADDRESS)); + RFM69_setACKRequested(flags, !noACK); RFM69_setACKRSSIReport(flags, RFM69.ATCenabled); (void)RFM69_send(recipient, (uint8_t *)buffer, bufferSize, flags, !retry); - if (recipient == RFM69_BROADCAST_ADDRESS) { + if (noACK) { // no ACK requested return true; } // radio is in RX const uint32_t enterMS = hwMillis(); - while (hwMillis() - enterMS < retryWaitTimeMS && !RFM69.dataReceived) { + while (hwMillis() - enterMS < RFM69_RETRY_TIMEOUT_MS && !RFM69.dataReceived) { RFM69_handler(); if (RFM69.ackReceived) { // radio is in stdby - const uint8_t sender = RFM69.currentPacket.header.sender; + const uint8_t ACKsender = RFM69.currentPacket.header.sender; const rfm69_sequenceNumber_t ACKsequenceNumber = RFM69.currentPacket.ACK.sequenceNumber; - const rfm69_controlFlags_t flags = RFM69.currentPacket.header.controlFlags; - const rfm69_RSSI_t RSSI = RFM69.currentPacket.ACK.RSSI; + const rfm69_controlFlags_t ACKflags = RFM69.currentPacket.header.controlFlags; + const rfm69_RSSI_t ACKRSSI = RFM69.currentPacket.ACK.RSSI; RFM69.ackReceived = false; // packet read, back to RX RFM69_setRadioMode(RFM69_RADIO_MODE_RX); - if (sender == recipient && ACKsequenceNumber == RFM69.txSequenceNumber) { - RFM69_DEBUG(PSTR("RFM69:SWR:ACK,FROM=%" PRIu8 ",SEQ=%" PRIu8 ",RSSI=%" PRIi16 "\n"),sender, + if (ACKsender == recipient && ACKsequenceNumber == RFM69.txSequenceNumber) { + RFM69_DEBUG(PSTR("RFM69:SWR:ACK,FROM=%" PRIu8 ",SEQ=%" PRIu8 ",RSSI=%" PRIi16 "\n"), ACKsender, ACKsequenceNumber, - RFM69_internalToRSSI(RSSI)); + RFM69_internalToRSSI(ACKRSSI)); // ATC - if (RFM69.ATCenabled && RFM69_getACKRSSIReport(flags)) { - (void)RFM69_executeATC(RSSI,RFM69.ATCtargetRSSI); + if (RFM69.ATCenabled && RFM69_getACKRSSIReport(ACKflags)) { + (void)RFM69_executeATC(ACKRSSI, RFM69.ATCtargetRSSI); } return true; } // seq check diff --git a/hal/transport/RFM69/driver/new/RFM69_new.h b/hal/transport/RFM69/driver/new/RFM69_new.h index fee55b826..5f5b0b297 100644 --- a/hal/transport/RFM69/driver/new/RFM69_new.h +++ b/hal/transport/RFM69/driver/new/RFM69_new.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -71,6 +71,10 @@ #include "RFM69registers_new.h" +#if !defined(RFM69_SPI) +#define RFM69_SPI hwSPI //!< default SPI +#endif + #if defined(ARDUINO_ARCH_AVR) #if defined(__AVR_ATmega32U4__) #define DEFAULT_RFM69_IRQ_PIN (3) //!< DEFAULT_RFM69_IRQ_PIN @@ -100,26 +104,6 @@ #define RFM69_SPI_DATA_ORDER MSBFIRST //!< SPI data order #define RFM69_SPI_DATA_MODE SPI_MODE0 //!< SPI mode -#if defined(ARDUINO) && !defined(__arm__) && !defined(RFM69_SPI) -#include -#if defined(MY_SOFTSPI) -SoftSPIRFM69_SPI; -#else -#define RFM69_SPI SPI -#endif -#else -#if defined(__arm__) || defined(__linux__) -#include -#else -extern HardwareSPI SPI; //!< SPI -#endif - -#if !defined(RFM69_SPI) -#define RFM69_SPI SPI //!< SPI -#endif -#endif - - // Additional radio settings #define RFM69_SYNCVALUE1 (0x2D) //!< Make this compatible with sync1 byte of RFM12B lib @@ -482,13 +466,12 @@ LOCAL void RFM69_sendACK(const uint8_t recipient, const rfm69_sequenceNumber_t s * @param recipient * @param buffer * @param bufferSize -* @param retries -* @param retryWaitTimeMS +* @param noACK * @return True if packet successfully sent */ LOCAL bool RFM69_sendWithRetry(const uint8_t recipient, const void *buffer, const uint8_t bufferSize, - const uint8_t retries = RFM69_RETRIES, const uint32_t retryWaitTimeMS = RFM69_RETRY_TIMEOUT_MS); + const bool noACK); /** * @brief RFM69_setRadioMode diff --git a/hal/transport/RFM69/driver/new/RFM69registers_new.h b/hal/transport/RFM69/driver/new/RFM69registers_new.h index bd1a349fc..ff17ebb50 100644 --- a/hal/transport/RFM69/driver/new/RFM69registers_new.h +++ b/hal/transport/RFM69/driver/new/RFM69registers_new.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/transport/RFM69/driver/old/RFM69_old.cpp b/hal/transport/RFM69/driver/old/RFM69_old.cpp index ede5b955b..9d840a3dd 100644 --- a/hal/transport/RFM69/driver/old/RFM69_old.cpp +++ b/hal/transport/RFM69/driver/old/RFM69_old.cpp @@ -30,7 +30,6 @@ // ********************************************************************************** #include "RFM69_old.h" #include "RFM69registers_old.h" -#include volatile uint8_t RFM69::DATA[RFM69_MAX_DATA_LEN]; volatile uint8_t RFM69::_mode; // current transceiver state @@ -94,7 +93,7 @@ bool RFM69::initialize(uint8_t freqBand, uint8_t nodeID, uint8_t networkID) hwPinMode(_slaveSelectPin, OUTPUT); hwPinMode(_interruptPin, INPUT); - SPI.begin(); + RFM69_SPI.begin(); unsigned long start = hwMillis(); const uint8_t timeout = 50; do { @@ -132,7 +131,7 @@ bool RFM69::initialize(uint8_t freqBand, uint8_t nodeID, uint8_t networkID) return false; } - //SPI.usingInterrupt(_interruptNum); + //RFM69_SPI.usingInterrupt(_interruptNum); attachInterrupt(_interruptNum, RFM69::isr0, RISING); selfPointer = this; @@ -393,14 +392,14 @@ void RFM69::sendFrame(uint8_t toAddress, const void* buffer, uint8_t bufferSize, // write to FIFO select(); - SPI.transfer(REG_FIFO | 0x80); - SPI.transfer(bufferSize + 3); - SPI.transfer(toAddress); - SPI.transfer(_address); - SPI.transfer(CTLbyte); + RFM69_SPI.transfer(REG_FIFO | 0x80); + RFM69_SPI.transfer(bufferSize + 3); + RFM69_SPI.transfer(toAddress); + RFM69_SPI.transfer(_address); + RFM69_SPI.transfer(CTLbyte); for (uint8_t i = 0; i < bufferSize; i++) { - SPI.transfer(((uint8_t *)buffer)[i]); + RFM69_SPI.transfer(((uint8_t *)buffer)[i]); } unselect(); @@ -415,7 +414,7 @@ void RFM69::sendFrame(uint8_t toAddress, const void* buffer, uint8_t bufferSize, } // internal function - interrupt gets called when a packet is received -void RFM69::interruptHandler() +void IRQ_HANDLER_ATTR RFM69::interruptHandler() { //hwPinMode(4, OUTPUT); //hwDigitalWrite(4, 1); @@ -423,10 +422,10 @@ void RFM69::interruptHandler() //RSSI = readRSSI(); setMode(RFM69_MODE_STANDBY); select(); - SPI.transfer(REG_FIFO & 0x7F); - PAYLOADLEN = SPI.transfer(0); + RFM69_SPI.transfer(REG_FIFO & 0x7F); + PAYLOADLEN = RFM69_SPI.transfer(0); PAYLOADLEN = PAYLOADLEN > 66 ? 66 : PAYLOADLEN; // precaution - TARGETID = SPI.transfer(0); + TARGETID = RFM69_SPI.transfer(0); if(!(_promiscuousMode || TARGETID == _address || TARGETID == RFM69_BROADCAST_ADDR) // match this node's address, or broadcast address or anything in promiscuous mode @@ -440,8 +439,8 @@ void RFM69::interruptHandler() } DATALEN = PAYLOADLEN - 3; - SENDERID = SPI.transfer(0); - uint8_t CTLbyte = SPI.transfer(0); + SENDERID = RFM69_SPI.transfer(0); + uint8_t CTLbyte = RFM69_SPI.transfer(0); ACK_RECEIVED = CTLbyte & RFM69_CTL_SENDACK; // extract ACK-received flag ACK_REQUESTED = CTLbyte & RFM69_CTL_REQACK; // extract ACK-requested flag @@ -449,7 +448,7 @@ void RFM69::interruptHandler() interruptHook(CTLbyte); // TWS: hook to derived class interrupt function for (uint8_t i = 0; i < DATALEN; i++) { - DATA[i] = SPI.transfer(0); + DATA[i] = RFM69_SPI.transfer(0); } if (DATALEN < RFM69_MAX_DATA_LEN) { DATA[DATALEN] = 0; // add null at end of string @@ -462,7 +461,7 @@ void RFM69::interruptHandler() } // internal function -void RFM69::isr0() +void IRQ_HANDLER_ATTR RFM69::isr0() { selfPointer->interruptHandler(); } @@ -511,9 +510,9 @@ void RFM69::encrypt(const char* key) setMode(RFM69_MODE_STANDBY); if (key != 0) { select(); - SPI.transfer(REG_AESKEY1 | 0x80); + RFM69_SPI.transfer(REG_AESKEY1 | 0x80); for (uint8_t i = 0; i < 16; i++) { - SPI.transfer(key[i]); + RFM69_SPI.transfer(key[i]); } unselect(); } @@ -537,8 +536,8 @@ int16_t RFM69::readRSSI(bool forceTrigger) uint8_t RFM69::readReg(uint8_t addr) { select(); - SPI.transfer(addr & 0x7F); - uint8_t regval = SPI.transfer(0); + RFM69_SPI.transfer(addr & 0x7F); + uint8_t regval = RFM69_SPI.transfer(0); unselect(); return regval; } @@ -546,8 +545,8 @@ uint8_t RFM69::readReg(uint8_t addr) void RFM69::writeReg(uint8_t addr, uint8_t value) { select(); - SPI.transfer(addr | 0x80); - SPI.transfer(value); + RFM69_SPI.transfer(addr | 0x80); + RFM69_SPI.transfer(value); unselect(); } @@ -561,9 +560,9 @@ void RFM69::select() _SPSR = SPSR; #endif // set RFM69 SPI settings - SPI.setDataMode(SPI_MODE0); - SPI.setBitOrder(MSBFIRST); - SPI.setClockDivider(RFM69_CLOCK_DIV); + RFM69_SPI.setDataMode(SPI_MODE0); + RFM69_SPI.setBitOrder(MSBFIRST); + RFM69_SPI.setClockDivider(RFM69_CLOCK_DIV); hwDigitalWrite(_slaveSelectPin, LOW); } @@ -650,8 +649,8 @@ void RFM69::readAllRegs() Serial.println("Address - HEX - BIN"); for (uint8_t regAddr = 1; regAddr <= 0x4F; regAddr++) { select(); - SPI.transfer(regAddr & 0x7F); // send address + r/w bit - uint8_t regVal = SPI.transfer(0); + RFM69_SPI.transfer(regAddr & 0x7F); // send address + r/w bit + uint8_t regVal = RFM69_SPI.transfer(0); unselect(); Serial.print(regAddr, HEX); diff --git a/hal/transport/RFM69/driver/old/RFM69_old.h b/hal/transport/RFM69/driver/old/RFM69_old.h index 9e56b6a00..4b3f95245 100644 --- a/hal/transport/RFM69/driver/old/RFM69_old.h +++ b/hal/transport/RFM69/driver/old/RFM69_old.h @@ -1,4 +1,4 @@ -// ********************************************************************************** +// ********************************************************************************** // Driver definition for HopeRF RFM69W/RFM69HW/RFM69CW/RFM69HCW, Semtech SX1231/1231H // ********************************************************************************** // Copyright Felix Rusu (2014), felix@lowpowerlab.com @@ -30,10 +30,12 @@ // ********************************************************************************** #ifndef RFM69_h #define RFM69_h -#include // assumes Arduino IDE v1.0 or greater -#include -#define RFM69_MAX_DATA_LEN 61 // to take advantage of the built in AES/CRC we want to limit the frame size to the internal FIFO size (66 bytes - 3 bytes overhead - 2 bytes crc) +#if !defined(RFM69_SPI) +#define RFM69_SPI hwSPI //!< default SPI +#endif + +#define RFM69_MAX_DATA_LEN (61u) // to take advantage of the built in AES/CRC we want to limit the frame size to the internal FIFO size (66 bytes - 3 bytes overhead - 2 bytes crc) #if defined(ARDUINO_ARCH_AVR) #if defined(__AVR_ATmega32U4__) @@ -138,6 +140,7 @@ class RFM69 * @param isRFM69HW Set to @c true to indicate RFM69HW variant. * @param interruptNum Interrupt number. */ + // cppcheck-suppress uninitMemberVar RFM69(uint8_t slaveSelectPin=MY_RFM69_CS_PIN, uint8_t interruptPin=MY_RFM69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=digitalPinToInterrupt(MY_RFM69_IRQ_PIN)) diff --git a/hal/transport/RFM95/MyTransportRFM95.cpp b/hal/transport/RFM95/MyTransportRFM95.cpp index 844dbf739..3d5bfdc80 100644 --- a/hal/transport/RFM95/MyTransportRFM95.cpp +++ b/hal/transport/RFM95/MyTransportRFM95.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,35 +18,9 @@ */ #include "hal/transport/RFM95/driver/RFM95.h" -#if defined(MY_RFM95_ENABLE_ENCRYPTION) -#include "drivers/AES/AES.h" -#endif - -#if defined(MY_RFM95_ENABLE_ENCRYPTION) -AES RFM95_aes; -uint8_t RFM95_dataenc[32] = {0}; -#endif - -#if defined(MY_RFM95_ENABLE_ENCRYPTION) -#include "drivers/AES/AES.cpp" -#endif bool transportInit(void) { -#if defined(MY_RFM95_ENABLE_ENCRYPTION) - uint8_t RFM95_psk[16]; -#ifdef MY_SIGNING_SIMPLE_PASSWD - (void)memset((void *)RFM95_psk, 0, 16); - (void)memcpy((void *)RFM95_psk, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD, 16)); -#else - hwReadConfigBlock((void *)RFM95_psk, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); -#endif - //set up AES-key - RFM95_aes.set_key(RFM95_psk, 16); - // Make sure it is purged from memory when set - (void)memset((void *)RFM95_psk, 0, 16); -#endif - const bool result = RFM95_initialise(MY_RFM95_FREQUENCY); #if defined(MY_RFM95_TCXO) RFM95_enableTCXO(); @@ -70,29 +44,10 @@ uint8_t transportGetAddress(void) bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) { -#if defined(MY_RFM95_ENABLE_ENCRYPTION) - // copy input data because it is read-only - (void)memcpy((void *)RFM95_dataenc, (const void *)data, len); - // has to be adjusted, WIP! - RFM95_aes.set_IV(0); - const uint8_t finalLength = len > 16 ? 32 : 16; - //encrypt data - RFM95_aes.cbc_encrypt(RFM95_dataenc, RFM95_dataenc, finalLength / 16); - if (noACK) { - (void)RFM95_sendWithRetry(to, RFM95_dataenc, finalLength, 0, 0); - return true; - } - return RFM95_sendWithRetry(to, RFM95_dataenc, finalLength); -#else - if (noACK) { - (void)RFM95_sendWithRetry(to, data, len, 0, 0); - return true; - } - return RFM95_sendWithRetry(to, data, len); -#endif + return RFM95_sendWithRetry(to, data, len, noACK); } -bool transportAvailable(void) +bool transportDataAvailable(void) { RFM95_handler(); return RFM95_available(); @@ -105,15 +60,7 @@ bool transportSanityCheck(void) uint8_t transportReceive(void *data) { - uint8_t len = RFM95_receive((uint8_t *)data, MAX_MESSAGE_LENGTH); -#if defined(MY_RFM95_ENABLE_ENCRYPTION) - // has to be adjusted, WIP! - RFM95_aes.set_IV(0); - // decrypt data - if (RFM95_aes.cbc_decrypt((uint8_t *)data, (uint8_t *)data, len > 16 ? 2 : 1) != AES_SUCCESS) { - len = 0; - } -#endif + uint8_t len = RFM95_receive((uint8_t *)data, MAX_MESSAGE_SIZE); return len; } diff --git a/hal/transport/RFM95/driver/RFM95.cpp b/hal/transport/RFM95/driver/RFM95.cpp index 8111a23bc..aa40b57f3 100644 --- a/hal/transport/RFM95/driver/RFM95.cpp +++ b/hal/transport/RFM95/driver/RFM95.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -233,7 +233,7 @@ LOCAL bool RFM95_initialise(const uint32_t frequencyHz) return true; } -LOCAL void RFM95_interruptHandler(void) +LOCAL void IRQ_HANDLER_ATTR RFM95_interruptHandler(void) { // set flag RFM95_irq = true; @@ -539,24 +539,24 @@ LOCAL bool RFM95_executeATC(const rfm95_RSSI_t currentRSSI, const rfm95_RSSI_t t } LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, - const uint8_t bufferSize, const uint8_t retries, const uint32_t retryWaitTime) + const uint8_t bufferSize, const bool noACK) { - for (uint8_t retry = 0; retry <= retries; retry++) { + for (uint8_t retry = 0; retry < RFM95_RETRIES; retry++) { RFM95_DEBUG(PSTR("RFM95:SWR:SEND,TO=%" PRIu8 ",SEQ=%" PRIu16 ",RETRY=%" PRIu8 "\n"), recipient, RFM95.txSequenceNumber, retry); rfm95_controlFlags_t flags = 0u; - RFM95_setACKRequested(flags, (recipient != RFM95_BROADCAST_ADDRESS)); + RFM95_setACKRequested(flags, !noACK); // send packet if (!RFM95_send(recipient, (uint8_t *)buffer, bufferSize, flags, !retry)) { return false; } (void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX); - if (recipient == RFM95_BROADCAST_ADDRESS) { + if (noACK) { return true; } const uint32_t enterMS = hwMillis(); - while (hwMillis() - enterMS < retryWaitTime && !RFM95.dataReceived) { + while (hwMillis() - enterMS < RFM95_RETRY_TIMEOUT_MS && !RFM95.dataReceived) { RFM95_handler(); if (RFM95.ackReceived) { const uint8_t sender = RFM95.currentPacket.header.sender; diff --git a/hal/transport/RFM95/driver/RFM95.h b/hal/transport/RFM95/driver/RFM95.h index fb9164aef..994d72c8c 100644 --- a/hal/transport/RFM95/driver/RFM95.h +++ b/hal/transport/RFM95/driver/RFM95.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -81,6 +81,10 @@ #include "RFM95registers.h" +#if !defined(RFM95_SPI) +#define RFM95_SPI hwSPI //!< default SPI +#endif + // default PIN assignments, can be overridden #if defined(ARDUINO_ARCH_AVR) #if defined(__AVR_ATmega32U4__) @@ -111,25 +115,6 @@ #define RFM95_SPI_DATA_ORDER MSBFIRST //!< SPI data order #define RFM95_SPI_DATA_MODE SPI_MODE0 //!< SPI mode -#if defined (ARDUINO) && !defined (__arm__) && !defined (RFM95_SPI) -#include -#if defined(MY_SOFTSPI) -SoftSPIRFM95_SPI; -#else -#define RFM95_SPI SPI //!< SPI -#endif -#else -#if defined(__arm__) || defined(__linux__) -#include -#else -extern HardwareSPI SPI; //!< SPI -#endif - -#if !defined(RFM95_SPI) -#define RFM95_SPI SPI //!< SPI -#endif -#endif - // RFM95 radio configurations: reg_1d, reg_1e, reg_26 (see datasheet) #define RFM95_BW125CR45SF128 RFM95_BW_125KHZ | RFM95_CODING_RATE_4_5, RFM95_SPREADING_FACTOR_128CPS | RFM95_RX_PAYLOAD_CRC_ON, RFM95_AGC_AUTO_ON //!< 0x72,0x74,0x04 #define RFM95_BW500CR45SF128 RFM95_BW_500KHZ | RFM95_CODING_RATE_4_5, RFM95_SPREADING_FACTOR_128CPS | RFM95_RX_PAYLOAD_CRC_ON, RFM95_AGC_AUTO_ON //!< 0x92,0x74,0x04 @@ -417,13 +402,11 @@ LOCAL void RFM95_sendACK(const uint8_t recipient, const rfm95_sequenceNumber_t s * @param recipient * @param buffer * @param bufferSize -* @param retries -* @param retryWaitTime +* @param noACK * @return True if packet successfully sent */ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, - const uint8_t bufferSize, const uint8_t retries = RFM95_RETRIES, - const uint32_t retryWaitTime = RFM95_RETRY_TIMEOUT_MS); + const uint8_t bufferSize, const bool noACK); /** * @brief Wait until no channel activity detected * @return True if no channel activity detected, False if timeout occured diff --git a/hal/transport/RFM95/driver/RFM95registers.h b/hal/transport/RFM95/driver/RFM95registers.h index dafd7e53c..46d0366ed 100644 --- a/hal/transport/RFM95/driver/RFM95registers.h +++ b/hal/transport/RFM95/driver/RFM95registers.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/transport/RS485/MyTransportRS485.cpp b/hal/transport/RS485/MyTransportRS485.cpp index a8feff48f..e0f3a8335 100644 --- a/hal/transport/RS485/MyTransportRS485.cpp +++ b/hal/transport/RS485/MyTransportRS485.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -57,9 +57,13 @@ #endif #if defined(MY_RS485_DE_PIN) +#if !defined(MY_RS485_DE_INVERSE) #define assertDE() hwDigitalWrite(MY_RS485_DE_PIN, HIGH); delayMicroseconds(5) #define deassertDE() hwDigitalWrite(MY_RS485_DE_PIN, LOW) - +#else +#define assertDE() hwDigitalWrite(MY_RS485_DE_PIN, LOW); delayMicroseconds(5) +#define deassertDE() hwDigitalWrite(MY_RS485_DE_PIN, HIG) +#endif #else #define assertDE() #define deassertDE() @@ -264,7 +268,11 @@ bool transportSend(const uint8_t to, const void* data, const uint8_t len, const } #if defined(MY_RS485_DE_PIN) +#if !defined(MY_RS485_DE_INVERSE) hwDigitalWrite(MY_RS485_DE_PIN, HIGH); +#else + hwDigitalWrite(MY_RS485_DE_PIN, LOW); +#endif delayMicroseconds(5); #endif @@ -310,7 +318,11 @@ bool transportSend(const uint8_t to, const void* data, const uint8_t len, const _dev.flush(); #endif #endif +#if !defined(MY_RS485_DE_INVERSE) hwDigitalWrite(MY_RS485_DE_PIN, LOW); +#else + hwDigitalWrite(MY_RS485_DE_PIN, HIGH); +#endif #endif return true; } @@ -324,7 +336,11 @@ bool transportInit(void) _serialReset(); #if defined(MY_RS485_DE_PIN) hwPinMode(MY_RS485_DE_PIN, OUTPUT); +#if !defined(MY_RS485_DE_INVERSE) hwDigitalWrite(MY_RS485_DE_PIN, LOW); +#else + hwDigitalWrite(MY_RS485_DE_PIN, HIGH); +#endif #endif return true; } @@ -340,7 +356,7 @@ uint8_t transportGetAddress(void) } -bool transportAvailable(void) +bool transportDataAvailable(void) { _serialProcess(); return _packet_received; diff --git a/keywords.txt b/keywords.txt index 3361eec01..f245dbb54 100644 --- a/keywords.txt +++ b/keywords.txt @@ -43,7 +43,6 @@ MY_RX_MESSAGE_BUFFER_SIZE LITERAL1 MY_RX_MESSAGE_BUFFER_FEATURE LITERAL1 MY_SERIAL_OUTPUT_SIZE LITERAL1 MY_SLEEP_NOT_POSSIBLE LITERAL1 -MY_SMART_SLEEP_WAIT_DURATION LITERAL1 MY_SPLASH_SCREEN_DISABLED LITERAL1 MY_WAKE_UP_BY_TIMER LITERAL1 @@ -87,7 +86,7 @@ MY_SPECIAL_DEBUG LITERAL1 # OTA MY_DEBUG_OTA LITERAL1 -MY_DEBUG_OTA_DISABLE_ACK LITERAL1 +MY_DEBUG_OTA_DISABLE_ECHO LITERAL1 MY_DEBUG_VERBOSE_OTA_UPDATE LITERAL1 MY_DEFAULT_ERR_LED_PIN LITERAL1 MY_DEFAULT_LED_BLINK_PERIOD LITERAL1 @@ -106,6 +105,8 @@ MY_OTA_LOG_SENDER_FEATURE LITERAL1 MY_OTA_USE_I2C_EEPROM LITERAL1 MY_SPIFLASH_SST25TYPE LITERAL1 MY_WITH_LEDS_BLINKING_INVERSE LITERAL1 +MY_OTA_RETRY LITERAL1 +MY_OTA_RETRY_DELAY LITERAL1 # Signing MY_DEBUG_VERBOSE_SIGNING LITERAL1 @@ -145,6 +146,9 @@ MY_SOFT_SPI_MISO_PIN LITERAL1 MY_SOFT_SPI_MOSI_PIN LITERAL1 MY_SOFT_SPI_SCK_PIN LITERAL1 +# TransportHAL +MY_DEBUG_VERBOSE_TRANSPORT_HAL LITERAL1 + # RF24 MY_DEBUG_VERBOSE_RF24 LITERAL1 MY_RADIO_RF24 LITERAL1 @@ -229,6 +233,7 @@ MY_RFM69_TX_POWER_DBM LITERAL1 MY_RS485 LITERAL1 MY_RS485_BAUD_RATE LITERAL1 MY_RS485_DE_PIN LITERAL1 +MY_RS485_DE_INVERSE LITERAL1 MY_RS485_HWSERIAL LITERAL1 MY_RS485_MAX_MESSAGE_LENGTH LITERAL1 MY_RS485_SOH_COUNT LITERAL1 @@ -323,8 +328,6 @@ MY_ESP8266_SERIAL_MODE LITERAL1 # MY_OTA_BOOTLOADER_MAJOR_VERSION # MY_OTA_BOOTLOADER_MINOR_VERSION # MY_OTA_BOOTLOADER_VERSION -# MY_OTA_RETRY -# MY_OTA_RETRY_DELAY # MY_SDCARD_CS # Blacklist - used by the Raspberry Pi gateway and not meant to be used by users @@ -348,8 +351,26 @@ MY_ESP8266_SERIAL_MODE LITERAL1 # MY_ESP8266_HOSTNAME LITERAL1 # MY_ESP8266_PASSWORD LITERAL1 # MY_ESP8266_SSID LITERAL1 +# MY_DEBUG_OTA_DISABLE_ACK # Blacklist - descriptional only # MY_XYZ_POWER_PIN # MY_MQTT_TOPIC_PREFIX +# Blacklist - listed in https://github.com/mysensors/MySensors/issues/1107 +# Since no-one can take responsibility for them, we blacklist them so +# we can get warnings when new items are added. +# MY_AVR_TEMPERATURE_GAIN +# MY_AVR_TEMPERATURE_OFFSET +# MY_CRYPTO_SHA256_ASM +# MY_ESP32_TEMPERATURE_GAIN +# MY_ESP32_TEMPERATURE_OFFSET +# MY_NRF5_RX_BUFFER_SIZE +# MY_SAMD_TEMPERATURE_GAIN +# MY_SAMD_TEMPERATURE_OFFSET +# MY_STM32F1_TEMPERATURE_GAIN +# MY_STM32F1_TEMPERATURE_OFFSET +# MY_TRANSPORT_ENCRYPTION +# MY_OTA_I2C_ADDR +# MY_RF69_DIO5 +# MY_SERIALDEVICE diff --git a/library.json b/library.json index 5fcbf9434..6c29ab35c 100644 --- a/library.json +++ b/library.json @@ -7,13 +7,19 @@ "type": "git", "url": "https://github.com/mysensors/MySensors.git" }, - "version": "2.3.1", + "version": "2.3.2", "frameworks": "arduino", "platforms": "*", "export": { "exclude": [ "tests", "Documentation", + ".mystools", + "projects", + "examples_linux", + "initscripts", + "configure", + ".ci", "Doxyfile" ] } diff --git a/library.properties b/library.properties index db44681c0..2354fe60b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=MySensors -version=2.3.1 +version=2.3.2 author=The MySensors Team maintainer=The MySensors Team sentence=Home Automation Framework diff --git a/tests/Arduino/sketches/hard_signing_no_whitelisting_full_debug/hard_signing_no_whitelisting_full_debug.ino b/tests/Arduino/sketches/hard_signing_no_whitelisting_full_debug/hard_signing_no_whitelisting_full_debug.ino index 1aff80cae..abdf8db3c 100644 --- a/tests/Arduino/sketches/hard_signing_no_whitelisting_full_debug/hard_signing_no_whitelisting_full_debug.ino +++ b/tests/Arduino/sketches/hard_signing_no_whitelisting_full_debug/hard_signing_no_whitelisting_full_debug.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RF24 diff --git a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug/hard_signing_whitelisting_full_debug.ino b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug/hard_signing_whitelisting_full_debug.ino index 55f20354e..378a3b19d 100644 --- a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug/hard_signing_whitelisting_full_debug.ino +++ b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug/hard_signing_whitelisting_full_debug.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RF24 diff --git a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_nodelock/hard_signing_whitelisting_full_debug_nodelock.ino b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_nodelock/hard_signing_whitelisting_full_debug_nodelock.ino index aa90b0484..f52f16605 100644 --- a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_nodelock/hard_signing_whitelisting_full_debug_nodelock.ino +++ b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_nodelock/hard_signing_whitelisting_full_debug_nodelock.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RF24 diff --git a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_nrf24_rsa/hard_signing_whitelisting_full_debug_nrf24_rsa.ino b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rf24_rsa/hard_signing_whitelisting_full_debug_rf24_rsa.ino similarity index 93% rename from tests/Arduino/sketches/hard_signing_whitelisting_full_debug_nrf24_rsa/hard_signing_whitelisting_full_debug_nrf24_rsa.ino rename to tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rf24_rsa/hard_signing_whitelisting_full_debug_rf24_rsa.ino index 8707807d4..04d41fa5a 100644 --- a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_nrf24_rsa/hard_signing_whitelisting_full_debug_nrf24_rsa.ino +++ b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rf24_rsa/hard_signing_whitelisting_full_debug_rf24_rsa.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,11 +18,10 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RF24 +#define MY_RF24_ENABLE_ENCRYPTION //#define MY_SIGNING_SOFT #define MY_SIGNING_ATSHA204 #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} @@ -33,6 +32,5 @@ #ifndef MY_SIGNING_ATSHA204_PIN #define MY_SIGNING_ATSHA204_PIN 17 #endif -#define MY_RF24_ENABLE_ENCRYPTION #include diff --git a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm69_rsa/hard_signing_whitelisting_full_debug_rfm69_rsa.ino b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm69_rsa/hard_signing_whitelisting_full_debug_rfm69_rsa.ino index 15177246d..7a4b72ec4 100644 --- a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm69_rsa/hard_signing_whitelisting_full_debug_rfm69_rsa.ino +++ b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm69_rsa/hard_signing_whitelisting_full_debug_rfm69_rsa.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,11 +18,10 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RFM69 +#define MY_RFM69_ENABLE_ENCRYPTION //#define MY_SIGNING_SOFT #define MY_SIGNING_ATSHA204 #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} @@ -33,6 +32,5 @@ #ifndef MY_SIGNING_ATSHA204_PIN #define MY_SIGNING_ATSHA204_PIN 17 #endif -#define MY_RFM69_ENABLE_ENCRYPTION #include diff --git a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm95_rsa/hard_signing_whitelisting_full_debug_rfm95_rsa.ino b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm95_rsa/hard_signing_whitelisting_full_debug_rfm95_rsa.ino index fe4f925b9..76f173e06 100644 --- a/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm95_rsa/hard_signing_whitelisting_full_debug_rfm95_rsa.ino +++ b/tests/Arduino/sketches/hard_signing_whitelisting_full_debug_rfm95_rsa/hard_signing_whitelisting_full_debug_rfm95_rsa.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,11 +18,10 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RFM95 +#define MY_RFM95_ENABLE_ENCRYPTION //#define MY_SIGNING_SOFT #define MY_SIGNING_ATSHA204 #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} @@ -33,6 +32,5 @@ #ifndef MY_SIGNING_ATSHA204_PIN #define MY_SIGNING_ATSHA204_PIN 17 #endif -#define MY_RFM95_ENABLE_ENCRYPTION #include diff --git a/tests/Arduino/sketches/new_rfm69_driver/new_rfm69_driver.ino b/tests/Arduino/sketches/new_rfm69_driver/new_rfm69_driver.ino index 833cdeb89..1676cb351 100644 --- a/tests/Arduino/sketches/new_rfm69_driver/new_rfm69_driver.ino +++ b/tests/Arduino/sketches/new_rfm69_driver/new_rfm69_driver.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,7 +18,9 @@ * ******************************* */ +#define MY_DEBUG #define MY_RADIO_RFM69 #define MY_RFM69_NEW_DRIVER +#define MY_RFM69_ENABLE_ENCRYPTION #define MY_DEBUG_VERBOSE_RFM69 #include \ No newline at end of file diff --git a/tests/Arduino/sketches/ota_firmware_update_nrf24/ota_firmware_update_nrf24.ino b/tests/Arduino/sketches/ota_firmware_update_rf24/ota_firmware_update_rf24.ino similarity index 91% rename from tests/Arduino/sketches/ota_firmware_update_nrf24/ota_firmware_update_nrf24.ino rename to tests/Arduino/sketches/ota_firmware_update_rf24/ota_firmware_update_rf24.ino index 8a0a76344..c2c1b2577 100644 --- a/tests/Arduino/sketches/ota_firmware_update_nrf24/ota_firmware_update_nrf24.ino +++ b/tests/Arduino/sketches/ota_firmware_update_rf24/ota_firmware_update_rf24.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_RADIO_RF24 #define MY_OTA_FIRMWARE_FEATURE diff --git a/tests/Arduino/sketches/serial_gw_no_transport_hard_signing_whitelisting_full_debug/serial_gw_no_transport_hard_signing_whitelisting_full_debug.ino b/tests/Arduino/sketches/serial_gw_no_transport_hard_signing_whitelisting_full_debug/serial_gw_no_transport_hard_signing_whitelisting_full_debug.ino index ff41ab9ec..1d9b8fa1b 100644 --- a/tests/Arduino/sketches/serial_gw_no_transport_hard_signing_whitelisting_full_debug/serial_gw_no_transport_hard_signing_whitelisting_full_debug.ino +++ b/tests/Arduino/sketches/serial_gw_no_transport_hard_signing_whitelisting_full_debug/serial_gw_no_transport_hard_signing_whitelisting_full_debug.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_GATEWAY_SERIAL #define MY_DEBUG_VERBOSE_SIGNING diff --git a/tests/Arduino/sketches/serial_gw_no_transport_soft_signing_whitelisting_full_debug/serial_gw_no_transport_soft_signing_whitelisting_full_debug.ino b/tests/Arduino/sketches/serial_gw_no_transport_soft_signing_whitelisting_full_debug/serial_gw_no_transport_soft_signing_whitelisting_full_debug.ino index 9d5ea3763..1fd9037c7 100644 --- a/tests/Arduino/sketches/serial_gw_no_transport_soft_signing_whitelisting_full_debug/serial_gw_no_transport_soft_signing_whitelisting_full_debug.ino +++ b/tests/Arduino/sketches/serial_gw_no_transport_soft_signing_whitelisting_full_debug/serial_gw_no_transport_soft_signing_whitelisting_full_debug.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_GATEWAY_SERIAL #define MY_DEBUG_VERBOSE_SIGNING diff --git a/tests/Arduino/sketches/soft_signing_no_whitelisting_full_debug/soft_signing_no_whitelisting_full_debug.ino b/tests/Arduino/sketches/soft_signing_no_whitelisting_full_debug/soft_signing_no_whitelisting_full_debug.ino index 34faae7c8..98d9580c7 100644 --- a/tests/Arduino/sketches/soft_signing_no_whitelisting_full_debug/soft_signing_no_whitelisting_full_debug.ino +++ b/tests/Arduino/sketches/soft_signing_no_whitelisting_full_debug/soft_signing_no_whitelisting_full_debug.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RF24 diff --git a/tests/Arduino/sketches/soft_signing_whitelisting_full_debug/soft_signing_whitelisting_full_debug.ino b/tests/Arduino/sketches/soft_signing_whitelisting_full_debug/soft_signing_whitelisting_full_debug.ino index d9e8749a2..06ceb062b 100644 --- a/tests/Arduino/sketches/soft_signing_whitelisting_full_debug/soft_signing_whitelisting_full_debug.ino +++ b/tests/Arduino/sketches/soft_signing_whitelisting_full_debug/soft_signing_whitelisting_full_debug.ino @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2018 Sensnology AB + * Copyright (C) 2013-2019 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -18,8 +18,6 @@ * ******************************* */ -#include -#include #define MY_DEBUG #define MY_DEBUG_VERBOSE_SIGNING #define MY_RADIO_RF24