From de083c964d9aeeb9fda7485c39fed27443cd617c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 18 Mar 2011 20:32:34 -0700 Subject: [PATCH 001/326] added --- Doxyfile | 1551 ++++++++++++++++++++++++++++++++ README | 12 + RF24.cpp | 419 +++++++++ RF24.h | 296 ++++++ examples/pingpair/pingpair.pde | 216 +++++ examples/pingpair/printf.h | 31 + keywords.txt | 13 + nRF24L01.h | 100 ++ 8 files changed, 2638 insertions(+) create mode 100644 Doxyfile create mode 100644 README create mode 100644 RF24.cpp create mode 100644 RF24.h create mode 100644 examples/pingpair/pingpair.pde create mode 100644 examples/pingpair/printf.h create mode 100644 keywords.txt create mode 100644 nRF24L01.h diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..a8a3e16 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1551 @@ +# Doxyfile 1.6.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = RF24 + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = v1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then 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. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +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. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. 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 that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip 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. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +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 only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +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 (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +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 (the default) these declarations will be included in the +# documentation. + +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 (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = 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. + +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. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +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. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED 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. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be 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. + +INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. 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. + +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 pattern (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 *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +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. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# 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. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = 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. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# 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, 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. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, 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). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +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 +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +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. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# 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. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = 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. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# 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 very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = 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. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +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. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# 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 them the man command +# would be unable to find the correct page. The default is NO. + +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 captures the structure of +# the code including all documentation. + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# 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. + +XML_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 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. + +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 file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# 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. + +PERLMOD_LATEX = NO + +# 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 size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +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 (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all 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. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that 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. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +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 listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# 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. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need 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 setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# 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. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. 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. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. 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. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# 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 by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# 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 root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES 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. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..818e45e --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +Aruino driver for nRF24L01 2.4GHz Wireless Transceiver + +See Datasheet at http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf + +This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or +the SPI hardware will go into 'slave' mode. + +Design Goals: This library is designed to be... +* Maximally compliant with the intended operation of the chip +* Easy for beginners to use +* Consumed with a public interface that's similiar to other Arduino standard libraries +* Built against the standard SPI library. \ No newline at end of file diff --git a/RF24.cpp b/RF24.cpp new file mode 100644 index 0000000..e20200f --- /dev/null +++ b/RF24.cpp @@ -0,0 +1,419 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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 +#include +#include "RF24.h" +#include "nRF24L01.h" + +#undef SERIAL_DEBUG +#ifdef SERIAL_DEBUG +#define IF_SERIAL_DEBUG(x) (x) +#else +#define IF_SERIAL_DEBUG(x) +#endif + +/******************************************************************/ + +void RF24::csn(int mode) +{ + digitalWrite(csn_pin,mode); +} + +/******************************************************************/ + +void RF24::ce(int mode) +{ + digitalWrite(ce_pin,mode); +} + +/******************************************************************/ + +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +{ + uint8_t status; + + csn(LOW); + status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + *buf++ = SPI.transfer(0xff); + + csn(HIGH); + + return status; +} + +/******************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + uint8_t status; + + csn(LOW); + status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + SPI.transfer(*buf++); + + csn(HIGH); + + return status; +} + +/******************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, uint8_t value) +{ + uint8_t status; + + csn(LOW); + status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + SPI.transfer(value); + csn(HIGH); + + return status; +} + +/******************************************************************/ + +uint8_t RF24::write_payload(const void* buf) +{ + uint8_t status; + + const uint8_t* current = (const uint8_t*)buf; + + csn(LOW); + status = SPI.transfer( W_TX_PAYLOAD ); + uint8_t len = payload_size; + while ( len-- ) + SPI.transfer(*current++); + + csn(HIGH); + + return status; +} + +/******************************************************************/ + +uint8_t RF24::read_payload(void* buf) +{ + uint8_t status; + uint8_t* current = (uint8_t*)buf; + + csn(LOW); + status = SPI.transfer( R_RX_PAYLOAD ); + uint8_t len = payload_size; + while ( len-- ) + *current++ = SPI.transfer(0xff); + csn(HIGH); + + return status; +} + +/******************************************************************/ + +uint8_t RF24::flush_rx(void) +{ + uint8_t status; + + csn(LOW); + status = SPI.transfer( FLUSH_RX ); + csn(HIGH); + + return status; +} + +/******************************************************************/ + +uint8_t RF24::flush_tx(void) +{ + uint8_t status; + + csn(LOW); + status = SPI.transfer( FLUSH_TX ); + csn(HIGH); + + return status; +} + +/******************************************************************/ + +uint8_t RF24::get_status(void) +{ + uint8_t status; + + csn(LOW); + status = SPI.transfer( NOP ); + csn(HIGH); + + return status; +} + +/******************************************************************/ + +void RF24::print_status(uint8_t status) +{ + printf("STATUS=%02x: RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r", + status, + (status & _BV(RX_DR))?1:0, + (status & _BV(TX_DS))?1:0, + (status & _BV(MAX_RT))?1:0, + ((status >> RX_P_NO) & B111), + (status & _BV(TX_FULL))?1:0 + ); +} + +/******************************************************************/ + +void RF24::print_observe_tx(uint8_t value) +{ + printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r", + value, + (value >> PLOS_CNT) & B1111, + (value >> ARC_CNT) & B1111 + ); +} + +/******************************************************************/ + +RF24::RF24(int _cepin, int _cspin): + ce_pin(_cepin), csn_pin(_cspin) +{ +} + +/******************************************************************/ + +void RF24::setChannel(int channel) +{ + write_register(RF_CH,min(channel,127)); +} + +/******************************************************************/ + +void RF24::setPayloadSize(uint8_t size) +{ + payload_size = min(size,32); + write_register(RX_PW_P0,min(size,32)); +} + +/******************************************************************/ + +uint8_t RF24::getPayloadSize(void) +{ + return payload_size; +} + +/******************************************************************/ + +void RF24::print_details(void) +{ + uint8_t buffer[5]; + uint8_t status = read_register(RX_ADDR_P0,buffer,5); + print_status(status); + printf("RX_ADDR_P0 = 0x",buffer); + uint8_t *bufptr = buffer + 5; + while( bufptr-- > buffer ) + printf("%02x",*bufptr); + printf("\n\r"); + + status = read_register(RX_ADDR_P1,buffer,5); + printf("RX_ADDR_P1 = 0x",buffer); + bufptr = buffer + 5; + while( bufptr-- > buffer ) + printf("%02x",*bufptr); + printf("\n\r"); + + status = read_register(TX_ADDR,buffer,5); + printf("TX_ADDR = 0x",buffer); + bufptr = buffer + 5; + while( bufptr-- > buffer ) + printf("%02x",*bufptr); + printf("\n\r"); + + read_register(EN_AA,buffer,1); + printf("EN_AA = %02x\n\r",*buffer); + + read_register(EN_RXADDR,buffer,1); + printf("EN_RXADDR = %02x\n\r",*buffer); + + read_register(RF_CH,buffer,1); + printf("RF_CH = %02x\n\r",*buffer); +} + +/******************************************************************/ + +void RF24::begin(void) +{ + pinMode(ce_pin,OUTPUT); + pinMode(csn_pin,OUTPUT); + + ce(LOW); + csn(HIGH); + + SPI.begin(); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV8); + + // Set generous timeouts, to make testing a little easier + write_register(SETUP_RETR,(B1111 << ARD) | (B1111 << ARC)); + + // Reset current status + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Flush buffers + flush_rx(); + flush_tx(); + + // Set up default configuration. Callers can always change it later. + setChannel(1); + setPayloadSize(8); +} + +/******************************************************************/ + +void RF24::startListening(void) +{ + write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Flush buffers + flush_rx(); + + // Go! + ce(HIGH); + + // wait for the radio to come up (130us actually only needed) + delay(1); +} + +/******************************************************************/ + +void RF24::stopListening(void) +{ + ce(LOW); +} + +/******************************************************************/ + +boolean RF24::write( const void* buf ) +{ + boolean result = false; + + // Transmitter power-up + write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP)); + + // Send the payload + write_payload( buf ); + + // Allons! + ce(HIGH); + + // IN the end, the send should be blocking. It comes back in 60ms worst case, or much faster + // if I tighted up the retry logic. (Default settings will be 750us. + // Monitor the send + uint8_t observe_tx; + uint8_t status; + do + { + status = read_register(OBSERVE_TX,&observe_tx,1); + IF_SERIAL_DEBUG(Serial.print(status,HEX)); + IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); + } + while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) ); + + if ( status & _BV(TX_DS) ) + result = true; + + IF_SERIAL_DEBUG(Serial.println(result?"...OK.":"...Failed")); + + // Yay, we are done. + ce(LOW); + + // Power down + write_register(CONFIG, _BV(EN_CRC) ); + + // Reset current status + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Flush buffers + flush_tx(); + + return result; +} + +/******************************************************************/ + +boolean RF24::available(void) +{ + uint8_t status = get_status(); + boolean result = ( status & _BV(RX_DR) ); + + if (result) + { + IF_SERIAL_DEBUG(print_status(status)); + + // Clear the status bit + write_register(STATUS,_BV(RX_DR) ); + } + + return result; +} + +/******************************************************************/ + +boolean RF24::read( void* buf ) +{ + // was this the last of the data available? + boolean result = false; + + // Fetch the payload + read_payload( buf ); + + uint8_t fifo_status; + read_register(FIFO_STATUS,&fifo_status,1); + if ( fifo_status & _BV(RX_EMPTY) ) + result = true; + + return result; +} + +/******************************************************************/ + +void RF24::openWritingPipe(uint64_t value) +{ + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01 + // expects it LSB first too, so we're good. + + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); + write_register(TX_ADDR, reinterpret_cast(&value), 5); +} + +/******************************************************************/ + +void RF24::openReadingPipe(uint8_t child, uint64_t value) +{ + const uint8_t child_pipe[] = { + RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 }; + const uint8_t child_payload_size[] = { + RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 }; + const uint8_t child_pipe_enable[] = { + ENAA_P1, ENAA_P2, ENAA_P3, ENAA_P4, ENAA_P5 }; + + if (--child < 5) + { + write_register(child_pipe[child], reinterpret_cast(&value), 5); + write_register(child_payload_size[child],payload_size); + + // Note this is kind of an inefficient way to set up these enable bits, bit I thought it made + // the calling code more simple + uint8_t en_aa; + read_register(EN_AA,&en_aa,1); + en_aa |= _BV(child_pipe_enable[child]); + write_register(EN_AA,en_aa); + } +} diff --git a/RF24.h b/RF24.h new file mode 100644 index 0000000..1940f56 --- /dev/null +++ b/RF24.h @@ -0,0 +1,296 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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 __RF24_H__ +#define __RF24_H__ + +#include + +/** + * Driver for nRF24L01 2.4GHz Wireless Transceiver + * + * See Datasheet + * + * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or + * the SPI hardware will go into 'slave' mode. + * + * Design Goals: This library is designed to be... + * * Maximally compliant with the intended operation of the chip + * * Easy for beginners to use + * * Consumed with a public interface that's similiar to other Arduino standard libraries + * * Built against the standard SPI library. + */ + +class RF24 +{ +private: + int ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + int csn_pin; /**< SPI Chip select */ + int payload_size; /**< Fixed size of payloads */ + +protected: + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. + */ + /**@{*/ + + /** + * Set chip select pin + * + * @param mode HIGH to take this unit off the SPI bus, LOW to put it on + */ + void csn(int mode) ; + + /** + * Set chip enable + * + * @param mode HIGH to actively begin transmission or LOW to put in standby. Please see data sheet + * for a much more detailed description of this pin. + */ + void ce(int mode); + + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; + + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); + + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @return Current value of status register + */ + uint8_t write_payload(const void* buf); + + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @return Current value of status register + */ + uint8_t read_payload(void* buf) ; + + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); + + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); + + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void) ; + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status) ; + + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param Value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value) ; + + /**@}*/ + +public: + /** + * Constructor + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(int _cepin, int _cspin); + + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + */ + void begin(void); + + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(int channel); + + /** + * Set Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); + + /** + * Get Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void) ; + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_details(void) ; + + /** + * Start listening on the pipes opened for reading. + * + * Be sure to open some pipes for reading first. Do not call 'write' + * while in this mode, without first calling 'stopListening'. + */ + void startListening(void); + + /** + * Stop listening for incoming messages + * + * Necessary to do this before writing. + */ + void stopListening(void); + + /** + * Write to the open writing pipe + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Pointer to the data to be sent + * @return True if the payload was delivered successfully false if not + */ + boolean write( const void* buf ); + + /** + * Test whether there are bytes available to be read + * + * @return True if there is a payload available, false if none is + */ + boolean available(void) ; + + /** + * Read the payload + * + * Return the last payload received + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @todo Indicate which pipe it came from + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @param buf Pointer to a buffer where the data should be written + * @return True if the payload was delivered successfully false if not + */ + boolean read( void* buf ) ; + + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param value The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); + + /** + * Open a pipe for reading + * + * Up to 5 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning all 5 reading pipes should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * @code + * openReadingPipe(0xF0F0F0F0AA); + * openReadingPipe(0xF0F0F0F066); + * @endcode + * + * @todo Enforce the restriction that all pipes must share the top 32 bits + * + * @param number Which pipe# to open, 1-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); + +}; +#endif // __RF24_H__ + diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde new file mode 100644 index 0000000..3041e56 --- /dev/null +++ b/examples/pingpair/pingpair.pde @@ -0,0 +1,216 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example RF Radio Ping Pair + * + * This sketch is an example of using the RF24 library for Arduino. Deploy this on + * two nodes, set one as the 'trasmit' and the other the 'receive' unit. The transmit + * unit will send out the value of millis() once a second. The receive unit will respond + * back with a copy of the value. The transmit unit can get that 'ping' back, and + * determine how long the whole cycle took. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the address (and therefore the role of operation) of this unit. +// lo = node0, hi = node1 +const int addr_pin = 7; + +// The actual value of the node's address will be filled in by the sketch +// when it reads the addr_pin +int node_address; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up address & role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the addr_pin. Set it low for address #0, high for #1. +// + +// The various roles supported by this sketch +typedef enum { role_rx = 1, role_tx1, role_end } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Receive", "Transmit"}; + +// Which role is assumed by each of the possible hardware addresses +const role_e role_map[2] = { role_rx, role_tx1 }; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Address & Role + // + + // set up the address pin + pinMode(addr_pin, INPUT); + digitalWrite(addr_pin,HIGH); + delay(20); // Just to get a solid reading on the addr pin + + // read the address pin, establish our address and role + node_address = digitalRead(addr_pin) ? 0 : 1; + role = role_map[node_address]; + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24 pingpair example\n\r"); + printf("ADDRESS: %x\n\r",node_address); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // Set channel (optional) + radio.setChannel(1); + + // Set size of payload (optional, but recommended) + // The library uses a fixed-size payload, so if you don't set one, it will pick + // one for you! + radio.setPayloadSize(sizeof(unsigned long)); + + // + // Open pipes to other nodes for communication (required) + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + + // We will open 'our' pipe for writing + radio.openWritingPipe(pipes[node_address]); + + // We open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + int other_node_address; + if (node_address == 0) + other_node_address = 1; + else + other_node_address = 0; + radio.openReadingPipe(1,pipes[other_node_address]); + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.print_details(); +} + +void loop(void) +{ + // + // Transmitter role. Repeatedly send the current time + // + + if (role == role_tx1) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + bool ok = radio.write( &time ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Receiver role. Receive each packet, dump it out, and send it back to the transmitter + // + + if ( role == role_rx ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time ); + + // Spew it + printf("Got payload %lu...",got_time); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} diff --git a/examples/pingpair/printf.h b/examples/pingpair/printf.h new file mode 100644 index 0000000..52e0c89 --- /dev/null +++ b/examples/pingpair/printf.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..d0bd557 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,13 @@ + RF24 KEYWORD1 + begin KEYWORD2 + setChannel KEYWORD2 + setPayloadSize KEYWORD2 + getPayloadSize KEYWORD2 + print_details KEYWORD2 + startListening KEYWORD2 + stopListening KEYWORD2 + write KEYWORD2 + available KEYWORD2 + read KEYWORD2 + openWritingPipe KEYWORD2 + openReadingPipe KEYWORD2 \ No newline at end of file diff --git a/nRF24L01.h b/nRF24L01.h new file mode 100644 index 0000000..10aedb3 --- /dev/null +++ b/nRF24L01.h @@ -0,0 +1,100 @@ +/* + Copyright (c) 2007 Stefan Engelke + + 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. +*/ + +/* Memory Map */ +#define CONFIG 0x00 +#define EN_AA 0x01 +#define EN_RXADDR 0x02 +#define SETUP_AW 0x03 +#define SETUP_RETR 0x04 +#define RF_CH 0x05 +#define RF_SETUP 0x06 +#define STATUS 0x07 +#define OBSERVE_TX 0x08 +#define CD 0x09 +#define RX_ADDR_P0 0x0A +#define RX_ADDR_P1 0x0B +#define RX_ADDR_P2 0x0C +#define RX_ADDR_P3 0x0D +#define RX_ADDR_P4 0x0E +#define RX_ADDR_P5 0x0F +#define TX_ADDR 0x10 +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define FIFO_STATUS 0x17 + +/* Bit Mnemonics */ +#define MASK_RX_DR 6 +#define MASK_TX_DS 5 +#define MASK_MAX_RT 4 +#define EN_CRC 3 +#define CRCO 2 +#define PWR_UP 1 +#define PRIM_RX 0 +#define ENAA_P5 5 +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW 0 +#define ARD 4 +#define ARC 0 +#define PLL_LOCK 4 +#define RF_DR 3 +#define RF_PWR 1 +#define LNA_HCURR 0 +#define RX_DR 6 +#define TX_DS 5 +#define MAX_RT 4 +#define RX_P_NO 1 +#define TX_FULL 0 +#define PLOS_CNT 4 +#define ARC_CNT 0 +#define TX_REUSE 6 +#define FIFO_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 + +/* Instruction Mnemonics */ +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define NOP 0xFF From ea69f9d1a6e085ce4df7ff125ca3af30ea94e2b3 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 18 Mar 2011 20:34:11 -0700 Subject: [PATCH 002/326] type --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 818e45e..f887070 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Aruino driver for nRF24L01 2.4GHz Wireless Transceiver +Arduino driver for nRF24L01 2.4GHz Wireless Transceiver See Datasheet at http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf @@ -9,4 +9,4 @@ Design Goals: This library is designed to be... * Maximally compliant with the intended operation of the chip * Easy for beginners to use * Consumed with a public interface that's similiar to other Arduino standard libraries -* Built against the standard SPI library. \ No newline at end of file +* Built against the standard SPI library. From 0503017d1a03dd5163d32db2b399e33fdfdf4caa Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 18 Mar 2011 23:52:20 -0700 Subject: [PATCH 003/326] Updated documentation --- Doxyfile | 6 +++--- RF24.h | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Doxyfile b/Doxyfile index a8a3e16..1bb2af3 100644 --- a/Doxyfile +++ b/Doxyfile @@ -486,7 +486,7 @@ SHOW_DIRECTORIES = NO # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. -SHOW_FILES = YES +SHOW_FILES = NO # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. @@ -721,7 +721,7 @@ REFERENCES_RELATION = NO # link to the source code. # Otherwise they will link to the documentation. -REFERENCES_LINK_SOURCE = YES +REFERENCES_LINK_SOURCE = NO # 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 @@ -735,7 +735,7 @@ USE_HTAGS = NO # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. -VERBATIM_HEADERS = YES +VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index diff --git a/RF24.h b/RF24.h index 1940f56..308232c 100644 --- a/RF24.h +++ b/RF24.h @@ -138,7 +138,7 @@ protected: /** * Decode and print the given 'observe_tx' value to stdout * - * @param Value The observe_tx value to print + * @param value The observe_tx value to print * * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h */ @@ -149,6 +149,9 @@ protected: public: /** * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. * * @param _cepin The pin attached to Chip Enable on the RF module * @param _cspin The pin attached to Chip Select @@ -258,11 +261,12 @@ public: * Remember to stopListening() first. * * Addresses are 40-bit hex values, e.g.: + * * @code * openWritingPipe(0xF0F0F0F0F0); * @endcode * - * @param value The 40-bit address of the pipe to open. This can be + * @param address The 40-bit address of the pipe to open. This can be * any value whatsoever, as long as you are the only one writing to it * and only one other radio is listening to it. Coordinate these pipe * addresses amongst nodes on the network. @@ -279,6 +283,7 @@ public: * * @warning all 5 reading pipes should share the first 32 bits. * Only the least significant byte should be unique, e.g. + * * @code * openReadingPipe(0xF0F0F0F0AA); * openReadingPipe(0xF0F0F0F066); From 0b89d37f19176f14fbbb8e35bced7c54b5452729 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 19 Mar 2011 13:14:07 -0700 Subject: [PATCH 004/326] Added a small documentation build system --- Jamfile | 42 ++++++++++++++++++++++++++++++++++++++++ wikidoc.xslt | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 Jamfile create mode 100644 wikidoc.xslt diff --git a/Jamfile b/Jamfile new file mode 100644 index 0000000..c17bfbd --- /dev/null +++ b/Jamfile @@ -0,0 +1,42 @@ +# +# Jamfile for generating the documentation +# + +DOXYGEN = doxygen ; +XSLT = xsltproc ; + +DOCSOURCES = Doxyfile RF24.h ; + +rule Doxygen +{ + Depends $(<) : $(>) ; + Clean clean : $(<) ; + Depends docs : $(<) ; +} + +actions Doxygen +{ + $(DOXYGEN) +} + +rule Xslt +{ + Depends $(<) : $(>) ; + Clean clean : $(<) ; +} + +actions Xslt +{ + $(XSLT) $(STYLESHEET) $(>) > $(<) +} + +Doxygen docs/xml/index.xml : $(DOCSOURCES) ; + +Xslt docs/xml/combined.xml : docs/xml/index.xml ; +STYLESHEET on docs/xml/combined.xml = docs/xml/combine.xslt ; +Depends combined : docs/xml/combined.xml ; + +Xslt docs/xml/wikidocs.txt : docs/xml/combined.xml ; +STYLESHEET on docs/xml/wikidocs.txt = wikidoc.xslt ; +Depends docs/xml/combined.xml : wikidoc.xslt ; +Depends wiki : docs/xml/wikidocs.txt ; diff --git a/wikidoc.xslt b/wikidoc.xslt new file mode 100644 index 0000000..1376b86 --- /dev/null +++ b/wikidoc.xslt @@ -0,0 +1,54 @@ + + + + + + + + === === + + + + + '''' + +Parameters: + + + * '''': + + + + + +Returns: +* + + Warning: + + + + <pre> </pre> + + + + + + + + From 5ba21261bbf2a096aafb4a95c0215a34ac8b5dfc Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 19 Mar 2011 13:15:24 -0700 Subject: [PATCH 005/326] Added --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35123f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.*.swp +docs/ From 0dc43ab8722c74c8efd9a960014219805407ebcf Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 19 Mar 2011 13:19:17 -0700 Subject: [PATCH 006/326] Changed name of print_details to comply with the overall naming convention --- RF24.cpp | 2 +- RF24.h | 2 +- examples/pingpair/pingpair.pde | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index e20200f..5d8cb60 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -209,7 +209,7 @@ uint8_t RF24::getPayloadSize(void) /******************************************************************/ -void RF24::print_details(void) +void RF24::printDetails(void) { uint8_t buffer[5]; uint8_t status = read_register(RX_ADDR_P0,buffer,5); diff --git a/RF24.h b/RF24.h index 308232c..8ae96f5 100644 --- a/RF24.h +++ b/RF24.h @@ -198,7 +198,7 @@ public: * * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h */ - void print_details(void) ; + void printDetails(void) ; /** * Start listening on the pipes opened for reading. diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 3041e56..46ff5b2 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -133,7 +133,7 @@ void setup(void) // Dump the configuration of the rf unit for debugging // - radio.print_details(); + radio.printDetails(); } void loop(void) From e97e0239d7861c27b08bb7541a1966b9c89a2120 Mon Sep 17 00:00:00 2001 From: james Date: Thu, 31 Mar 2011 21:32:45 -0700 Subject: [PATCH 007/326] Simplified example to use less calls. Defaults now to 32-byte payloads, but you can call in with any size under that. Added example to docs. --- .gitignore | 1 + Doxyfile | 6 +- RF24.cpp | 30 ++-- RF24.h | 47 ++++-- examples/pingpair/.gitignore | 1 + examples/pingpair/makefile | 277 +++++++++++++++++++++++++++++++++ examples/pingpair/pingpair.pde | 91 +++++------ 7 files changed, 376 insertions(+), 77 deletions(-) create mode 100644 examples/pingpair/.gitignore create mode 100644 examples/pingpair/makefile diff --git a/.gitignore b/.gitignore index 35123f9..f49947e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +*.o .*.swp docs/ diff --git a/Doxyfile b/Doxyfile index 1bb2af3..ee5ba85 100644 --- a/Doxyfile +++ b/Doxyfile @@ -631,7 +631,7 @@ EXCLUDE_SYMBOLS = # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = +EXAMPLE_PATH = examples # 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 @@ -645,7 +645,7 @@ EXAMPLE_PATTERNS = # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. -EXAMPLE_RECURSIVE = NO +EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see @@ -1548,4 +1548,4 @@ GENERATE_LEGEND = YES # remove the intermediate dot files that are used to generate # the various graphs. -DOT_CLEANUP = YES \ No newline at end of file +DOT_CLEANUP = YES diff --git a/RF24.cpp b/RF24.cpp index 5d8cb60..2533df1 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -80,7 +80,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) /******************************************************************/ -uint8_t RF24::write_payload(const void* buf) +uint8_t RF24::write_payload(const void* buf, uint8_t len) { uint8_t status; @@ -88,9 +88,12 @@ uint8_t RF24::write_payload(const void* buf) csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); - uint8_t len = payload_size; - while ( len-- ) + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = payload_size - data_len; + while ( data_len-- ) SPI.transfer(*current++); + while ( blank_len-- ) + SPI.transfer(0); csn(HIGH); @@ -99,16 +102,19 @@ uint8_t RF24::write_payload(const void* buf) /******************************************************************/ -uint8_t RF24::read_payload(void* buf) +uint8_t RF24::read_payload(void* buf, uint8_t len) { uint8_t status; uint8_t* current = (uint8_t*)buf; csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); - uint8_t len = payload_size; - while ( len-- ) + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = payload_size - data_len; + while ( data_len-- ) *current++ = SPI.transfer(0xff); + while ( blank_len-- ) + SPI.transfer(0xff); csn(HIGH); return status; @@ -181,7 +187,7 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ RF24::RF24(int _cepin, int _cspin): - ce_pin(_cepin), csn_pin(_cspin) + ce_pin(_cepin), csn_pin(_cspin), payload_size(32) { } @@ -197,7 +203,6 @@ void RF24::setChannel(int channel) void RF24::setPayloadSize(uint8_t size) { payload_size = min(size,32); - write_register(RX_PW_P0,min(size,32)); } /******************************************************************/ @@ -300,7 +305,7 @@ void RF24::stopListening(void) /******************************************************************/ -boolean RF24::write( const void* buf ) +boolean RF24::write( const void* buf, uint8_t len ) { boolean result = false; @@ -308,7 +313,7 @@ boolean RF24::write( const void* buf ) write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP)); // Send the payload - write_payload( buf ); + write_payload( buf, len ); // Allons! ce(HIGH); @@ -366,13 +371,13 @@ boolean RF24::available(void) /******************************************************************/ -boolean RF24::read( void* buf ) +boolean RF24::read( void* buf, uint8_t len ) { // was this the last of the data available? boolean result = false; // Fetch the payload - read_payload( buf ); + read_payload( buf, len ); uint8_t fifo_status; read_register(FIFO_STATUS,&fifo_status,1); @@ -391,6 +396,7 @@ void RF24::openWritingPipe(uint64_t value) write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); write_register(TX_ADDR, reinterpret_cast(&value), 5); + write_register(RX_PW_P0,min(payload_size,32)); } /******************************************************************/ diff --git a/RF24.h b/RF24.h index 8ae96f5..8916f0b 100644 --- a/RF24.h +++ b/RF24.h @@ -14,16 +14,20 @@ /** * Driver for nRF24L01 2.4GHz Wireless Transceiver * - * See Datasheet + * Please refer to: + * + * @li Detailed Documentation + * @li Source Code + * @li Chip Datasheet * * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or * the SPI hardware will go into 'slave' mode. * * Design Goals: This library is designed to be... - * * Maximally compliant with the intended operation of the chip - * * Easy for beginners to use - * * Consumed with a public interface that's similiar to other Arduino standard libraries - * * Built against the standard SPI library. + * @li Maximally compliant with the intended operation of the chip + * @li Easy for beginners to use + * @li Consumed with a public interface that's similiar to other Arduino standard libraries + * @li Built against the standard SPI library. */ class RF24 @@ -91,9 +95,10 @@ protected: * The size of data written is the fixed payload size, see getPayloadSize() * * @param buf Where to get the data + * @param len Number of bytes to be sent * @return Current value of status register */ - uint8_t write_payload(const void* buf); + uint8_t write_payload(const void* buf, uint8_t len); /** * Read the receive payload @@ -101,9 +106,10 @@ protected: * The size of data read is the fixed payload size, see getPayloadSize() * * @param buf Where to put the data + * @param len Maximum number of bytes to read * @return Current value of status register */ - uint8_t read_payload(void* buf) ; + uint8_t read_payload(void* buf, uint8_t len) ; /** * Empty the receive buffer @@ -222,12 +228,15 @@ public: * the receiver or the timeout/retransmit maxima are reached. In * the current configuration, the max delay here is 60ms. * - * The size of data written is the fixed payload size, see getPayloadSize() + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. * * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not */ - boolean write( const void* buf ); + boolean write( const void* buf, uint8_t len ); /** * Test whether there are bytes available to be read @@ -249,9 +258,10 @@ public: * for beginners to use. No casting needed. * * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer * @return True if the payload was delivered successfully false if not */ - boolean read( void* buf ) ; + boolean read( void* buf, uint8_t len ) ; /** * Open a pipe for writing @@ -296,6 +306,21 @@ public: */ void openReadingPipe(uint8_t number, uint64_t address); -}; +}; + +/** + * @example pingpair.pde + * + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. + */ + +/** + * @mainpage Driver Library for nRF24L01 + * + * See the RF24 class for details on how to drive this chip. + */ + #endif // __RF24_H__ diff --git a/examples/pingpair/.gitignore b/examples/pingpair/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/pingpair/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/pingpair/makefile b/examples/pingpair/makefile new file mode 100644 index 0000000..52db900 --- /dev/null +++ b/examples/pingpair/makefile @@ -0,0 +1,277 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +ARDUINO_VERSION = 22 +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +#CWARN = -Wall # show all warnings +CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 46ff5b2..dc70270 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -29,13 +29,9 @@ RF24 radio(8,9); -// sets the address (and therefore the role of operation) of this unit. -// lo = node0, hi = node1 -const int addr_pin = 7; - -// The actual value of the node's address will be filled in by the sketch -// when it reads the addr_pin -int node_address; +// sets the role of this unit in hardware. Connect to GND to be the 'ping' sender. +// Connect to +5V to be the 'pong' receiver. +const int role_pin = 7; // // Topology @@ -47,21 +43,18 @@ const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; // // Role management // -// Set up address & role. This sketch uses the same software for all the nodes +// Set up role. This sketch uses the same software for all the nodes // in this system. Doing so greatly simplifies testing. The hardware itself specifies // which node it is. // -// This is done through the addr_pin. Set it low for address #0, high for #1. +// This is done through the role_pin // // The various roles supported by this sketch -typedef enum { role_rx = 1, role_tx1, role_end } role_e; +typedef enum { role_ping_out = 1, role_pong_back } role_e; // The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Receive", "Transmit"}; - -// Which role is assumed by each of the possible hardware addresses -const role_e role_map[2] = { role_rx, role_tx1 }; +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The role of the current running sketch role_e role; @@ -69,17 +62,19 @@ role_e role; void setup(void) { // - // Address & Role + // Role // - // set up the address pin - pinMode(addr_pin, INPUT); - digitalWrite(addr_pin,HIGH); - delay(20); // Just to get a solid reading on the addr pin + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin - // read the address pin, establish our address and role - node_address = digitalRead(addr_pin) ? 0 : 1; - role = role_map[node_address]; + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_pong_back; + else + role = role_ping_out; // // Print preamble @@ -87,8 +82,7 @@ void setup(void) Serial.begin(9600); printf_begin(); - printf("\n\rRF24 pingpair example\n\r"); - printf("ADDRESS: %x\n\r",node_address); + printf("\n\rRF24/examples/pingpair/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); // @@ -97,32 +91,26 @@ void setup(void) radio.begin(); - // Set channel (optional) - radio.setChannel(1); - - // Set size of payload (optional, but recommended) - // The library uses a fixed-size payload, so if you don't set one, it will pick - // one for you! - radio.setPayloadSize(sizeof(unsigned long)); - // - // Open pipes to other nodes for communication (required) + // Open pipes to other nodes for communication // // This simple sketch opens two pipes for these two nodes to communicate // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - // We will open 'our' pipe for writing - radio.openWritingPipe(pipes[node_address]); - - // We open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - int other_node_address; - if (node_address == 0) - other_node_address = 1; + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else - other_node_address = 0; - radio.openReadingPipe(1,pipes[other_node_address]); - + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + // // Start listening // @@ -139,10 +127,10 @@ void setup(void) void loop(void) { // - // Transmitter role. Repeatedly send the current time + // Ping out role. Repeatedly send the current time // - if (role == role_tx1) + if (role == role_ping_out) { // First, stop listening so we can talk. radio.stopListening(); @@ -150,7 +138,7 @@ void loop(void) // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); - bool ok = radio.write( &time ); + bool ok = radio.write( &time, sizeof(unsigned long) ); // Now, continue listening radio.startListening(); @@ -171,7 +159,7 @@ void loop(void) { // Grab the response, compare, and send to debugging spew unsigned long got_time; - radio.read( &got_time ); + radio.read( &got_time, sizeof(unsigned long) ); // Spew it printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); @@ -182,10 +170,10 @@ void loop(void) } // - // Receiver role. Receive each packet, dump it out, and send it back to the transmitter + // Pong back role. Receive each packet, dump it out, and send it back // - if ( role == role_rx ) + if ( role == role_pong_back ) { // if there is data ready if ( radio.available() ) @@ -196,7 +184,7 @@ void loop(void) while (!done) { // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time ); + done = radio.read( &got_time, sizeof(unsigned long) ); // Spew it printf("Got payload %lu...",got_time); @@ -206,7 +194,7 @@ void loop(void) radio.stopListening(); // Send the final one back. - radio.write( &got_time ); + radio.write( &got_time, sizeof(unsigned long) ); printf("Sent response.\n\r"); // Now, resume listening so we catch the next packets. @@ -214,3 +202,4 @@ void loop(void) } } } +// vim:ci sts=2 sw=2 ft=cpp From c5c4537cdc94a2d50b4fec5bd61aa15b738da04e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 31 Mar 2011 21:47:01 -0700 Subject: [PATCH 008/326] Tuned the text a bit --- wikidoc.xslt | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/wikidoc.xslt b/wikidoc.xslt index 1376b86..b94d3ef 100644 --- a/wikidoc.xslt +++ b/wikidoc.xslt @@ -5,7 +5,7 @@ - === === + === === @@ -13,6 +13,7 @@ '''' Parameters: + * '''': @@ -22,9 +23,11 @@ Parameters: Returns: + * - Warning: + +Warning: @@ -36,19 +39,3 @@ Returns: - From 37d8e8b17d4672d297f3ffe1d031008f862a2ef4 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 1 Apr 2011 22:53:36 -0700 Subject: [PATCH 009/326] Added new star-topology example --- examples/starping/.gitignore | 1 + examples/starping/makefile | 291 +++++++++++++++++++++++++++++++++ examples/starping/printf.h | 31 ++++ examples/starping/starping.pde | 280 +++++++++++++++++++++++++++++++ 4 files changed, 603 insertions(+) create mode 100644 examples/starping/.gitignore create mode 100644 examples/starping/makefile create mode 100644 examples/starping/printf.h create mode 100644 examples/starping/starping.pde diff --git a/examples/starping/.gitignore b/examples/starping/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/starping/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/starping/makefile b/examples/starping/makefile new file mode 100644 index 0000000..42aa4e0 --- /dev/null +++ b/examples/starping/makefile @@ -0,0 +1,291 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +#CWARN = -Wall # show all warnings +CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/starping/printf.h b/examples/starping/printf.h new file mode 100644 index 0000000..52e0c89 --- /dev/null +++ b/examples/starping/printf.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde new file mode 100644 index 0000000..34e2eb9 --- /dev/null +++ b/examples/starping/starping.pde @@ -0,0 +1,280 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example RF Radio Ping Star Group + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' and the others + * as 'ping transmit' units. The ping units unit will send out the value of millis() + * once a second. The pong unit will respond back with a copy of the value. + * The ping unit can get that response back, and + * determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is + * which. The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +#include +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +extern EEPROMClass EEPROM; + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'pong' receiver. +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the 6 nodes to communicate +const uint64_t talking_pipes[6] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; +const uint64_t listening_pipes[6] = { 0x3A3A3A3AE1LL, 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_invalid = 0, role_ping_out, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +// +// Address management +// + +// Where in EEPROM is the address stored? +const uint8_t address_at_eeprom_location = 0; + +// What is our address (SRAM cache of the address from EEPROM) +// Note that zero is an INVALID address +uint8_t node_address; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Address + // + + if ( role == role_pong_back ) + node_address = 1; + else + { + // Read the address from EEPROM + uint8_t reading = EEPROM.read(address_at_eeprom_location); + + // If it is in a valid range for node addresses, it is our + // address. + if ( reading >= 2 && reading <= 6 ) + node_address = reading; + + // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' + else + { + node_address = 0; + role = role_invalid; + } + } + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/starping/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("ADDRESS: %i\n\r",node_address); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // Open 'our' pipe for writing + // ping nodes open the parent's pipe for reading + // pong node opens all children's pipes for reading + + if ( role == role_pong_back ) + { + // Listen to all ping nodes' talking pipes + radio.openReadingPipe(1,talking_pipes[1]); + radio.openReadingPipe(2,talking_pipes[2]); + radio.openReadingPipe(3,talking_pipes[3]); + radio.openReadingPipe(4,talking_pipes[4]); + radio.openReadingPipe(5,talking_pipes[5]); + } + if ( role == role_ping_out ) + { + // Write on our talking pipe + radio.openWritingPipe(talking_pipes[node_address-1]); + // Listen on our listening pipe + radio.openReadingPipe(1,listening_pipes[node_address-1]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + uint8_t pipe_num; + if ( radio.available(&pipe_num) ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu from %i...",got_time,pipe_num); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Open the correct pipe for writing + radio.openWritingPipe(listening_pipes[pipe_num]); + + // Retain the low 2 bytes to identify the pipe for the spew + uint16_t pipe_id = listening_pipes[pipe_num] & 0xffff; + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response to %04x.\n\r",pipe_id); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } + + // + // Listen for serial input, which is how we set the address + // + if (Serial.available()) + { + // If the character on serial input is in a valid range... + char c = Serial.read(); + if ( c >= '1' && c <= '6' ) + { + // It is our address + EEPROM.write(address_at_eeprom_location,c-'0'); + + // And we are done right now (no easy way to soft reset) + printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); + while(1); + } + } +} +// vim:ai sts=2 sw=2 ft=cpp From c8ed556aba2259a2c658eecf9ef38494918647ce Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 1 Apr 2011 22:55:33 -0700 Subject: [PATCH 010/326] Added an indication of which pipe a message came from. Fixed a bug where pipes 2+ were not getting enabled. --- RF24.cpp | 43 ++++++++++++++++++++++++++++++++++++------- RF24.h | 11 +++++++++-- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 2533df1..91b5a2b 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -232,6 +232,14 @@ void RF24::printDetails(void) printf("%02x",*bufptr); printf("\n\r"); + status = read_register(RX_ADDR_P2,buffer,1); + printf("RX_ADDR_P2 = 0x%02x",*buffer); + printf("\n\r"); + + status = read_register(RX_ADDR_P3,buffer,1); + printf("RX_ADDR_P3 = 0x%02x",*buffer); + printf("\n\r"); + status = read_register(TX_ADDR,buffer,5); printf("TX_ADDR = 0x",buffer); bufptr = buffer + 5; @@ -350,10 +358,16 @@ boolean RF24::write( const void* buf, uint8_t len ) return result; } - /******************************************************************/ boolean RF24::available(void) +{ + return available(NULL); +} + +/******************************************************************/ + +boolean RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); boolean result = ( status & _BV(RX_DR) ); @@ -362,7 +376,15 @@ boolean RF24::available(void) { IF_SERIAL_DEBUG(print_status(status)); + // If the caller wants the pipe number, include that + if ( pipe_num ) + *pipe_num = ( status >> RX_P_NO ) & B111; + // Clear the status bit + + // ??? Should this REALLY be cleared now? Or wait until we + // actually READ the payload? + write_register(STATUS,_BV(RX_DR) ); } @@ -408,18 +430,25 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) const uint8_t child_payload_size[] = { RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 }; const uint8_t child_pipe_enable[] = { - ENAA_P1, ENAA_P2, ENAA_P3, ENAA_P4, ENAA_P5 }; + ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 }; if (--child < 5) { - write_register(child_pipe[child], reinterpret_cast(&value), 5); + // For pipes 2-5, only write the LSB + if ( !child ) + write_register(child_pipe[child], reinterpret_cast(&value), 5); + else + write_register(child_pipe[child], reinterpret_cast(&value), 1); + write_register(child_payload_size[child],payload_size); // Note this is kind of an inefficient way to set up these enable bits, bit I thought it made // the calling code more simple - uint8_t en_aa; - read_register(EN_AA,&en_aa,1); - en_aa |= _BV(child_pipe_enable[child]); - write_register(EN_AA,en_aa); + uint8_t en_rx; + read_register(EN_RXADDR,&en_rx,1); + en_rx |= _BV(child_pipe_enable[child]); + write_register(EN_RXADDR,en_rx); } } +// vim:ai sts=2 sw=2 ft=cpp + diff --git a/RF24.h b/RF24.h index 8916f0b..3f91ff9 100644 --- a/RF24.h +++ b/RF24.h @@ -245,6 +245,14 @@ public: */ boolean available(void) ; + /** + * Test whether there are bytes available to be read + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + boolean available(uint8_t* pipe_num); + /** * Read the payload * @@ -252,8 +260,6 @@ public: * * The size of data read is the fixed payload size, see getPayloadSize() * - * @todo Indicate which pipe it came from - * * @note I specifically chose 'void*' as a data type to make it easier * for beginners to use. No casting needed. * @@ -323,4 +329,5 @@ public: */ #endif // __RF24_H__ +// vim:ai sts=2 sw=2 ft=cpp From c2dc35652e500e58aa11b44e6f1344c5e8868a26 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 1 Apr 2011 22:56:46 -0700 Subject: [PATCH 011/326] Flipped the logic on the role pin. --- examples/pingpair/pingpair.pde | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index dc70270..18456e2 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -29,8 +29,8 @@ RF24 radio(8,9); -// sets the role of this unit in hardware. Connect to GND to be the 'ping' sender. -// Connect to +5V to be the 'pong' receiver. +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'pong' receiver. const int role_pin = 7; // @@ -72,9 +72,9 @@ void setup(void) // read the address pin, establish our role if ( digitalRead(role_pin) ) - role = role_pong_back; - else role = role_ping_out; + else + role = role_pong_back; // // Print preamble From e1db4401ca01fd5a113e49acdbe8b0148c45c0e7 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 1 Apr 2011 22:57:01 -0700 Subject: [PATCH 012/326] Make the makefile be smart about which OS it's running, and make environmental changes as appropriate --- examples/pingpair/makefile | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/pingpair/makefile b/examples/pingpair/makefile index 52db900..55a4885 100644 --- a/examples/pingpair/makefile +++ b/examples/pingpair/makefile @@ -57,17 +57,29 @@ AVRDUDE_PROTOCOL = stk500v1 COM = 33 endif +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +else ARDUINO_VERSION = 22 -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries AVR_TOOLS_PATH = /usr/bin AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools PORT = /dev/ttyUSB0 PORT2 = /dev/ttyUSB1 +endif + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries F_CPU = 16000000 ########################################################## From 117678c46f0d0cf82377953a399bb9f6740fb705 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 1 Apr 2011 23:02:01 -0700 Subject: [PATCH 013/326] Tweaked doc paramters --- Doxyfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doxyfile b/Doxyfile index ee5ba85..5cfb66f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -129,7 +129,7 @@ STRIP_FROM_INC_PATH = # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. -SHORT_NAMES = YES +SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style @@ -486,7 +486,7 @@ SHOW_DIRECTORIES = NO # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. -SHOW_FILES = NO +SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. @@ -735,7 +735,7 @@ USE_HTAGS = NO # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. -VERBATIM_HEADERS = NO +VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index From 21057e4608e755ef606e33041df3f18290572638 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 3 Apr 2011 20:28:54 -0700 Subject: [PATCH 014/326] Cleaned up documentation. --- README | 12 ------ README.md | 19 ++++++++++ RF24.h | 62 +++++++++++++++++++----------- examples/pingpair/pingpair.pde | 9 ++--- examples/starping/starping.pde | 69 ++++++++++++++++++++-------------- 5 files changed, 104 insertions(+), 67 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index f887070..0000000 --- a/README +++ /dev/null @@ -1,12 +0,0 @@ -Arduino driver for nRF24L01 2.4GHz Wireless Transceiver - -See Datasheet at http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf - -This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or -the SPI hardware will go into 'slave' mode. - -Design Goals: This library is designed to be... -* Maximally compliant with the intended operation of the chip -* Easy for beginners to use -* Consumed with a public interface that's similiar to other Arduino standard libraries -* Built against the standard SPI library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..504829f --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Arduino driver for nRF24L01 2.4GHz Wireless Transceiver + +Design Goals: This library is designed to be... +* Maximally compliant with the intended operation of the chip +* Easy for beginners to use +* Consumed with a public interface that's similiar to other Arduino standard libraries +* Built against the standard SPI library. + +Please refer to: + +* [Documentation Main Page](http://maniacbug.github.com/RF24) +* [RF24 Class Documentation](http://maniacbug.github.com/RF24/classRF24.html) +* [Source Code](https://github.com/maniacbug/RF24) +* [Downloads](https://github.com/maniacbug/RF24/archives/master) +* [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) + +This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or +the SPI hardware will go into 'slave' mode. + diff --git a/RF24.h b/RF24.h index 3f91ff9..0975fdf 100644 --- a/RF24.h +++ b/RF24.h @@ -13,21 +13,6 @@ /** * Driver for nRF24L01 2.4GHz Wireless Transceiver - * - * Please refer to: - * - * @li Detailed Documentation - * @li Source Code - * @li Chip Datasheet - * - * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or - * the SPI hardware will go into 'slave' mode. - * - * Design Goals: This library is designed to be... - * @li Maximally compliant with the intended operation of the chip - * @li Easy for beginners to use - * @li Consumed with a public interface that's similiar to other Arduino standard libraries - * @li Built against the standard SPI library. */ class RF24 @@ -182,7 +167,9 @@ public: * Set Payload Size * * This implementation uses a pre-stablished fixed payload size for all - * transmissions. + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). * * @todo Implement variable-sized payloads feature * @@ -197,7 +184,7 @@ public: * * @return The number of bytes in the payload */ - uint8_t getPayloadSize(void) ; + uint8_t getPayloadSize(void) ; /** * Print a giant block of debugging information to stdout @@ -301,8 +288,8 @@ public: * Only the least significant byte should be unique, e.g. * * @code - * openReadingPipe(0xF0F0F0F0AA); - * openReadingPipe(0xF0F0F0F066); + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); * @endcode * * @todo Enforce the restriction that all pipes must share the top 32 bits @@ -319,13 +306,44 @@ public: * * This is an example of how to use the RF24 class. Write this sketch to two different nodes, * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. */ /** - * @mainpage Driver Library for nRF24L01 + * @example starping.pde * - * See the RF24 class for details on how to drive this chip. + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +/** + * @mainpage Driver for nRF24L01 2.4GHz Wireless Transceiver + * + * Design Goals: This library is designed to be... + * @li Maximally compliant with the intended operation of the chip + * @li Easy for beginners to use + * @li Consumed with a public interface that's similiar to other Arduino standard libraries + * @li Built against the standard SPI library. + * + * Please refer to: + * + * @li Documentation Main Page + * @li RF24 Class Documentation + * @li Source Code + * @li Downloads Page + * @li Chip Datasheet + * + * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or + * the SPI hardware will go into 'slave' mode. */ #endif // __RF24_H__ diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 18456e2..3a1cf41 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -9,11 +9,10 @@ /** * Example RF Radio Ping Pair * - * This sketch is an example of using the RF24 library for Arduino. Deploy this on - * two nodes, set one as the 'trasmit' and the other the 'receive' unit. The transmit - * unit will send out the value of millis() once a second. The receive unit will respond - * back with a copy of the value. The transmit unit can get that 'ping' back, and - * determine how long the whole cycle took. + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. */ #include diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index 34e2eb9..f3c4e9c 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -10,14 +10,14 @@ * Example RF Radio Ping Star Group * * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' and the others - * as 'ping transmit' units. The ping units unit will send out the value of millis() - * once a second. The pong unit will respond back with a copy of the value. - * The ping unit can get that response back, and - * determine how long the whole cycle took. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. * - * This example requires a bit more complexity to determine which unit is - * which. The pong receiver is identified by having its role_pin tied to ground. + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. * The ping senders are further differentiated by a byte in eeprom. */ @@ -27,8 +27,6 @@ #include "RF24.h" #include "printf.h" -extern EEPROMClass EEPROM; - // // Hardware configuration // @@ -45,9 +43,14 @@ const int role_pin = 7; // Topology // -// Radio pipe addresses for the 6 nodes to communicate -const uint64_t talking_pipes[6] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; -const uint64_t listening_pipes[6] = { 0x3A3A3A3AE1LL, 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; +// Radio pipe addresses for the nodes to communicate. Only ping nodes need +// dedicated pipes in this topology. Each ping node has a talking pipe +// that it will ping into, and a listening pipe that it will listen for +// the pong. The pong node listens on all the ping node talking pipes +// and sends the pong back on the sending node's specific listening pipe. + +const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; +const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; // // Role management @@ -76,7 +79,8 @@ role_e role; const uint8_t address_at_eeprom_location = 0; // What is our address (SRAM cache of the address from EEPROM) -// Note that zero is an INVALID address +// Note that zero is an INVALID address. The pong back unit takes address +// 1, and the rest are 2-6 uint8_t node_address; void setup(void) @@ -139,26 +143,26 @@ void setup(void) // // Open pipes to other nodes for communication // - - // Open 'our' pipe for writing - // ping nodes open the parent's pipe for reading - // pong node opens all children's pipes for reading - + + // The pong node listens on all the ping node talking pipes + // and sends the pong back on the sending node's specific listening pipe. if ( role == role_pong_back ) { - // Listen to all ping nodes' talking pipes - radio.openReadingPipe(1,talking_pipes[1]); - radio.openReadingPipe(2,talking_pipes[2]); - radio.openReadingPipe(3,talking_pipes[3]); - radio.openReadingPipe(4,talking_pipes[4]); - radio.openReadingPipe(5,talking_pipes[5]); + radio.openReadingPipe(1,talking_pipes[0]); + radio.openReadingPipe(2,talking_pipes[1]); + radio.openReadingPipe(3,talking_pipes[2]); + radio.openReadingPipe(4,talking_pipes[3]); + radio.openReadingPipe(5,talking_pipes[4]); } + + // Each ping node has a talking pipe that it will ping into, and a listening + // pipe that it will listen for the pong. if ( role == role_ping_out ) { // Write on our talking pipe - radio.openWritingPipe(talking_pipes[node_address-1]); + radio.openWritingPipe(talking_pipes[node_address-2]); // Listen on our listening pipe - radio.openReadingPipe(1,listening_pipes[node_address-1]); + radio.openReadingPipe(1,listening_pipes[node_address-2]); } // @@ -172,6 +176,15 @@ void setup(void) // radio.printDetails(); + + // + // Prompt the user to assign a node address if we don't have one + // + + if ( role == role_invalid ) + { + printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); + } } void loop(void) @@ -238,7 +251,7 @@ void loop(void) done = radio.read( &got_time, sizeof(unsigned long) ); // Spew it - printf("Got payload %lu from %i...",got_time,pipe_num); + printf("Got payload %lu from node %i...",got_time,pipe_num+1); } // First, stop listening so we can talk @@ -277,4 +290,4 @@ void loop(void) } } } -// vim:ai sts=2 sw=2 ft=cpp +// vim:ai:ci sts=2 sw=2 ft=cpp From 0668494aee5536d43fb23806d9f0c9bdfb113dee Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 3 Apr 2011 20:32:21 -0700 Subject: [PATCH 015/326] Trying to get the formatting just right --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 504829f..c0e71c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Arduino driver for nRF24L01 2.4GHz Wireless Transceiver Design Goals: This library is designed to be... + * Maximally compliant with the intended operation of the chip * Easy for beginners to use * Consumed with a public interface that's similiar to other Arduino standard libraries From 5b632f374085b17d8614fb3c13fb3d79bae9a9ff Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 24 Apr 2011 11:24:21 -0700 Subject: [PATCH 016/326] Updated vim modeline --- RF24.cpp | 2 +- RF24.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 91b5a2b..e38ee66 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -450,5 +450,5 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) write_register(EN_RXADDR,en_rx); } } -// vim:ai sts=2 sw=2 ft=cpp +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 0975fdf..4c4e653 100644 --- a/RF24.h +++ b/RF24.h @@ -347,5 +347,5 @@ public: */ #endif // __RF24_H__ -// vim:ai sts=2 sw=2 ft=cpp +// vim:ai:cin:sts=2 sw=2 ft=cpp From e188242a8687c2880dbb60b394308a6dd25c254e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 24 Apr 2011 11:34:05 -0700 Subject: [PATCH 017/326] Enabled warnings, and cleaned up warnings. Updated pingpair makefile for recent makefile improvements --- RF24.cpp | 6 +++--- examples/pingpair/makefile | 5 +++-- examples/pingpair/pingpair.pde | 4 ++-- examples/pingpair/printf.h | 2 ++ examples/starping/makefile | 4 ++-- examples/starping/printf.h | 2 ++ examples/starping/starping.pde | 2 +- 7 files changed, 15 insertions(+), 10 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index e38ee66..6c042b9 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -219,14 +219,14 @@ void RF24::printDetails(void) uint8_t buffer[5]; uint8_t status = read_register(RX_ADDR_P0,buffer,5); print_status(status); - printf("RX_ADDR_P0 = 0x",buffer); + printf("RX_ADDR_P0 = 0x"); uint8_t *bufptr = buffer + 5; while( bufptr-- > buffer ) printf("%02x",*bufptr); printf("\n\r"); status = read_register(RX_ADDR_P1,buffer,5); - printf("RX_ADDR_P1 = 0x",buffer); + printf("RX_ADDR_P1 = 0x"); bufptr = buffer + 5; while( bufptr-- > buffer ) printf("%02x",*bufptr); @@ -241,7 +241,7 @@ void RF24::printDetails(void) printf("\n\r"); status = read_register(TX_ADDR,buffer,5); - printf("TX_ADDR = 0x",buffer); + printf("TX_ADDR = 0x"); bufptr = buffer + 5; while( bufptr-- > buffer ) printf("%02x",*bufptr); diff --git a/examples/pingpair/makefile b/examples/pingpair/makefile index 55a4885..a1c6d4c 100644 --- a/examples/pingpair/makefile +++ b/examples/pingpair/makefile @@ -139,8 +139,8 @@ CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) #CSTANDARD = -std=gnu99 CDEBUG = -g$(DEBUG) #CWARN = -Wall -Wstrict-prototypes -#CWARN = -Wall # show all warnings -CWARN = -w # suppress all warnings +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings CMAP = -Wl,-Map,output.map ####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums CTUNING = -ffunction-sections -fdata-sections @@ -198,6 +198,7 @@ build: elf hex output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde test -d output || mkdir output echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ cat $< >> $@ elf: output/$(PROJECT_NAME).elf diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 3a1cf41..b0b3f0e 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -29,7 +29,7 @@ RF24 radio(8,9); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'pong' receiver. +// Leave open to be the 'ping' transmitter const int role_pin = 7; // @@ -137,7 +137,7 @@ void loop(void) // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); + radio.write( &time, sizeof(unsigned long) ); // Now, continue listening radio.startListening(); diff --git a/examples/pingpair/printf.h b/examples/pingpair/printf.h index 52e0c89..63501e4 100644 --- a/examples/pingpair/printf.h +++ b/examples/pingpair/printf.h @@ -21,6 +21,8 @@ int serial_putc( char c, FILE *t ) { Serial.write( c ); + + return c; } void printf_begin(void) diff --git a/examples/starping/makefile b/examples/starping/makefile index 42aa4e0..935d5dc 100644 --- a/examples/starping/makefile +++ b/examples/starping/makefile @@ -140,8 +140,8 @@ CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) #CSTANDARD = -std=gnu99 CDEBUG = -g$(DEBUG) #CWARN = -Wall -Wstrict-prototypes -#CWARN = -Wall # show all warnings -CWARN = -w # suppress all warnings +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings CMAP = -Wl,-Map,output.map ####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums CTUNING = -ffunction-sections -fdata-sections diff --git a/examples/starping/printf.h b/examples/starping/printf.h index 52e0c89..63501e4 100644 --- a/examples/starping/printf.h +++ b/examples/starping/printf.h @@ -21,6 +21,8 @@ int serial_putc( char c, FILE *t ) { Serial.write( c ); + + return c; } void printf_begin(void) diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index f3c4e9c..828b07d 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -201,7 +201,7 @@ void loop(void) // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); + radio.write( &time, sizeof(unsigned long) ); // Now, continue listening radio.startListening(); From 1045cec9b13d079102c92488854c74b1aa0d66c9 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 24 Apr 2011 12:56:43 -0700 Subject: [PATCH 018/326] Now sets spi data mode before interacting with the chip. needed to interoperate with other spi devices --- RF24.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/RF24.cpp b/RF24.cpp index 91b5a2b..0d5e99e 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -22,6 +22,7 @@ void RF24::csn(int mode) { + SPI.setDataMode(SPI_MODE0); digitalWrite(csn_pin,mode); } From dfb2faa8412ac7c64abd4da8409301c622c64ed1 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 25 Apr 2011 20:29:57 -0700 Subject: [PATCH 019/326] Removed the line that set the payload size to 8 in begin(). Reduced some ints to uint8_t. Added some more debugging info. --- RF24.cpp | 13 ++++++++++--- RF24.h | 10 +++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index ddd9f02..0bca10d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -71,6 +71,8 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) { uint8_t status; + IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\n\r"),reg,value)); + csn(LOW); status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); SPI.transfer(value); @@ -187,14 +189,14 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ -RF24::RF24(int _cepin, int _cspin): +RF24::RF24(uint8_t _cepin, uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), payload_size(32) { } /******************************************************************/ -void RF24::setChannel(int channel) +void RF24::setChannel(uint8_t channel) { write_register(RF_CH,min(channel,127)); } @@ -247,6 +249,12 @@ void RF24::printDetails(void) while( bufptr-- > buffer ) printf("%02x",*bufptr); printf("\n\r"); + + status = read_register(RX_PW_P0,buffer,1); + printf("RX_PW_P0 = 0x%02x\n\r",*buffer); + + status = read_register(RX_PW_P1,buffer,1); + printf("RX_PW_P1 = 0x%02x\n\r",*buffer); read_register(EN_AA,buffer,1); printf("EN_AA = %02x\n\r",*buffer); @@ -285,7 +293,6 @@ void RF24::begin(void) // Set up default configuration. Callers can always change it later. setChannel(1); - setPayloadSize(8); } /******************************************************************/ diff --git a/RF24.h b/RF24.h index 4c4e653..2b2d6b0 100644 --- a/RF24.h +++ b/RF24.h @@ -18,9 +18,9 @@ class RF24 { private: - int ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ - int csn_pin; /**< SPI Chip select */ - int payload_size; /**< Fixed size of payloads */ + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + uint8_t payload_size; /**< Fixed size of payloads */ protected: /** @@ -147,7 +147,7 @@ public: * @param _cepin The pin attached to Chip Enable on the RF module * @param _cspin The pin attached to Chip Select */ - RF24(int _cepin, int _cspin); + RF24(uint8_t _cepin, uint8_t _cspin); /** * Begin operation of the chip @@ -161,7 +161,7 @@ public: * * @param channel Which RF channel to communicate on, 0-127 */ - void setChannel(int channel); + void setChannel(uint8_t channel); /** * Set Payload Size From 33b897318fd04f33c91fc5adf0aa67cb2461938e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 25 Apr 2011 20:40:51 -0700 Subject: [PATCH 020/326] Moved printf format strings to progmem. --- RF24.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 0bca10d..b9606cd 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -166,7 +166,7 @@ uint8_t RF24::get_status(void) void RF24::print_status(uint8_t status) { - printf("STATUS=%02x: RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r", + printf_P(PSTR("STATUS=%02x: RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), status, (status & _BV(RX_DR))?1:0, (status & _BV(TX_DS))?1:0, @@ -180,7 +180,7 @@ void RF24::print_status(uint8_t status) void RF24::print_observe_tx(uint8_t value) { - printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r", + printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r"), value, (value >> PLOS_CNT) & B1111, (value >> ARC_CNT) & B1111 @@ -222,48 +222,48 @@ void RF24::printDetails(void) uint8_t buffer[5]; uint8_t status = read_register(RX_ADDR_P0,buffer,5); print_status(status); - printf("RX_ADDR_P0 = 0x"); + printf_P(PSTR("RX_ADDR_P0 = 0x")); uint8_t *bufptr = buffer + 5; while( bufptr-- > buffer ) - printf("%02x",*bufptr); - printf("\n\r"); + printf_P(PSTR("%02x"),*bufptr); + printf_P(PSTR("\n\r")); status = read_register(RX_ADDR_P1,buffer,5); - printf("RX_ADDR_P1 = 0x"); + printf_P(PSTR("RX_ADDR_P1 = 0x")); bufptr = buffer + 5; while( bufptr-- > buffer ) - printf("%02x",*bufptr); - printf("\n\r"); + printf_P(PSTR("%02x"),*bufptr); + printf_P(PSTR("\n\r")); status = read_register(RX_ADDR_P2,buffer,1); - printf("RX_ADDR_P2 = 0x%02x",*buffer); - printf("\n\r"); + printf_P(PSTR("RX_ADDR_P2 = 0x%02x"),*buffer); + printf_P(PSTR("\n\r")); status = read_register(RX_ADDR_P3,buffer,1); - printf("RX_ADDR_P3 = 0x%02x",*buffer); - printf("\n\r"); + printf_P(PSTR("RX_ADDR_P3 = 0x%02x"),*buffer); + printf_P(PSTR("\n\r")); status = read_register(TX_ADDR,buffer,5); - printf("TX_ADDR = 0x"); + printf_P(PSTR("TX_ADDR = 0x")); bufptr = buffer + 5; while( bufptr-- > buffer ) - printf("%02x",*bufptr); - printf("\n\r"); + printf_P(PSTR("%02x"),*bufptr); + printf_P(PSTR("\n\r")); status = read_register(RX_PW_P0,buffer,1); - printf("RX_PW_P0 = 0x%02x\n\r",*buffer); + printf_P(PSTR("RX_PW_P0 = 0x%02x\n\r"),*buffer); status = read_register(RX_PW_P1,buffer,1); - printf("RX_PW_P1 = 0x%02x\n\r",*buffer); + printf_P(PSTR("RX_PW_P1 = 0x%02x\n\r"),*buffer); read_register(EN_AA,buffer,1); - printf("EN_AA = %02x\n\r",*buffer); + printf_P(PSTR("EN_AA = %02x\n\r"),*buffer); read_register(EN_RXADDR,buffer,1); - printf("EN_RXADDR = %02x\n\r",*buffer); + printf_P(PSTR("EN_RXADDR = %02x\n\r"),*buffer); read_register(RF_CH,buffer,1); - printf("RF_CH = %02x\n\r",*buffer); + printf_P(PSTR("RF_CH = %02x\n\r"),*buffer); } /******************************************************************/ From 997140e0d46b6ec98da293a6a280bfd413bdb050 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 28 Apr 2011 16:14:07 -0700 Subject: [PATCH 021/326] Added new sample, based on pingpair --- examples/ackpacket/.gitignore | 1 + examples/ackpacket/ackpacket.pde | 204 ++++++++++++++++++++++ examples/ackpacket/makefile | 290 +++++++++++++++++++++++++++++++ examples/ackpacket/printf.h | 33 ++++ 4 files changed, 528 insertions(+) create mode 100644 examples/ackpacket/.gitignore create mode 100644 examples/ackpacket/ackpacket.pde create mode 100644 examples/ackpacket/makefile create mode 100644 examples/ackpacket/printf.h diff --git a/examples/ackpacket/.gitignore b/examples/ackpacket/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/ackpacket/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/ackpacket/ackpacket.pde b/examples/ackpacket/ackpacket.pde new file mode 100644 index 0000000..b0b3f0e --- /dev/null +++ b/examples/ackpacket/ackpacket.pde @@ -0,0 +1,204 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/pingpair/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:ci sts=2 sw=2 ft=cpp diff --git a/examples/ackpacket/makefile b/examples/ackpacket/makefile new file mode 100644 index 0000000..a1c6d4c --- /dev/null +++ b/examples/ackpacket/makefile @@ -0,0 +1,290 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/ackpacket/printf.h b/examples/ackpacket/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/ackpacket/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From 79628e7849cc3b9f92bc7203ed3d8fbe49d8e537 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 30 Apr 2011 15:31:04 -0700 Subject: [PATCH 022/326] Added ack payload. Added a sample using pingpair as a base with minimal changes. --- RF24.cpp | 111 +++++++++- RF24.h | 13 +- examples/pingpair_pl/.gitignore | 1 + examples/pingpair_pl/makefile | 290 +++++++++++++++++++++++++++ examples/pingpair_pl/pingpair_pl.pde | 249 +++++++++++++++++++++++ examples/pingpair_pl/printf.h | 33 +++ nRF24L01.h | 14 ++ 7 files changed, 703 insertions(+), 8 deletions(-) create mode 100644 examples/pingpair_pl/.gitignore create mode 100644 examples/pingpair_pl/makefile create mode 100644 examples/pingpair_pl/pingpair_pl.pde create mode 100644 examples/pingpair_pl/printf.h diff --git a/RF24.cpp b/RF24.cpp index b9606cd..6b8a073 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -11,7 +11,7 @@ #include "RF24.h" #include "nRF24L01.h" -#undef SERIAL_DEBUG +#define SERIAL_DEBUG #ifdef SERIAL_DEBUG #define IF_SERIAL_DEBUG(x) (x) #else @@ -51,6 +51,18 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) /******************************************************************/ +uint8_t RF24::read_register(uint8_t reg) +{ + csn(LOW); + SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + uint8_t result = SPI.transfer(0xff); + + csn(HIGH); + return result; +} + +/******************************************************************/ + uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) { uint8_t status; @@ -190,7 +202,7 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), payload_size(32) + ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_packet_available(false) { } @@ -327,6 +339,7 @@ boolean RF24::write( const void* buf, uint8_t len ) // Transmitter power-up write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP)); + delay(2); // Send the payload write_payload( buf, len ); @@ -339,18 +352,37 @@ boolean RF24::write( const void* buf, uint8_t len ) // Monitor the send uint8_t observe_tx; uint8_t status; + uint8_t retries = 255; do { status = read_register(OBSERVE_TX,&observe_tx,1); IF_SERIAL_DEBUG(Serial.print(status,HEX)); IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); + if ( ! retries-- ) + { + IF_SERIAL_DEBUG(printf("ABORTED: too many retries\n\r")); + break; + } } while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) ); if ( status & _BV(TX_DS) ) result = true; - - IF_SERIAL_DEBUG(Serial.println(result?"...OK.":"...Failed")); + + IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); + + ack_packet_available = ( status & _BV(RX_DR) ); + uint8_t pl_len = 0; + if ( ack_packet_available ) + { + write_register(STATUS,_BV(RX_DR) ); + csn(LOW); + SPI.transfer( R_RX_PL_WID ); + pl_len = SPI.transfer(0xff); + csn(HIGH); + IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); + IF_SERIAL_DEBUG(Serial.println(pl_len,DEC)); + } // Yay, we are done. ce(LOW); @@ -378,12 +410,11 @@ boolean RF24::available(void) boolean RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); + IF_SERIAL_DEBUG(print_status(status)); boolean result = ( status & _BV(RX_DR) ); if (result) { - IF_SERIAL_DEBUG(print_status(status)); - // If the caller wants the pipe number, include that if ( pipe_num ) *pipe_num = ( status >> RX_P_NO ) & B111; @@ -394,6 +425,12 @@ boolean RF24::available(uint8_t* pipe_num) // actually READ the payload? write_register(STATUS,_BV(RX_DR) ); + + // Handle ack payload receipt + if ( status & _BV(TX_DS) ) + { + write_register(STATUS,_BV(TX_DS)); + } } return result; @@ -458,5 +495,67 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) write_register(EN_RXADDR,en_rx); } } + +/******************************************************************/ + +void RF24::toggle_features(void) +{ + csn(LOW); + SPI.transfer( ACTIVATE ); + SPI.transfer( 0x73 ); + csn(HIGH); +} + +/******************************************************************/ + +void RF24::enableAckPayload(void) +{ + // + // enable ack payload and dynamic payload features + // + + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + + // If it didn't work, the features are not enabled + if ( ! read_register(FEATURE) ) + { + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + } + + IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); + + // + // Enable dynamic payload on pipe 0 + // + + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/******************************************************************/ + +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + const uint8_t* current = (const uint8_t*)buf; + + csn(LOW); + SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); + uint8_t data_len = min(len,32); + while ( data_len-- ) + SPI.transfer(*current++); + + csn(HIGH); +} + +/******************************************************************/ + +boolean RF24::isAckPayloadAvailable(void) +{ + boolean result = ack_packet_available; + ack_packet_available = false; + return result; +} + // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 2b2d6b0..d68deac 100644 --- a/RF24.h +++ b/RF24.h @@ -21,6 +21,7 @@ private: uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ uint8_t csn_pin; /**< SPI Chip select */ uint8_t payload_size; /**< Fixed size of payloads */ + boolean ack_packet_available; /**< Whether there is an ack packet waiting */ protected: /** @@ -54,6 +55,8 @@ protected: * @return Current value of status register */ uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; + + uint8_t read_register(uint8_t reg) ; /** * Write a chunk of data to a register @@ -95,7 +98,7 @@ protected: * @return Current value of status register */ uint8_t read_payload(void* buf, uint8_t len) ; - +public: /** * Empty the receive buffer * @@ -109,7 +112,7 @@ protected: * @return Current value of status register */ uint8_t flush_tx(void); - +protected: /** * Retrieve the current status of the chip * @@ -135,6 +138,7 @@ protected: */ void print_observe_tx(uint8_t value) ; + void toggle_features(void); /**@}*/ public: @@ -299,6 +303,11 @@ public: */ void openReadingPipe(uint8_t number, uint64_t address); + void enableAckPayload(void); + + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + boolean isAckPayloadAvailable(void); }; /** diff --git a/examples/pingpair_pl/.gitignore b/examples/pingpair_pl/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/pingpair_pl/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/pingpair_pl/makefile b/examples/pingpair_pl/makefile new file mode 100644 index 0000000..a1c6d4c --- /dev/null +++ b/examples/pingpair_pl/makefile @@ -0,0 +1,290 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde new file mode 100644 index 0000000..96896e3 --- /dev/null +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -0,0 +1,249 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const short role_pin = 7; + +const short led_pin = 13; // flash the led when a packet has been sent + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xE8E8F0F0E1LL, 0xE8E8F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_pl/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // We will be using the Ack Payload feature, so please enable it + radio.enableAckPayload(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + if ( radio.isAckPayloadAvailable() ) + { + static char response[32]; + radio.read(response,32); + printf("Ack: [%s]",response); + + // try flushing the rx buffer to get the ack packet out of the system ... + radio.flush_rx(); + } + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 500 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Stop listening while we are clocking out the data + radio.stopListening(); + + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + // Begin listening again + radio.startListening(); + } + + // Try again later + digitalWrite(led_pin,HIGH); + delay(3000); + digitalWrite(led_pin,LOW); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + } + + // First, stop listening so we can talk + radio.stopListening(); + + //delay(250); + + // necessary to flush the ack packet out + // but kills the entire tx queue, which I might not + // want in the future!! + radio.flush_tx(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + + //delay(250); + + // Add an ack packet for the next time around + static unsigned id = 0; + static char pl_buffer[10]; + memset(pl_buffer,' ',10); + pl_buffer[9] = 0; + snprintf(pl_buffer,10,"id %04x",id++); + radio.writeAckPayload( 1, pl_buffer, 10 ); + + } + } +} +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_pl/printf.h b/examples/pingpair_pl/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/pingpair_pl/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/nRF24L01.h b/nRF24L01.h index 10aedb3..5f4d1b8 100644 --- a/nRF24L01.h +++ b/nRF24L01.h @@ -47,6 +47,8 @@ #define RX_PW_P4 0x15 #define RX_PW_P5 0x16 #define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D /* Bit Mnemonics */ #define MASK_RX_DR 6 @@ -87,13 +89,25 @@ #define TX_EMPTY 4 #define RX_FULL 1 #define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 /* Instruction Mnemonics */ #define R_REGISTER 0x00 #define W_REGISTER 0x20 #define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 #define R_RX_PAYLOAD 0x61 #define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 #define FLUSH_TX 0xE1 #define FLUSH_RX 0xE2 #define REUSE_TX_PL 0xE3 From 38392fa5f46e945afbfe7e7962a7d94702e9a727 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 30 Apr 2011 19:52:13 -0700 Subject: [PATCH 023/326] Removed ping/pong. Now tx just sends and rx just sends ack payload --- examples/pingpair_pl/pingpair_pl.pde | 68 ++-------------------------- 1 file changed, 4 insertions(+), 64 deletions(-) diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index 96896e3..7f7dcec 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -136,9 +136,6 @@ void loop(void) if (role == role_ping_out) { - // First, stop listening so we can talk. - radio.stopListening(); - // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); @@ -149,45 +146,8 @@ void loop(void) static char response[32]; radio.read(response,32); printf("Ack: [%s]",response); - - // try flushing the rx buffer to get the ack packet out of the system ... - radio.flush_rx(); - } - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 500 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Stop listening while we are clocking out the data - radio.stopListening(); - - // Dump the payloads until we've gotten everything - unsigned long got_time; - boolean done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - // Begin listening again - radio.startListening(); - } + } + printf(" OK\n\r"); // Try again later digitalWrite(led_pin,HIGH); @@ -196,7 +156,7 @@ void loop(void) } // - // Pong back role. Receive each packet, dump it out, and send it back + // Pong back role. Receive each packet, dump it out, add ack payload for next time // if ( role == role_pong_back ) @@ -213,28 +173,9 @@ void loop(void) done = radio.read( &got_time, sizeof(unsigned long) ); // Spew it - printf("Got payload %lu...",got_time); + printf("Got payload %lu\n",got_time); } - // First, stop listening so we can talk - radio.stopListening(); - - //delay(250); - - // necessary to flush the ack packet out - // but kills the entire tx queue, which I might not - // want in the future!! - radio.flush_tx(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - - //delay(250); - // Add an ack packet for the next time around static unsigned id = 0; static char pl_buffer[10]; @@ -242,7 +183,6 @@ void loop(void) pl_buffer[9] = 0; snprintf(pl_buffer,10,"id %04x",id++); radio.writeAckPayload( 1, pl_buffer, 10 ); - } } } From 39b15c8b55f8e86ac0295cf430123818eb9db349 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 30 Apr 2011 21:10:33 -0700 Subject: [PATCH 024/326] Stripped down extraneous pipe opens, and other un-needed things. --- examples/pingpair_pl/pingpair_pl.pde | 49 ++++++++++++---------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index 7f7dcec..6bcb495 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -32,14 +32,12 @@ RF24 radio(8,9); // Leave open to be the 'ping' transmitter const short role_pin = 7; -const short led_pin = 13; // flash the led when a packet has been sent - // // Topology // -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xE8E8F0F0E1LL, 0xE8E8F0F0D2LL }; +// Radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; // // Role management @@ -99,27 +97,24 @@ void setup(void) // Open pipes to other nodes for communication // - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + // This simple sketch opens a single pipes for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. if ( role == role_ping_out ) { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); + radio.openWritingPipe(pipe); } else { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); + radio.openReadingPipe(1,pipe); } // // Start listening // - radio.startListening(); + if ( role == role_pong_back ) + radio.startListening(); // // Dump the configuration of the rf unit for debugging @@ -130,6 +125,8 @@ void setup(void) void loop(void) { + static uint32_t id = 0; + // // Ping out role. Repeatedly send the current time // @@ -143,16 +140,13 @@ void loop(void) if ( radio.isAckPayloadAvailable() ) { - static char response[32]; - radio.read(response,32); - printf("Ack: [%s]",response); + radio.read(&id,sizeof(id)); + printf("Ack: [%lu] ",id); } - printf(" OK\n\r"); + printf("OK\n\r"); - // Try again later - digitalWrite(led_pin,HIGH); - delay(3000); - digitalWrite(led_pin,LOW); + // Try again soon + delay(2000); } // @@ -165,7 +159,7 @@ void loop(void) if ( radio.available() ) { // Dump the payloads until we've gotten everything - unsigned long got_time; + static unsigned long got_time; boolean done = false; while (!done) { @@ -176,13 +170,10 @@ void loop(void) printf("Got payload %lu\n",got_time); } - // Add an ack packet for the next time around - static unsigned id = 0; - static char pl_buffer[10]; - memset(pl_buffer,' ',10); - pl_buffer[9] = 0; - snprintf(pl_buffer,10,"id %04x",id++); - radio.writeAckPayload( 1, pl_buffer, 10 ); + // Add an ack packet for the next time around. This is a simple + // packet counter + radio.writeAckPayload( 1, &id, sizeof(id) ); + ++id; } } } From e7254a3b3a0f85b5c168057cdf4336681dbe92bb Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 2 May 2011 21:20:55 -0700 Subject: [PATCH 025/326] Split out upload target into two --- examples/starping/makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/starping/makefile b/examples/starping/makefile index 935d5dc..2ea7538 100644 --- a/examples/starping/makefile +++ b/examples/starping/makefile @@ -209,8 +209,12 @@ lss: output/$(PROJECT_NAME).lss #sym: output/$(PROJECT_NAME).sym # Upload HEX file to Arduino -upload: output/$(PROJECT_NAME).hex +upload: upload1 upload2 + +upload1: output/$(PROJECT_NAME).hex $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + +upload2: output/$(PROJECT_NAME).hex $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) sym: From 93f5cface0ebc0b82801ab7741b952578af37f7c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 2 May 2011 21:21:10 -0700 Subject: [PATCH 026/326] Fixed a bug where it was writing to the wrong pipe --- examples/starping/starping.pde | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index 828b07d..2100c31 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -258,10 +258,10 @@ void loop(void) radio.stopListening(); // Open the correct pipe for writing - radio.openWritingPipe(listening_pipes[pipe_num]); + radio.openWritingPipe(listening_pipes[pipe_num-1]); // Retain the low 2 bytes to identify the pipe for the spew - uint16_t pipe_id = listening_pipes[pipe_num] & 0xffff; + uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; // Send the final one back. radio.write( &got_time, sizeof(unsigned long) ); From ffc7d099ed05e48724b6471b4d75ce98d8a05075 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 14:39:01 -0700 Subject: [PATCH 027/326] Got ack payloads working. Renamed from 'ackpacket' --- RF24.cpp | 33 ++++++++++++++++++++++----------- RF24.h | 17 ++++++++++++++--- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 6b8a073..215ade7 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -202,7 +202,7 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_packet_available(false) + ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) { } @@ -371,17 +371,13 @@ boolean RF24::write( const void* buf, uint8_t len ) IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); - ack_packet_available = ( status & _BV(RX_DR) ); - uint8_t pl_len = 0; - if ( ack_packet_available ) + ack_payload_available = ( status & _BV(RX_DR) ); + if ( ack_payload_available ) { write_register(STATUS,_BV(RX_DR) ); - csn(LOW); - SPI.transfer( R_RX_PL_WID ); - pl_len = SPI.transfer(0xff); - csn(HIGH); + ack_payload_length = read_payload_length(); IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); - IF_SERIAL_DEBUG(Serial.println(pl_len,DEC)); + IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); } // Yay, we are done. @@ -398,6 +394,21 @@ boolean RF24::write( const void* buf, uint8_t len ) return result; } + +/******************************************************************/ + +uint8_t RF24::read_payload_length(void) +{ + uint8_t result = 0; + + csn(LOW); + SPI.transfer( R_RX_PL_WID ); + result = SPI.transfer(0xff); + csn(HIGH); + + return result; +} + /******************************************************************/ boolean RF24::available(void) @@ -552,8 +563,8 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) boolean RF24::isAckPayloadAvailable(void) { - boolean result = ack_packet_available; - ack_packet_available = false; + boolean result = ack_payload_available; + ack_payload_available = false; return result; } diff --git a/RF24.h b/RF24.h index d68deac..e920b3f 100644 --- a/RF24.h +++ b/RF24.h @@ -21,7 +21,8 @@ private: uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ uint8_t csn_pin; /**< SPI Chip select */ uint8_t payload_size; /**< Fixed size of payloads */ - boolean ack_packet_available; /**< Whether there is an ack packet waiting */ + boolean ack_payload_available; /**< Whether there is an ack payload waiting */ + uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ protected: /** @@ -98,7 +99,17 @@ protected: * @return Current value of status register */ uint8_t read_payload(void* buf, uint8_t len) ; -public: + + /** + * Read the payload length + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @return Payload length of last-received dynamic payload + */ + uint8_t read_payload_length(void); + /** * Empty the receive buffer * @@ -112,7 +123,7 @@ public: * @return Current value of status register */ uint8_t flush_tx(void); -protected: + /** * Retrieve the current status of the chip * From f3356f31a281dc5895b16c7842440a32979738bf Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 14:40:42 -0700 Subject: [PATCH 028/326] Cleaned up comments, variables names, enums --- examples/pingpair_pl/pingpair_pl.pde | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index 6bcb495..952648b 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -7,12 +7,12 @@ */ /** - * Example RF Radio Ping Pair + * Example of using Ack Payloads * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within + * the Ack packet of each transmission. Note that the payload is set BEFORE + * the sender's message arrives. */ #include @@ -36,24 +36,24 @@ const short role_pin = 7; // Topology // -// Radio pipe address for the 2 nodes to communicate. +// Single radio pipe address for the 2 nodes to communicate. const uint64_t pipe = 0xE8E8F0F0E1LL; // // Role management // -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies // which node it is. // // This is done through the role_pin // // The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; +typedef enum { role_sender = 1, role_receiver } role_e; // The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; // The role of the current running sketch role_e role; @@ -71,9 +71,9 @@ void setup(void) // read the address pin, establish our role if ( digitalRead(role_pin) ) - role = role_ping_out; + role = role_sender; else - role = role_pong_back; + role = role_receiver; // // Print preamble @@ -100,7 +100,7 @@ void setup(void) // This simple sketch opens a single pipes for these two nodes to communicate // back and forth. One listens on it, the other talks to it. - if ( role == role_ping_out ) + if ( role == role_sender ) { radio.openWritingPipe(pipe); } @@ -113,7 +113,7 @@ void setup(void) // Start listening // - if ( role == role_pong_back ) + if ( role == role_receiver ) radio.startListening(); // @@ -125,13 +125,13 @@ void setup(void) void loop(void) { - static uint32_t id = 0; + static uint32_t message_count = 0; // - // Ping out role. Repeatedly send the current time + // Sender role. Repeatedly send the current time // - if (role == role_ping_out) + if (role == role_sender) { // Take the time, and send it. This will block until complete unsigned long time = millis(); @@ -140,8 +140,8 @@ void loop(void) if ( radio.isAckPayloadAvailable() ) { - radio.read(&id,sizeof(id)); - printf("Ack: [%lu] ",id); + radio.read(&message_count,sizeof(message_count)); + printf("Ack: [%lu] ",message_count); } printf("OK\n\r"); @@ -150,10 +150,10 @@ void loop(void) } // - // Pong back role. Receive each packet, dump it out, add ack payload for next time + // Receiver role. Receive each packet, dump it out, add ack payload for next time // - if ( role == role_pong_back ) + if ( role == role_receiver ) { // if there is data ready if ( radio.available() ) @@ -172,8 +172,8 @@ void loop(void) // Add an ack packet for the next time around. This is a simple // packet counter - radio.writeAckPayload( 1, &id, sizeof(id) ); - ++id; + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); + ++message_count; } } } From f9b664cd34834885eb383fd166921239e39f065e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 14:42:29 -0700 Subject: [PATCH 029/326] Removed 'ackpacket' example. It was never working --- examples/ackpacket/.gitignore | 1 - examples/ackpacket/ackpacket.pde | 204 ---------------------- examples/ackpacket/makefile | 290 ------------------------------- examples/ackpacket/printf.h | 33 ---- 4 files changed, 528 deletions(-) delete mode 100644 examples/ackpacket/.gitignore delete mode 100644 examples/ackpacket/ackpacket.pde delete mode 100644 examples/ackpacket/makefile delete mode 100644 examples/ackpacket/printf.h diff --git a/examples/ackpacket/.gitignore b/examples/ackpacket/.gitignore deleted file mode 100644 index ea1472e..0000000 --- a/examples/ackpacket/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ diff --git a/examples/ackpacket/ackpacket.pde b/examples/ackpacket/ackpacket.pde deleted file mode 100644 index b0b3f0e..0000000 --- a/examples/ackpacket/ackpacket.pde +++ /dev/null @@ -1,204 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example RF Radio Ping Pair - * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(9600); - printf_begin(); - printf("\n\rRF24/examples/pingpair/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - boolean done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -// vim:ci sts=2 sw=2 ft=cpp diff --git a/examples/ackpacket/makefile b/examples/ackpacket/makefile deleted file mode 100644 index a1c6d4c..0000000 --- a/examples/ackpacket/makefile +++ /dev/null @@ -1,290 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/ackpacket/printf.h b/examples/ackpacket/printf.h deleted file mode 100644 index 63501e4..0000000 --- a/examples/ackpacket/printf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#include "WProgram.h" - -int serial_putc( char c, FILE *t ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ From 72dc77467e0d42a0e0ccc2c5d20e2506074330e0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 14:51:51 -0700 Subject: [PATCH 030/326] Created a new example to show sleeping in between sends. This isn't done yet. It's just a copy of pingpair for now. --- examples/pingpair_sleepy/.gitignore | 1 + examples/pingpair_sleepy/makefile | 290 +++++++++++++++++++ examples/pingpair_sleepy/pingpair_sleepy.pde | 205 +++++++++++++ examples/pingpair_sleepy/printf.h | 33 +++ 4 files changed, 529 insertions(+) create mode 100644 examples/pingpair_sleepy/.gitignore create mode 100644 examples/pingpair_sleepy/makefile create mode 100644 examples/pingpair_sleepy/pingpair_sleepy.pde create mode 100644 examples/pingpair_sleepy/printf.h diff --git a/examples/pingpair_sleepy/.gitignore b/examples/pingpair_sleepy/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/pingpair_sleepy/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/pingpair_sleepy/makefile b/examples/pingpair_sleepy/makefile new file mode 100644 index 0000000..a1c6d4c --- /dev/null +++ b/examples/pingpair_sleepy/makefile @@ -0,0 +1,290 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde new file mode 100644 index 0000000..9098644 --- /dev/null +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -0,0 +1,205 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example RF Radio Ping Pair which Sleeps between Sends + * + * This is an example of how to use the RF24 class to create a battery-efficient system. + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:ci sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_sleepy/printf.h b/examples/pingpair_sleepy/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/pingpair_sleepy/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From b697fd4e79453c2450b88a441e2e1a36e78442fc Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 15:16:06 -0700 Subject: [PATCH 031/326] Added powerDown(). --- RF24.cpp | 7 +++++++ RF24.h | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index 215ade7..24c074a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -333,6 +333,13 @@ void RF24::stopListening(void) /******************************************************************/ +void RF24::powerDown(void) +{ + write_register(CONFIG,0); +} + +/******************************************************************/ + boolean RF24::write( const void* buf, uint8_t len ) { boolean result = false; diff --git a/RF24.h b/RF24.h index e920b3f..593e80c 100644 --- a/RF24.h +++ b/RF24.h @@ -223,6 +223,14 @@ public: */ void stopListening(void); + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening(). + */ + void powerDown(void); + /** * Write to the open writing pipe * From 31793823eaefee67246638621e6cdc126e3bea5b Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 15:16:41 -0700 Subject: [PATCH 032/326] First complete compile/link with sleep functionality. Haven't run it yet. --- examples/pingpair_sleepy/pingpair_sleepy.pde | 76 ++++++++++++++++++-- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index 9098644..79fb29a 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -17,6 +17,7 @@ */ #include +#include #include "nRF24L01.h" #include "RF24.h" #include "printf.h" @@ -59,6 +60,19 @@ const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The role of the current running sketch role_e role; +// +// Sleep declarations +// + +typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e; + +void setup_watchdog(uint8_t prescalar); +void do_sleep(void); + +// +// Normal operation +// + void setup(void) { // @@ -85,6 +99,14 @@ void setup(void) printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); + // + // Prepare sleep parameters + // + + // Only the ping out role sleeps. Wake up every 2s to send a ping + if ( role == role_ping_out ) + setup_watchdog(wdt_2s); + // // Setup and configure rf radio // @@ -164,14 +186,25 @@ void loop(void) // Spew it printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); } - - // Try again 1s later - delay(1000); + + // + // Shut down the system + // + + // Power down the radio. Note that the radio will get powered back up + // on the next write() call. + radio.powerDown(); + + // Sleep the MCU. The watchdog timer will awaken in a short while, and + // continue execution here. + do_sleep(); } // // Pong back role. Receive each packet, dump it out, and send it back // + // This is untouched from the pingpair example. + // if ( role == role_pong_back ) { @@ -202,4 +235,39 @@ void loop(void) } } } -// vim:ci sts=2 sw=2 ft=cpp + +// +// Sleep helpers +// + +// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms +// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec + + +void setup_watchdog(uint8_t prescalar) +{ + prescalar = min(9,prescalar); + uint8_t wdtcsr = prescalar & 7; + if ( prescalar & 8 ) + wdtcsr |= _BV(WDP3); + + MCUSR &= ~_BV(WDRF); + WDTCSR = _BV(WDCE) | _BV(WDE); + WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); +} + +// Even though it does nothing, it's necessary to have a WDT ISR +ISR(WDT_vect) { +} + +void do_sleep(void) +{ + set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here + sleep_enable(); + + sleep_mode(); // System sleeps here + + sleep_disable(); // System continues execution here when watchdog timed out +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp From 0d20f831d02958f78f1c759288adc71de4a6ae0f Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 19:34:22 -0700 Subject: [PATCH 033/326] Turned off serial debug --- RF24.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 24c074a..b03307a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -11,7 +11,7 @@ #include "RF24.h" #include "nRF24L01.h" -#define SERIAL_DEBUG +#undef SERIAL_DEBUG #ifdef SERIAL_DEBUG #define IF_SERIAL_DEBUG(x) (x) #else From ee4568ede4108e19cfd5426841a7dd20c489e3c3 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 19:35:09 -0700 Subject: [PATCH 034/326] Added server time to printout. Power disable/enable before/after sleeping. Changed timer to 4s. --- examples/pingpair_sleepy/pingpair_sleepy.pde | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index 79fb29a..a3a31bf 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -18,6 +18,7 @@ #include #include +#include #include "nRF24L01.h" #include "RF24.h" #include "printf.h" @@ -103,9 +104,9 @@ void setup(void) // Prepare sleep parameters // - // Only the ping out role sleeps. Wake up every 2s to send a ping + // Only the ping out role sleeps. Wake up every 4s to send a ping if ( role == role_ping_out ) - setup_watchdog(wdt_2s); + setup_watchdog(wdt_4s); // // Setup and configure rf radio @@ -219,8 +220,9 @@ void loop(void) // Fetch the payload, and see if this was the last one. done = radio.read( &got_time, sizeof(unsigned long) ); - // Spew it - printf("Got payload %lu...",got_time); + // Spew it. Include our time, because the ping_out millis counter is unreliable + // due to it sleeping + printf("Got payload %lu @ %lu...",got_time,millis()); } // First, stop listening so we can talk @@ -265,9 +267,12 @@ void do_sleep(void) set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here sleep_enable(); + power_all_disable(); sleep_mode(); // System sleeps here sleep_disable(); // System continues execution here when watchdog timed out + power_all_enable(); } + // vim:ai:cin:sts=2 sw=2 ft=cpp From 4ef1f4b8e656c1e7b8b98f455517879a56c6a9a2 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 10 May 2011 21:34:51 -0700 Subject: [PATCH 035/326] Removed power_all/enable/disable, useless. Added a multiples of the wakeup. --- examples/pingpair_sleepy/pingpair_sleepy.pde | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index a3a31bf..2abffc9 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -70,6 +70,9 @@ typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms void setup_watchdog(uint8_t prescalar); void do_sleep(void); +const short sleep_cycles_per_transmission = 4; +volatile short sleep_cycles_remaining = sleep_cycles_per_transmission; + // // Normal operation // @@ -106,7 +109,7 @@ void setup(void) // Only the ping out role sleeps. Wake up every 4s to send a ping if ( role == role_ping_out ) - setup_watchdog(wdt_4s); + setup_watchdog(wdt_1s); // // Setup and configure rf radio @@ -192,13 +195,19 @@ void loop(void) // Shut down the system // + // Experiment with some delay here to see if it has an effect + delay(500); + // Power down the radio. Note that the radio will get powered back up // on the next write() call. radio.powerDown(); // Sleep the MCU. The watchdog timer will awaken in a short while, and // continue execution here. - do_sleep(); + while( sleep_cycles_remaining ) + do_sleep(); + + sleep_cycles_remaining = sleep_cycles_per_transmission; } // @@ -242,10 +251,9 @@ void loop(void) // Sleep helpers // -// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms +// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms // 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec - void setup_watchdog(uint8_t prescalar) { prescalar = min(9,prescalar); @@ -258,8 +266,8 @@ void setup_watchdog(uint8_t prescalar) WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); } -// Even though it does nothing, it's necessary to have a WDT ISR ISR(WDT_vect) { + --sleep_cycles_remaining; } void do_sleep(void) @@ -267,12 +275,9 @@ void do_sleep(void) set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here sleep_enable(); - power_all_disable(); sleep_mode(); // System sleeps here sleep_disable(); // System continues execution here when watchdog timed out - power_all_enable(); } - // vim:ai:cin:sts=2 sw=2 ft=cpp From 6ec7640a7a6f9cccacb313a44a6516fef6457148 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 11 May 2011 14:50:07 -0700 Subject: [PATCH 036/326] Added more documentation --- RF24.h | 79 ++++++++++++++++++-- examples/pingpair_sleepy/pingpair_sleepy.pde | 14 ++-- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/RF24.h b/RF24.h index 593e80c..f28e687 100644 --- a/RF24.h +++ b/RF24.h @@ -57,7 +57,13 @@ protected: */ uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; - uint8_t read_register(uint8_t reg) ; + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); /** * Write a chunk of data to a register @@ -149,6 +155,12 @@ protected: */ void print_observe_tx(uint8_t value) ; + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ void toggle_features(void); /**@}*/ @@ -322,20 +334,54 @@ public: */ void openReadingPipe(uint8_t number, uint64_t address); + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. See the + * pingpair_pl.pde example. + */ void enableAckPayload(void); + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time. I have not tested this. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @warning Calling this function clears the internal flag which indicates + * a payload is available. If it returns true, you must read the packet + * out as the very next interaction with the radio, or the results are + * undefined. + * + * @return True if an ack payload is available. + */ boolean isAckPayloadAvailable(void); }; /** * @example pingpair.pde * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. + * This is an example of how to use the RF24 class. Write this sketch to two + * different nodes, connect the role_pin to ground on one. The ping node sends + * the current time to the pong node, which responds by sending the value back. + * The ping node can then see how long the whole cycle took. */ /** @@ -347,12 +393,33 @@ public: * unit will send out the value of millis() once a second. The pong unit will * respond back with a copy of the value. Each ping unit can get that response * back, and determine how long the whole cycle took. - * + Warning: + Calling this function clears the internal flag which indicates a payload is available. If it returns true, you must read the packet out as the very next interaction with the radio, or the results are undefined. + +* * This example requires a bit more complexity to determine which unit is which. * The pong receiver is identified by having its role_pin tied to ground. * The ping senders are further differentiated by a byte in eeprom. */ +/** + * @example pingpair_pl.pde + * + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within + * the Ack packet of each transmission. Note that the payload is set BEFORE + * the sender's message arrives. + */ + +/** + * @example pingpair_sleepy.pde + * + * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the pingpair.pde example, but the + * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle. + */ + /** * @mainpage Driver for nRF24L01 2.4GHz Wireless Transceiver * diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index 2abffc9..08e5a99 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -9,11 +9,15 @@ /** * Example RF Radio Ping Pair which Sleeps between Sends * - * This is an example of how to use the RF24 class to create a battery-efficient system. - * Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. + * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the pingpair.pde example, but the + * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle. + * + * As with the pingpair.pde example, write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. */ #include From 1fd8ee72c0b9a32d754ece211b1790bea0265b4e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 12 May 2011 14:21:44 -0700 Subject: [PATCH 037/326] Created new example, copied from starping --- examples/starping_relay/.gitignore | 1 + examples/starping_relay/makefile | 295 +++++++++++++++++++++ examples/starping_relay/printf.h | 33 +++ examples/starping_relay/starping_relay.pde | 293 ++++++++++++++++++++ 4 files changed, 622 insertions(+) create mode 100644 examples/starping_relay/.gitignore create mode 100644 examples/starping_relay/makefile create mode 100644 examples/starping_relay/printf.h create mode 100644 examples/starping_relay/starping_relay.pde diff --git a/examples/starping_relay/.gitignore b/examples/starping_relay/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/starping_relay/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/starping_relay/makefile b/examples/starping_relay/makefile new file mode 100644 index 0000000..2ea7538 --- /dev/null +++ b/examples/starping_relay/makefile @@ -0,0 +1,295 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: upload1 upload2 + +upload1: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + +upload2: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/starping_relay/printf.h b/examples/starping_relay/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/starping_relay/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde new file mode 100644 index 0000000..2100c31 --- /dev/null +++ b/examples/starping_relay/starping_relay.pde @@ -0,0 +1,293 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example RF Radio Ping Star Group + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +#include +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'pong' receiver. +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the nodes to communicate. Only ping nodes need +// dedicated pipes in this topology. Each ping node has a talking pipe +// that it will ping into, and a listening pipe that it will listen for +// the pong. The pong node listens on all the ping node talking pipes +// and sends the pong back on the sending node's specific listening pipe. + +const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; +const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_invalid = 0, role_ping_out, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +// +// Address management +// + +// Where in EEPROM is the address stored? +const uint8_t address_at_eeprom_location = 0; + +// What is our address (SRAM cache of the address from EEPROM) +// Note that zero is an INVALID address. The pong back unit takes address +// 1, and the rest are 2-6 +uint8_t node_address; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Address + // + + if ( role == role_pong_back ) + node_address = 1; + else + { + // Read the address from EEPROM + uint8_t reading = EEPROM.read(address_at_eeprom_location); + + // If it is in a valid range for node addresses, it is our + // address. + if ( reading >= 2 && reading <= 6 ) + node_address = reading; + + // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' + else + { + node_address = 0; + role = role_invalid; + } + } + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/starping/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("ADDRESS: %i\n\r",node_address); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // The pong node listens on all the ping node talking pipes + // and sends the pong back on the sending node's specific listening pipe. + if ( role == role_pong_back ) + { + radio.openReadingPipe(1,talking_pipes[0]); + radio.openReadingPipe(2,talking_pipes[1]); + radio.openReadingPipe(3,talking_pipes[2]); + radio.openReadingPipe(4,talking_pipes[3]); + radio.openReadingPipe(5,talking_pipes[4]); + } + + // Each ping node has a talking pipe that it will ping into, and a listening + // pipe that it will listen for the pong. + if ( role == role_ping_out ) + { + // Write on our talking pipe + radio.openWritingPipe(talking_pipes[node_address-2]); + // Listen on our listening pipe + radio.openReadingPipe(1,listening_pipes[node_address-2]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Prompt the user to assign a node address if we don't have one + // + + if ( role == role_invalid ) + { + printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); + } +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + uint8_t pipe_num; + if ( radio.available(&pipe_num) ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu from node %i...",got_time,pipe_num+1); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Open the correct pipe for writing + radio.openWritingPipe(listening_pipes[pipe_num-1]); + + // Retain the low 2 bytes to identify the pipe for the spew + uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response to %04x.\n\r",pipe_id); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } + + // + // Listen for serial input, which is how we set the address + // + if (Serial.available()) + { + // If the character on serial input is in a valid range... + char c = Serial.read(); + if ( c >= '1' && c <= '6' ) + { + // It is our address + EEPROM.write(address_at_eeprom_location,c-'0'); + + // And we are done right now (no easy way to soft reset) + printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); + while(1); + } + } +} +// vim:ai:ci sts=2 sw=2 ft=cpp From 986864236c51b70692e6ca143ddcf6a6a3bba79d Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 12 May 2011 20:21:49 -0700 Subject: [PATCH 038/326] Rewired everything, re-tested with ping/pong nodes only --- examples/starping_relay/starping_relay.pde | 208 +++++++++++++-------- 1 file changed, 135 insertions(+), 73 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 2100c31..3e347d7 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -7,7 +7,7 @@ */ /** - * Example RF Radio Ping Star Group + * Example RF Radio Ping Star Group with Relay * * This sketch is a more complex example of using the RF24 library for Arduino. * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the @@ -16,6 +16,10 @@ * respond back with a copy of the value. Each ping unit can get that response * back, and determine how long the whole cycle took. * + * This example introduces a new role, the 'relay', which can relay pings or + * pongs from one host to another. This is needed in larger meshes because + * each radio can only listen to 5-6 others. + * * This example requires a bit more complexity to determine which unit is which. * The pong receiver is identified by having its role_pin tied to ground. * The ping senders are further differentiated by a byte in eeprom. @@ -35,10 +39,6 @@ RF24 radio(8,9); -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'pong' receiver. -const int role_pin = 7; - // // Topology // @@ -49,8 +49,23 @@ const int role_pin = 7; // the pong. The pong node listens on all the ping node talking pipes // and sends the pong back on the sending node's specific listening pipe. -const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; -const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; +struct node_info +{ + uint64_t talking_pipe; // Pipe used to talk to parent node + uint64_t listening_pipe; // Pipe used to listen to parent node + uint8_t parent_node; // Number of parent node +}; + +const node_info topology[] = +{ + { 0x0000000000LL, 0x0000000000LL,-1 }, // Base + { 0xF0F0F0F0E1LL, 0x3A3A3A3AE1LL, 0 }, // Relay + { 0xF0F0F0F0D2LL, 0x3A3A3A3AD2LL, 1 }, // Leaf + { 0xF0F0F0F0C3LL, 0x3A3A3A3AC3LL, 1 }, // Leaf + { 0xF0F0F0F0B4LL, 0x3A3A3A3AB4LL, 1 }, // Leaf + { 0xF0F0F0F0A5LL, 0x3A3A3A3AA5LL, 0 }, // Leaf, direct to Base +}; +const short num_nodes = sizeof(topology)/sizeof(node_info); // // Role management @@ -63,10 +78,10 @@ const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3A // // The various roles supported by this sketch -typedef enum { role_invalid = 0, role_ping_out, role_pong_back } role_e; +typedef enum { role_invalid = 0, role_base, role_relay, role_leaf } role_e; // The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; +const char* role_friendly_name[] = { "invalid", "Base", "Relay", "Leaf" }; // The role of the current running sketch role_e role; @@ -78,49 +93,77 @@ role_e role; // Where in EEPROM is the address stored? const uint8_t address_at_eeprom_location = 0; +// What flag value is stored there so we know the value is valid? +const uint8_t valid_eeprom_flag = 0xdf; + // What is our address (SRAM cache of the address from EEPROM) -// Note that zero is an INVALID address. The pong back unit takes address -// 1, and the rest are 2-6 -uint8_t node_address; +// This is an index into the topology[] table above +uint8_t node_address = role_invalid;; + +// +// Payload +// + +struct payload_t +{ + uint8_t from_node; + uint8_t to_node; + unsigned long time; +}; + +void payload_printf(const char* name, const payload_t& pl) +{ + printf("%s Payload from:%u to:%u time:%lu",name,pl.from_node,pl.to_node,pl.time); +} void setup(void) { - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - // // Address // - if ( role == role_pong_back ) - node_address = 1; - else + // Unless we find reasonable values in the EEPROM, these are the defaults + node_address = -1; + + // Look for the token in EEPROM to indicate the following value is + // a validly set node address + if ( EEPROM.read(address_at_eeprom_location) == valid_eeprom_flag ) { // Read the address from EEPROM - uint8_t reading = EEPROM.read(address_at_eeprom_location); + uint8_t reading = EEPROM.read(address_at_eeprom_location+1); // If it is in a valid range for node addresses, it is our // address. - if ( reading >= 2 && reading <= 6 ) + if ( reading <= 5 ) node_address = reading; - - // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' + } + + // + // Role + // + + // Role is determined by address. + if ( node_address != -1 ) + { + // Node #0 is the base, by definition + if ( node_address == 0 ) + role = role_base; else { - node_address = 0; - role = role_invalid; + // Otherwise, it is probably a leaf node + role = role_leaf; + + // If there are any nodes in the topology table which consider this + // a parent, then we are a relay. + int i = num_nodes; + while (i--) + { + if ( topology[i].parent_node == node_address ) + { + role = role_relay; + break; + } + } } } @@ -130,7 +173,7 @@ void setup(void) Serial.begin(9600); printf_begin(); - printf("\n\rRF24/examples/starping/\n\r"); + printf("\n\rRF24/examples/starping_relay/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); printf("ADDRESS: %i\n\r",node_address); @@ -143,28 +186,34 @@ void setup(void) // // Open pipes to other nodes for communication // - - // The pong node listens on all the ping node talking pipes - // and sends the pong back on the sending node's specific listening pipe. - if ( role == role_pong_back ) - { - radio.openReadingPipe(1,talking_pipes[0]); - radio.openReadingPipe(2,talking_pipes[1]); - radio.openReadingPipe(3,talking_pipes[2]); - radio.openReadingPipe(4,talking_pipes[3]); - radio.openReadingPipe(5,talking_pipes[4]); - } - // Each ping node has a talking pipe that it will ping into, and a listening - // pipe that it will listen for the pong. - if ( role == role_ping_out ) + // First listening pipe is #1 + uint8_t current_pipe = 1; + + // Each leaf node has a talking pipe that it will ping into, and a listening + // pipe that it will listen for the pong. Relay nodes also do this. + if ( role == role_leaf || role == role_relay ) { // Write on our talking pipe - radio.openWritingPipe(talking_pipes[node_address-2]); + radio.openWritingPipe(topology[node_address].talking_pipe); + // Listen on our listening pipe - radio.openReadingPipe(1,listening_pipes[node_address-2]); + radio.openReadingPipe(current_pipe++,topology[node_address].listening_pipe); } + // The base and relay nodes listens on all their children node's talking pipes + // and sends the pong back on the child node's specific listening pipe. + if ( role == role_base || role == role_relay ) + { + // The topology table tells us who our children are + int i = num_nodes; + while (i--) + { + if ( topology[i].parent_node == node_address ) + radio.openReadingPipe(current_pipe++,topology[i].talking_pipe); + } + } + // // Start listening // @@ -190,18 +239,22 @@ void setup(void) void loop(void) { // - // Ping out role. Repeatedly send the current time + // Leaf role. Repeatedly send the current time // - if (role == role_ping_out) + if ( role == role_leaf ) { // First, stop listening so we can talk. radio.stopListening(); // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); + payload_t ping; + ping.time = millis(); + ping.from_node = node_address; + ping.to_node = 0; // All pings go to the base + + payload_printf("PING",ping); + radio.write( &ping, sizeof(payload_t) ); // Now, continue listening radio.startListening(); @@ -221,11 +274,12 @@ void loop(void) else { // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); + payload_t pong; + radio.read( &pong, sizeof(payload_t) ); // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + payload_printf(" ...PONG",pong); + printf(" Round-trip delay: %lu\n\r",millis()-pong.time); } // Try again 1s later @@ -233,39 +287,46 @@ void loop(void) } // - // Pong back role. Receive each packet, dump it out, and send it back + // Base role. Receive each packet, dump it out, and send it back // - if ( role == role_pong_back ) + if ( role == role_base ) { // if there is data ready uint8_t pipe_num; if ( radio.available(&pipe_num) ) { // Dump the payloads until we've gotten everything - unsigned long got_time; + payload_t ping; boolean done = false; while (!done) { // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); + done = radio.read( &ping, sizeof(payload_t) ); // Spew it - printf("Got payload %lu from node %i...",got_time,pipe_num+1); + payload_printf("PING",ping); } // First, stop listening so we can talk radio.stopListening(); + + // Construct the return payload (pong) + payload_t pong; + pong.time = ping.time; + pong.from_node = node_address; + pong.to_node = ping.from_node; // Open the correct pipe for writing - radio.openWritingPipe(listening_pipes[pipe_num-1]); + radio.openWritingPipe(topology[pong.to_node].listening_pipe); // Retain the low 2 bytes to identify the pipe for the spew - uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; + uint16_t pipe_id = topology[pong.to_node].listening_pipe & 0xffff; // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response to %04x.\n\r",pipe_id); + radio.write( &pong, sizeof(payload_t) ); + payload_printf(" ...PONG",pong); + printf(" on pipe %04x.\n\r",pipe_id); // Now, resume listening so we catch the next packets. radio.startListening(); @@ -279,10 +340,11 @@ void loop(void) { // If the character on serial input is in a valid range... char c = Serial.read(); - if ( c >= '1' && c <= '6' ) + if ( c >= '0' && c <= '5' ) { // It is our address - EEPROM.write(address_at_eeprom_location,c-'0'); + EEPROM.write(address_at_eeprom_location,valid_eeprom_flag); + EEPROM.write(address_at_eeprom_location+1,c-'0'); // And we are done right now (no easy way to soft reset) printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); @@ -290,4 +352,4 @@ void loop(void) } } } -// vim:ai:ci sts=2 sw=2 ft=cpp +// vim:ai:cin:sts=2 sw=2 ft=cpp From 4f6c4c909e3534dcf63051d49fcee1a3237d13f8 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 12 May 2011 20:27:09 -0700 Subject: [PATCH 039/326] Added a full constructor for payload_t --- examples/starping_relay/starping_relay.pde | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 3e347d7..b6f05d1 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -109,6 +109,8 @@ struct payload_t uint8_t from_node; uint8_t to_node; unsigned long time; + payload_t(void) {} + payload_t(uint8_t _from, uint8_t _to, const unsigned long& _time): from_node(_from), to_node(_to), time(_time) {} }; void payload_printf(const char* name, const payload_t& pl) @@ -247,11 +249,8 @@ void loop(void) // First, stop listening so we can talk. radio.stopListening(); - // Take the time, and send it. This will block until complete - payload_t ping; - ping.time = millis(); - ping.from_node = node_address; - ping.to_node = 0; // All pings go to the base + // Take the time, and send it to the base. This will block until complete + payload_t ping(node_address,0,millis()); payload_printf("PING",ping); radio.write( &ping, sizeof(payload_t) ); @@ -312,10 +311,7 @@ void loop(void) radio.stopListening(); // Construct the return payload (pong) - payload_t pong; - pong.time = ping.time; - pong.from_node = node_address; - pong.to_node = ping.from_node; + payload_t pong(node_address,ping.from_node,ping.time); // Open the correct pipe for writing radio.openWritingPipe(topology[pong.to_node].listening_pipe); From d8b2512e0c391ef218e941a52dd554d0f288a199 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 12 May 2011 21:46:27 -0700 Subject: [PATCH 040/326] Allow opening pipe 0 for reading. Useful if it's on a different subnet from others --- RF24.cpp | 10 +++++----- RF24.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index b03307a..a6775ba 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -489,16 +489,16 @@ void RF24::openWritingPipe(uint64_t value) void RF24::openReadingPipe(uint8_t child, uint64_t value) { const uint8_t child_pipe[] = { - RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 }; + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 }; const uint8_t child_payload_size[] = { - RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 }; + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 }; const uint8_t child_pipe_enable[] = { - ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 }; + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 }; - if (--child < 5) + if (child < 5) { // For pipes 2-5, only write the LSB - if ( !child ) + if ( child < 2 ) write_register(child_pipe[child], reinterpret_cast(&value), 5); else write_register(child_pipe[child], reinterpret_cast(&value), 1); diff --git a/RF24.h b/RF24.h index 593e80c..c71f644 100644 --- a/RF24.h +++ b/RF24.h @@ -307,7 +307,7 @@ public: * * @see openWritingPipe * - * @warning all 5 reading pipes should share the first 32 bits. + * @warning Pipes 1-5 should share the first 32 bits. * Only the least significant byte should be unique, e.g. * * @code @@ -315,9 +315,9 @@ public: * openReadingPipe(2,0xF0F0F0F066); * @endcode * - * @todo Enforce the restriction that all pipes must share the top 32 bits + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits * - * @param number Which pipe# to open, 1-5. + * @param number Which pipe# to open, 0-5. * @param address The 40-bit address of the pipe to open. */ void openReadingPipe(uint8_t number, uint64_t address); From 2e7ea6709325e7a517ff5dadbdf993e4aa7d302e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 12 May 2011 21:47:16 -0700 Subject: [PATCH 041/326] Added relay role, and tweaked base to work with it --- examples/starping_relay/starping_relay.pde | 132 +++++++++++++++++++-- 1 file changed, 124 insertions(+), 8 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index b6f05d1..28ac8e0 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -67,6 +67,41 @@ const node_info topology[] = }; const short num_nodes = sizeof(topology)/sizeof(node_info); +/** + * Find where to send a message to reach the target node + * + * Given the @p target_node, find the child or parent of + * the @p current_node which will relay messages for the target. + * + * This is needed in a multi-hop system where the @p current_node + * is not adjacent to the @p target_node in the topology + */ +uint8_t find_node( uint8_t current_node, uint8_t target_node ) +{ + uint8_t out_node = target_node; + bool found_target = false; + while ( ! found_target ) + { + if ( topology[out_node].parent_node == current_node ) + { + found_target = true; + } + else + { + out_node = topology[out_node].parent_node; + + // If we've made it all the way back to the base without finding + // common lineage with the to_node, we will just send it to our parent + if ( out_node == 0 || out_node == -1 ) + { + out_node = topology[current_node].parent_node; + found_target = true; + } + } + } + return out_node; +} + // // Role management // @@ -189,24 +224,30 @@ void setup(void) // Open pipes to other nodes for communication // - // First listening pipe is #1 - uint8_t current_pipe = 1; - // Each leaf node has a talking pipe that it will ping into, and a listening // pipe that it will listen for the pong. Relay nodes also do this. if ( role == role_leaf || role == role_relay ) { // Write on our talking pipe radio.openWritingPipe(topology[node_address].talking_pipe); - + } + + // Relay nodes have a special function. They open their listening pipe on pipe + // #0. This will get over-written every time we open a writing pipe. So + // Remember to re-open the reading pipe whenever we start to listen again. + if ( role == role_relay ) + { // Listen on our listening pipe - radio.openReadingPipe(current_pipe++,topology[node_address].listening_pipe); + radio.openReadingPipe(0,topology[node_address].listening_pipe); } // The base and relay nodes listens on all their children node's talking pipes // and sends the pong back on the child node's specific listening pipe. if ( role == role_base || role == role_relay ) { + // First child listening pipe is #1 + uint8_t current_pipe = 1; + // The topology table tells us who our children are int i = num_nodes; while (i--) @@ -234,7 +275,7 @@ void setup(void) if ( role == role_invalid ) { - printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); + printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 0 through 5 to assign an address\n\r"); } } @@ -313,11 +354,16 @@ void loop(void) // Construct the return payload (pong) payload_t pong(node_address,ping.from_node,ping.time); + // Find the correct pipe for writing. We can only talk on one of our + // direct children's listening pipes. If the to_node is further out, + // it will get relayed. + uint8_t out_node = find_node(node_address,pong.to_node); + // Open the correct pipe for writing - radio.openWritingPipe(topology[pong.to_node].listening_pipe); + radio.openWritingPipe(topology[out_node].listening_pipe); // Retain the low 2 bytes to identify the pipe for the spew - uint16_t pipe_id = topology[pong.to_node].listening_pipe & 0xffff; + uint16_t pipe_id = topology[out_node].listening_pipe & 0xffff; // Send the final one back. radio.write( &pong, sizeof(payload_t) ); @@ -328,6 +374,76 @@ void loop(void) radio.startListening(); } } + + // + // Relay role. Forward packets to the appropriate destination + // + + if ( role == role_relay ) + { + // if there is data ready + uint8_t pipe_num; + if ( radio.available(&pipe_num) ) + { + // Dump the payloads until we've gotten everything + payload_t payload; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &payload, sizeof(payload_t) ); + + // Is this for us? + if ( payload.to_node == node_address ) + { + // Treat it as a PONG + payload_printf(" ...PONG",payload); + printf(" Round-trip delay: %lu\n\r",millis()-payload.time); + } + else + { + // Relay it + + // Spew it + payload_printf("RELAY IN",payload); + printf(" on pipe %u. ",pipe_num); + + // Which pipe should we use to get the message to the "to_node"? + // We need to find a node who is OUR CHILD that either IS the to_node + // or has the to_node as one of ITS children. Failing that, we'll just + // send it back to the parent to deal with. + uint8_t out_node = find_node(node_address,payload.to_node); + + // First, stop listening so we can talk + radio.stopListening(); + + // If this node is our child, we talk on it's listening pipe. + uint64_t out_pipe; + if ( topology[out_node].parent_node == node_address ) + out_pipe = topology[out_node].listening_pipe; + + // Otherwise, it's our parent so we talk on OUR talking pipe + else + out_pipe = topology[node_address].talking_pipe; + + // Open the correct pipe for writing. + radio.openWritingPipe(out_pipe); + + // Send the payload back out + radio.write( &payload, sizeof(payload_t) ); + + // Debug spew + uint16_t pipe_id = out_pipe & 0xffff; + printf("OUT on pipe %04x.\n\r",pipe_id); + + } + } + + // Now, resume listening so we catch the next packets. + radio.openReadingPipe(0,topology[node_address].listening_pipe); + radio.startListening(); + } + } // // Listen for serial input, which is how we set the address From 52ab9e0081752725fa2c2aa6b8a823a76c59bc9e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 13 May 2011 15:09:37 -0700 Subject: [PATCH 042/326] Fixed relay writing pipe. Added id to payload. Made Ping timing a consistent gap --- examples/starping_relay/starping_relay.pde | 145 +++++++++++++++------ 1 file changed, 103 insertions(+), 42 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 28ac8e0..14956ac 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -143,16 +143,29 @@ struct payload_t { uint8_t from_node; uint8_t to_node; + uint16_t id; unsigned long time; + + static uint16_t next_id; + payload_t(void) {} - payload_t(uint8_t _from, uint8_t _to, const unsigned long& _time): from_node(_from), to_node(_to), time(_time) {} + payload_t(uint8_t _from, uint8_t _to, const unsigned long& _time): from_node(_from), to_node(_to), id(next_id++), time(_time) {} }; +uint16_t payload_t::next_id; + void payload_printf(const char* name, const payload_t& pl) { - printf("%s Payload from:%u to:%u time:%lu",name,pl.from_node,pl.to_node,pl.time); + printf("%s Payload from:%u to:%u id:%u time:%lu",name,pl.from_node,pl.to_node,pl.id,pl.time); } +// +// Setup/loop shared statics +// + +static unsigned long last_ping_time; +const unsigned long ping_delay = 2000; + void setup(void) { // @@ -230,6 +243,9 @@ void setup(void) { // Write on our talking pipe radio.openWritingPipe(topology[node_address].talking_pipe); + + // Listen on our listening pipe + radio.openReadingPipe(1,topology[node_address].listening_pipe); } // Relay nodes have a special function. They open their listening pipe on pipe @@ -287,43 +303,52 @@ void loop(void) if ( role == role_leaf ) { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it to the base. This will block until complete - payload_t ping(node_address,0,millis()); + // Is it time to ping again? + unsigned long now = millis(); + if ( now - last_ping_time >= ping_delay ) + { + last_ping_time = now; - payload_printf("PING",ping); - radio.write( &ping, sizeof(payload_t) ); + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it to the base. This will block until complete + payload_t ping(node_address,0,millis()); + + printf("%lu ",millis()); + payload_printf("PING",ping); + bool ok = radio.write( &ping, sizeof(payload_t) ); + if (ok) + printf(" ok "); + else + printf(" failed "); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + payload_t pong; + radio.read( &pong, sizeof(payload_t) ); - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); + // Spew it + payload_printf(" ...PONG",pong); + printf(" Round-trip delay: %lu\n\r",millis()-pong.time); + } } - else - { - // Grab the response, compare, and send to debugging spew - payload_t pong; - radio.read( &pong, sizeof(payload_t) ); - - // Spew it - payload_printf(" ...PONG",pong); - printf(" Round-trip delay: %lu\n\r",millis()-pong.time); - } - - // Try again 1s later - delay(1000); } // @@ -345,7 +370,9 @@ void loop(void) done = radio.read( &ping, sizeof(payload_t) ); // Spew it + printf("%lu ",millis()); payload_printf("PING",ping); + printf(" on pipe %u. ",pipe_num); } // First, stop listening so we can talk @@ -366,9 +393,9 @@ void loop(void) uint16_t pipe_id = topology[out_node].listening_pipe & 0xffff; // Send the final one back. - radio.write( &pong, sizeof(payload_t) ); + bool ok = radio.write( &pong, sizeof(payload_t) ); payload_printf(" ...PONG",pong); - printf(" on pipe %04x.\n\r",pipe_id); + printf(" on pipe %04x %s.\n\r",pipe_id,ok?"ok":"failed"); // Now, resume listening so we catch the next packets. radio.startListening(); @@ -381,6 +408,38 @@ void loop(void) if ( role == role_relay ) { +#if 1 + // Relay role is ALSO a ping sender!! + + // Is it time to ping again? + unsigned long now = millis(); + if ( now - last_ping_time >= ping_delay ) + { + last_ping_time = now; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Write on our talking pipe. The relay has to do this every time, because + // we ALSO use pipe 0 as a listening pipe. + radio.openWritingPipe(topology[node_address].talking_pipe); + + // Take the time, and send it to the base. This will block until complete + payload_t ping(node_address,0,millis()); + + printf("%lu ",millis()); + payload_printf(">PING",ping); + bool ok = radio.write( &ping, sizeof(payload_t) ); + if (ok) + printf(" ok.\n\r"); + else + printf(" failed.\n\r"); + + // Now, continue listening + radio.openReadingPipe(0,topology[node_address].listening_pipe); + radio.startListening(); + } +#endif // if there is data ready uint8_t pipe_num; if ( radio.available(&pipe_num) ) @@ -397,7 +456,8 @@ void loop(void) if ( payload.to_node == node_address ) { // Treat it as a PONG - payload_printf(" ...PONG",payload); + printf("%lu ",millis()); + payload_printf(">PONG",payload); printf(" Round-trip delay: %lu\n\r",millis()-payload.time); } else @@ -405,7 +465,8 @@ void loop(void) // Relay it // Spew it - payload_printf("RELAY IN",payload); + printf("%lu ",millis()); + payload_printf("RELAY",payload); printf(" on pipe %u. ",pipe_num); // Which pipe should we use to get the message to the "to_node"? @@ -430,11 +491,11 @@ void loop(void) radio.openWritingPipe(out_pipe); // Send the payload back out - radio.write( &payload, sizeof(payload_t) ); + bool ok = radio.write( &payload, sizeof(payload_t) ); // Debug spew uint16_t pipe_id = out_pipe & 0xffff; - printf("OUT on pipe %04x.\n\r",pipe_id); + printf("OUT on pipe %04x %s.\n\r",pipe_id,ok?"ok":"failed"); } } From 7b7138027893b1e31ba49398e314a64890a908ad Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 13 May 2011 20:16:54 -0700 Subject: [PATCH 043/326] Added self-healing ping offset to leaf node. Made timeout more parallelized --- examples/starping_relay/starping_relay.pde | 81 +++++++++++++--------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 14956ac..6a7b9fb 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -163,8 +163,11 @@ void payload_printf(const char* name, const payload_t& pl) // Setup/loop shared statics // -static unsigned long last_ping_time; -const unsigned long ping_delay = 2000; +static unsigned long last_ping_sent_at; +static bool waiting_for_pong = false; +const unsigned long ping_delay = 2000; // ms +const unsigned long pong_timeout = 250; // ms +const unsigned long ping_phase_shift = 100; // ms void setup(void) { @@ -305,9 +308,10 @@ void loop(void) { // Is it time to ping again? unsigned long now = millis(); - if ( now - last_ping_time >= ping_delay ) + if ( now - last_ping_sent_at >= ping_delay ) { - last_ping_time = now; + last_ping_sent_at = now; + waiting_for_pong = true; // First, stop listening so we can talk. radio.stopListening(); @@ -315,39 +319,54 @@ void loop(void) // Take the time, and send it to the base. This will block until complete payload_t ping(node_address,0,millis()); + // Print details. printf("%lu ",millis()); - payload_printf("PING",ping); + payload_printf(">PING",ping); bool ok = radio.write( &ping, sizeof(payload_t) ); if (ok) - printf(" ok "); + printf(" ok\n\r"); else - printf(" failed "); + printf(" failed\n\r"); // Now, continue listening radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) - timeout = true; - - // Describe the results - if ( timeout ) + } + + // Did we get a pong? + if ( radio.available() ) + { + // Not waiting anymore, got one. + waiting_for_pong = false; + + // Dump the payloads until we've gotten everything + payload_t payload; + boolean done = false; + while (!done) { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - payload_t pong; - radio.read( &pong, sizeof(payload_t) ); - - // Spew it - payload_printf(" ...PONG",pong); - printf(" Round-trip delay: %lu\n\r",millis()-pong.time); - } + // Fetch the payload, and see if this was the last one. + done = radio.read( &payload, sizeof(payload_t) ); + + // Print details. + printf("%lu ",millis()); + payload_printf(">PONG",payload); + printf(" Round-trip delay: %lu\n\r",millis()-payload.time); + } + } + + // Have we timed out waiting for our pong? + if ( waiting_for_pong && ( millis() - last_ping_sent_at > pong_timeout ) ) + { + // Not waiting anymore, timed out. + waiting_for_pong = false; + + // Timeouts usually happen because of collisions with other nodes + // getting a pong just as we are trying to get a ping. The best thing + // to do right now is offset our ping timing to search for a slot + // that's not occupied. + last_ping_sent_at += ping_phase_shift; + + // Print details + printf("TIMED OUT.\n\r"); } } @@ -413,9 +432,9 @@ void loop(void) // Is it time to ping again? unsigned long now = millis(); - if ( now - last_ping_time >= ping_delay ) + if ( now - last_ping_sent_at >= ping_delay ) { - last_ping_time = now; + last_ping_sent_at = now; // First, stop listening so we can talk. radio.stopListening(); From 33edb1f18acee3f45db56ce7785e70febccc6c57 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 13 May 2011 20:18:49 -0700 Subject: [PATCH 044/326] Moved base node code, to make it more readable --- examples/starping_relay/starping_relay.pde | 103 +++++++++++---------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 6a7b9fb..3da684d 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -370,57 +370,6 @@ void loop(void) } } - // - // Base role. Receive each packet, dump it out, and send it back - // - - if ( role == role_base ) - { - // if there is data ready - uint8_t pipe_num; - if ( radio.available(&pipe_num) ) - { - // Dump the payloads until we've gotten everything - payload_t ping; - boolean done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &ping, sizeof(payload_t) ); - - // Spew it - printf("%lu ",millis()); - payload_printf("PING",ping); - printf(" on pipe %u. ",pipe_num); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Construct the return payload (pong) - payload_t pong(node_address,ping.from_node,ping.time); - - // Find the correct pipe for writing. We can only talk on one of our - // direct children's listening pipes. If the to_node is further out, - // it will get relayed. - uint8_t out_node = find_node(node_address,pong.to_node); - - // Open the correct pipe for writing - radio.openWritingPipe(topology[out_node].listening_pipe); - - // Retain the low 2 bytes to identify the pipe for the spew - uint16_t pipe_id = topology[out_node].listening_pipe & 0xffff; - - // Send the final one back. - bool ok = radio.write( &pong, sizeof(payload_t) ); - payload_printf(" ...PONG",pong); - printf(" on pipe %04x %s.\n\r",pipe_id,ok?"ok":"failed"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } - // // Relay role. Forward packets to the appropriate destination // @@ -524,6 +473,58 @@ void loop(void) radio.startListening(); } } + + // + // Base role. Receive each packet, dump it out, and send it back + // + + if ( role == role_base ) + { + // if there is data ready + uint8_t pipe_num; + if ( radio.available(&pipe_num) ) + { + // Dump the payloads until we've gotten everything + payload_t ping; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &ping, sizeof(payload_t) ); + + // Spew it + printf("%lu ",millis()); + payload_printf("PING",ping); + printf(" on pipe %u. ",pipe_num); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Construct the return payload (pong) + payload_t pong(node_address,ping.from_node,ping.time); + + // Find the correct pipe for writing. We can only talk on one of our + // direct children's listening pipes. If the to_node is further out, + // it will get relayed. + uint8_t out_node = find_node(node_address,pong.to_node); + + // Open the correct pipe for writing + radio.openWritingPipe(topology[out_node].listening_pipe); + + // Retain the low 2 bytes to identify the pipe for the spew + uint16_t pipe_id = topology[out_node].listening_pipe & 0xffff; + + // Send the final one back. + bool ok = radio.write( &pong, sizeof(payload_t) ); + payload_printf(" ...PONG",pong); + printf(" on pipe %04x %s.\n\r",pipe_id,ok?"ok":"failed"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } + // // Listen for serial input, which is how we set the address From e86c6b0f15f7171adf57f6aaacdef72cf15a07c0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 13 May 2011 21:00:26 -0700 Subject: [PATCH 045/326] Leaf only starts hunting for a new slot after 3 timeouts. Makes the system more stable. --- examples/starping_relay/starping_relay.pde | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 3da684d..942367c 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -165,9 +165,11 @@ void payload_printf(const char* name, const payload_t& pl) static unsigned long last_ping_sent_at; static bool waiting_for_pong = false; +static short consecutive_timeouts; const unsigned long ping_delay = 2000; // ms const unsigned long pong_timeout = 250; // ms const unsigned long ping_phase_shift = 100; // ms +const short timeout_shift_threshold = 3; void setup(void) { @@ -337,6 +339,7 @@ void loop(void) { // Not waiting anymore, got one. waiting_for_pong = false; + consecutive_timeouts = 0; // Dump the payloads until we've gotten everything payload_t payload; @@ -363,7 +366,11 @@ void loop(void) // getting a pong just as we are trying to get a ping. The best thing // to do right now is offset our ping timing to search for a slot // that's not occupied. - last_ping_sent_at += ping_phase_shift; + // + // Only do this after getting a few timeouts, so we aren't always skittishly + // moving around the cycle. + if ( ++consecutive_timeouts > timeout_shift_threshold ) + last_ping_sent_at += ping_phase_shift; // Print details printf("TIMED OUT.\n\r"); From 1d28d28a29d452d79b42dd94732e550721b727be Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 May 2011 07:21:11 -0700 Subject: [PATCH 046/326] Now caches the reading pipe 0 and re-sets it when starting to listen. --- RF24.cpp | 9 +++++++++ RF24.h | 1 + 2 files changed, 10 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index a6775ba..3718874 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -313,6 +313,9 @@ void RF24::startListening(void) { write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Restore the pipe0 adddress + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); // Flush buffers flush_rx(); @@ -495,6 +498,12 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) const uint8_t child_pipe_enable[] = { ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 }; + // If this is pipe 0, cache the address. This is needed because + // openWritingPipe() will overwrite the pipe 0 address, so + // startListening() will have to restore it. + if (child == 0) + pipe0_reading_address = value; + if (child < 5) { // For pipes 2-5, only write the LSB diff --git a/RF24.h b/RF24.h index c71f644..2939c30 100644 --- a/RF24.h +++ b/RF24.h @@ -23,6 +23,7 @@ private: uint8_t payload_size; /**< Fixed size of payloads */ boolean ack_payload_available; /**< Whether there is an ack payload waiting */ uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ protected: /** From 7e658242e2e22c313ecbb06c55a49be93f50a507 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 May 2011 07:22:11 -0700 Subject: [PATCH 047/326] Refactored common code from leaf/relay nodes into static functions. Improved comments. --- examples/starping_relay/starping_relay.pde | 242 +++++++++++---------- 1 file changed, 123 insertions(+), 119 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 942367c..740afbd 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -9,20 +9,27 @@ /** * Example RF Radio Ping Star Group with Relay * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the - * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will - * respond back with a copy of the value. Each ping unit can get that response - * back, and determine how long the whole cycle took. + * This sketch is a very complex example of using the RF24 library for Arduino. + * Deploy this on any number of nodes to create a basic mesh network. I have + * tested this on 6 nodes, but it should work on many more. * - * This example introduces a new role, the 'relay', which can relay pings or - * pongs from one host to another. This is needed in larger meshes because - * each radio can only listen to 5-6 others. + * There are three different roles a node can be: * - * This example requires a bit more complexity to determine which unit is which. - * The pong receiver is identified by having its role_pin tied to ground. - * The ping senders are further differentiated by a byte in eeprom. + * @li Leaf. Leaf nodes send a ping to the base unit, and wait for a pong in + * return + * + * @li Relay. Relay nodes do the same as a leaf node, AND they relay pings + * from leaf nodes toward the base, and relay pongs toward the leaves. + * + * @li Base. One node is the base station, which receives pings, and sends + * a pong back out. + * + * The address of each node is a number from 1 to n (the # of known nodes). + * It is set in EEPROM. To change a nodes address, send the character code + * for that address. e.g. send the character '5' to set address 5. + * + * The role is determined from the topology table. Leafs have no children. + * The base node has no parent. Relays have parents and children. */ #include @@ -43,17 +50,11 @@ RF24 radio(8,9); // Topology // -// Radio pipe addresses for the nodes to communicate. Only ping nodes need -// dedicated pipes in this topology. Each ping node has a talking pipe -// that it will ping into, and a listening pipe that it will listen for -// the pong. The pong node listens on all the ping node talking pipes -// and sends the pong back on the sending node's specific listening pipe. - struct node_info { uint64_t talking_pipe; // Pipe used to talk to parent node uint64_t listening_pipe; // Pipe used to listen to parent node - uint8_t parent_node; // Number of parent node + uint8_t parent_node; // Address of parent node }; const node_info topology[] = @@ -106,10 +107,8 @@ uint8_t find_node( uint8_t current_node, uint8_t target_node ) // Role management // // Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin +// in this system. Doing so greatly simplifies testing. Role is +// determined by the topology table. // // The various roles supported by this sketch @@ -133,7 +132,7 @@ const uint8_t valid_eeprom_flag = 0xdf; // What is our address (SRAM cache of the address from EEPROM) // This is an index into the topology[] table above -uint8_t node_address = role_invalid;; +uint8_t node_address = -1; // // Payload @@ -171,15 +170,16 @@ const unsigned long pong_timeout = 250; // ms const unsigned long ping_phase_shift = 100; // ms const short timeout_shift_threshold = 3; +// Space to track the last packet we received from each node, useful +// for tracking lost packets +static uint16_t last_id_received[num_nodes]; + void setup(void) { // // Address // - // Unless we find reasonable values in the EEPROM, these are the defaults - node_address = -1; - // Look for the token in EEPROM to indicate the following value is // a validly set node address if ( EEPROM.read(address_at_eeprom_location) == valid_eeprom_flag ) @@ -244,7 +244,7 @@ void setup(void) // Each leaf node has a talking pipe that it will ping into, and a listening // pipe that it will listen for the pong. Relay nodes also do this. - if ( role == role_leaf || role == role_relay ) + if ( role == role_leaf ) { // Write on our talking pipe radio.openWritingPipe(topology[node_address].talking_pipe); @@ -258,11 +258,14 @@ void setup(void) // Remember to re-open the reading pipe whenever we start to listen again. if ( role == role_relay ) { + // Write on our talking pipe + radio.openWritingPipe(topology[node_address].talking_pipe); + // Listen on our listening pipe radio.openReadingPipe(0,topology[node_address].listening_pipe); } - // The base and relay nodes listens on all their children node's talking pipes + // The base and relay nodes listen on all their children node's talking pipes // and sends the pong back on the child node's specific listening pipe. if ( role == role_base || role == role_relay ) { @@ -300,6 +303,10 @@ void setup(void) } } +void ping_if_ready(void); +void handle_pong(const payload_t& payload); +void check_pong_timeout(void); + void loop(void) { // @@ -308,39 +315,12 @@ void loop(void) if ( role == role_leaf ) { - // Is it time to ping again? - unsigned long now = millis(); - if ( now - last_ping_sent_at >= ping_delay ) - { - last_ping_sent_at = now; - waiting_for_pong = true; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it to the base. This will block until complete - payload_t ping(node_address,0,millis()); - - // Print details. - printf("%lu ",millis()); - payload_printf(">PING",ping); - bool ok = radio.write( &ping, sizeof(payload_t) ); - if (ok) - printf(" ok\n\r"); - else - printf(" failed\n\r"); - - // Now, continue listening - radio.startListening(); - } + ping_if_ready(); + check_pong_timeout(); // Did we get a pong? if ( radio.available() ) { - // Not waiting anymore, got one. - waiting_for_pong = false; - consecutive_timeouts = 0; - // Dump the payloads until we've gotten everything payload_t payload; boolean done = false; @@ -349,32 +329,9 @@ void loop(void) // Fetch the payload, and see if this was the last one. done = radio.read( &payload, sizeof(payload_t) ); - // Print details. - printf("%lu ",millis()); - payload_printf(">PONG",payload); - printf(" Round-trip delay: %lu\n\r",millis()-payload.time); + handle_pong(payload); } } - - // Have we timed out waiting for our pong? - if ( waiting_for_pong && ( millis() - last_ping_sent_at > pong_timeout ) ) - { - // Not waiting anymore, timed out. - waiting_for_pong = false; - - // Timeouts usually happen because of collisions with other nodes - // getting a pong just as we are trying to get a ping. The best thing - // to do right now is offset our ping timing to search for a slot - // that's not occupied. - // - // Only do this after getting a few timeouts, so we aren't always skittishly - // moving around the cycle. - if ( ++consecutive_timeouts > timeout_shift_threshold ) - last_ping_sent_at += ping_phase_shift; - - // Print details - printf("TIMED OUT.\n\r"); - } } // @@ -385,35 +342,8 @@ void loop(void) { #if 1 // Relay role is ALSO a ping sender!! - - // Is it time to ping again? - unsigned long now = millis(); - if ( now - last_ping_sent_at >= ping_delay ) - { - last_ping_sent_at = now; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Write on our talking pipe. The relay has to do this every time, because - // we ALSO use pipe 0 as a listening pipe. - radio.openWritingPipe(topology[node_address].talking_pipe); - - // Take the time, and send it to the base. This will block until complete - payload_t ping(node_address,0,millis()); - - printf("%lu ",millis()); - payload_printf(">PING",ping); - bool ok = radio.write( &ping, sizeof(payload_t) ); - if (ok) - printf(" ok.\n\r"); - else - printf(" failed.\n\r"); - - // Now, continue listening - radio.openReadingPipe(0,topology[node_address].listening_pipe); - radio.startListening(); - } + ping_if_ready(); + check_pong_timeout(); #endif // if there is data ready uint8_t pipe_num; @@ -430,10 +360,7 @@ void loop(void) // Is this for us? if ( payload.to_node == node_address ) { - // Treat it as a PONG - printf("%lu ",millis()); - payload_printf(">PONG",payload); - printf(" Round-trip delay: %lu\n\r",millis()-payload.time); + handle_pong(payload); } else { @@ -472,12 +399,10 @@ void loop(void) uint16_t pipe_id = out_pipe & 0xffff; printf("OUT on pipe %04x %s.\n\r",pipe_id,ok?"ok":"failed"); + // Now, resume listening so we catch the next packets. + radio.startListening(); } } - - // Now, resume listening so we catch the next packets. - radio.openReadingPipe(0,topology[node_address].listening_pipe); - radio.startListening(); } } @@ -503,6 +428,17 @@ void loop(void) printf("%lu ",millis()); payload_printf("PING",ping); printf(" on pipe %u. ",pipe_num); + + // Track the packets lost since we last heard from this node + // Packet loss is generally a sign of poor system health + uint16_t* last_id_ptr = &last_id_received[ping.from_node]; + if ( *last_id_ptr && ping.id > *last_id_ptr ) + { + uint16_t lost = ping.id - *last_id_ptr - 1; + if ( lost ) + printf(" lost %u",lost); + } + *last_id_ptr = ping.id; } // First, stop listening so we can talk @@ -552,4 +488,72 @@ void loop(void) } } } + +void ping_if_ready(void) +{ + // Is it time to ping again? + unsigned long now = millis(); + if ( now - last_ping_sent_at >= ping_delay ) + { + last_ping_sent_at = now; + waiting_for_pong = true; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Write on our talking pipe. The relay has to do this every time, because + // we ALSO use pipe 0 as a listening pipe. + radio.openWritingPipe(topology[node_address].talking_pipe); + + // Take the time, and send it to the base. This will block until complete + payload_t ping(node_address,0,millis()); + + // Print details. + printf("%lu ",millis()); + payload_printf(">PING",ping); + bool ok = radio.write( &ping, sizeof(payload_t) ); + if (ok) + printf(" ok\n\r"); + else + printf(" failed\n\r"); + + // Now, continue listening + radio.startListening(); + } +} + +void handle_pong(const payload_t& payload) +{ + // Not waiting anymore, got one. + waiting_for_pong = false; + consecutive_timeouts = 0; + + // Print details. + printf("%lu ",millis()); + payload_printf(">PONG",payload); + printf(" Round-trip delay: %lu\n\r",millis()-payload.time); +} + +void check_pong_timeout(void) +{ + // Have we timed out waiting for our pong? + if ( waiting_for_pong && ( millis() - last_ping_sent_at > pong_timeout ) ) + { + // Not waiting anymore, timed out. + waiting_for_pong = false; + + // Timeouts usually happen because of collisions with other nodes + // getting a pong just as we are trying to send a ping. The best thing + // to do right now is offset our ping timing to search for a slot + // that's not occupied. + // + // Only do this after getting a few timeouts, so we aren't always skittishly + // moving around the cycle. + if ( ++consecutive_timeouts > timeout_shift_threshold ) + last_ping_sent_at += ping_phase_shift; + + // Print details + printf("TIMED OUT.\n\r"); + } +} // vim:ai:cin:sts=2 sw=2 ft=cpp From 77a4c953193c8c18cce7fcd09ff53e12c46d5c41 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 May 2011 07:26:25 -0700 Subject: [PATCH 048/326] Comment bug --- examples/starping_relay/starping_relay.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 740afbd..08caab0 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -24,7 +24,7 @@ * @li Base. One node is the base station, which receives pings, and sends * a pong back out. * - * The address of each node is a number from 1 to n (the # of known nodes). + * The address of each node is a number from 0 to n (the # of known nodes). * It is set in EEPROM. To change a nodes address, send the character code * for that address. e.g. send the character '5' to set address 5. * From 937805d6a2acde22bf685e5fdbc0905c937004f1 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 May 2011 07:32:04 -0700 Subject: [PATCH 049/326] Renamed ping delay to ping interval and added some comments --- examples/starping_relay/starping_relay.pde | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 08caab0..7b394a5 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -11,7 +11,9 @@ * * This sketch is a very complex example of using the RF24 library for Arduino. * Deploy this on any number of nodes to create a basic mesh network. I have - * tested this on 6 nodes, but it should work on many more. + * tested this on 6 nodes, but it should work on many more. Although if there + * are a lot more nodes, increase the ping_interval, or the base will be + * overwhelmed! * * There are three different roles a node can be: * @@ -25,7 +27,7 @@ * a pong back out. * * The address of each node is a number from 0 to n (the # of known nodes). - * It is set in EEPROM. To change a nodes address, send the character code + * It is set in EEPROM. To change a node's address, send the character code * for that address. e.g. send the character '5' to set address 5. * * The role is determined from the topology table. Leafs have no children. @@ -165,7 +167,7 @@ void payload_printf(const char* name, const payload_t& pl) static unsigned long last_ping_sent_at; static bool waiting_for_pong = false; static short consecutive_timeouts; -const unsigned long ping_delay = 2000; // ms +const unsigned long ping_interval = 2000; // ms const unsigned long pong_timeout = 250; // ms const unsigned long ping_phase_shift = 100; // ms const short timeout_shift_threshold = 3; @@ -493,7 +495,7 @@ void ping_if_ready(void) { // Is it time to ping again? unsigned long now = millis(); - if ( now - last_ping_sent_at >= ping_delay ) + if ( now - last_ping_sent_at >= ping_interval ) { last_ping_sent_at = now; waiting_for_pong = true; From 8f8003822760e76bf1e43cd6000031e804de1168 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 21 May 2011 23:24:27 -0700 Subject: [PATCH 050/326] Do clock divider on chip select. Should improve SPI interop with other devices --- RF24.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/RF24.cpp b/RF24.cpp index 3718874..4cad18f 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -23,6 +23,7 @@ void RF24::csn(int mode) { SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV8); digitalWrite(csn_pin,mode); } From badc86cfe0bc247fab6a0bec2874413b9dc7e443 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 21 May 2011 23:25:36 -0700 Subject: [PATCH 051/326] Added extra debugging readability --- examples/starping_relay/starping_relay.pde | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 7b394a5..304917b 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -555,6 +555,7 @@ void check_pong_timeout(void) last_ping_sent_at += ping_phase_shift; // Print details + printf("%lu ",millis()); printf("TIMED OUT.\n\r"); } } From 40eea86ffe6993eecb3ef97652009821f5b316a7 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 25 May 2011 20:46:31 -0700 Subject: [PATCH 052/326] Avoid spurious warnings --- RF24.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index 4cad18f..db39bea 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -18,6 +18,12 @@ #define IF_SERIAL_DEBUG(x) #endif +// Avoid spurious warnings +#undef PROGMEM +#define PROGMEM __attribute__(( section(".progmem.data") )) +#undef PSTR +#define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) + /******************************************************************/ void RF24::csn(int mode) From 1062f9d879c6c6a1ca77c315b7a9bc39a31cd9bd Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 25 May 2011 20:47:16 -0700 Subject: [PATCH 053/326] Environment-specific changes for me. Testing out latest arduino from git, and added a 3rd upload option --- examples/starping_relay/makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/examples/starping_relay/makefile b/examples/starping_relay/makefile index 2ea7538..15cb43b 100644 --- a/examples/starping_relay/makefile +++ b/examples/starping_relay/makefile @@ -64,8 +64,9 @@ ARDUINO_VERSION = 21 ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs +PORT = /dev/tty.usbserial-A600eHIs PORT2 = /dev/tty.usbserial-A9007LmI +PORT3 = /dev/tty.usbserial-A40081RP else ARDUINO_VERSION = 22 ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) @@ -75,6 +76,12 @@ PORT = /dev/ttyUSB0 PORT2 = /dev/ttyUSB1 endif +# Temporary testing of github Arduino environment +OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc +ARDUINO_DIR = /opt/Arduino + PROJECT_NAME = $(notdir $(PWD)) PROJECT_DIR = . ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino @@ -217,6 +224,9 @@ upload1: output/$(PROJECT_NAME).hex upload2: output/$(PROJECT_NAME).hex $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) +upload3: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) + sym: $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym From 1c65c7f181f8e6040e3f37d4302574eb5ebea147 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 25 May 2011 20:52:01 -0700 Subject: [PATCH 054/326] Revised documentation --- RF24.h | 154 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 89 insertions(+), 65 deletions(-) diff --git a/RF24.h b/RF24.h index 2082ebc..c28324a 100644 --- a/RF24.h +++ b/RF24.h @@ -29,7 +29,9 @@ protected: /** * @name Low-level internal interface. * - * Protected methods that address the chip directly. + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. */ /**@{*/ @@ -116,14 +118,14 @@ protected: * @return Payload length of last-received dynamic payload */ uint8_t read_payload_length(void); - + /** * Empty the receive buffer * * @return Current value of status register */ uint8_t flush_rx(void); - + /** * Empty the transmit buffer * @@ -166,6 +168,13 @@ protected: /**@}*/ public: + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ + /** * Constructor * @@ -184,69 +193,28 @@ public: */ void begin(void); - /** - * Set RF communication channel - * - * @param channel Which RF channel to communicate on, 0-127 - */ - void setChannel(uint8_t channel); - - /** - * Set Payload Size - * - * This implementation uses a pre-stablished fixed payload size for all - * transmissions. If this method is never called, the driver will always - * transmit the maximum payload size (32 bytes), no matter how much - * was sent to write(). - * - * @todo Implement variable-sized payloads feature - * - * @param size The number of bytes in the payload - */ - void setPayloadSize(uint8_t size); - - /** - * Get Payload Size - * - * @see setPayloadSize() - * - * @return The number of bytes in the payload - */ - uint8_t getPayloadSize(void) ; - - /** - * Print a giant block of debugging information to stdout - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void printDetails(void) ; - /** * Start listening on the pipes opened for reading. * - * Be sure to open some pipes for reading first. Do not call 'write' - * while in this mode, without first calling 'stopListening'. + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. */ void startListening(void); /** * Stop listening for incoming messages * - * Necessary to do this before writing. + * Do this before calling write(). */ void stopListening(void); - /** - * Enter low-power mode - * - * To return to normal power mode, either write() some data or - * startListening(). - */ - void powerDown(void); - /** * Write to the open writing pipe * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * * This blocks until the message is successfully acknowledged by * the receiver or the timeout/retransmit maxima are reached. In * the current configuration, the max delay here is 60ms. @@ -268,14 +236,6 @@ public: */ boolean available(void) ; - /** - * Test whether there are bytes available to be read - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - boolean available(uint8_t* pipe_num); - /** * Read the payload * @@ -335,12 +295,77 @@ public: */ void openReadingPipe(uint8_t number, uint64_t address); + /**@}*/ + /** + * @name Optional public interface + * + * Methods you may want to use but are not needed for regular operation + */ + /**@{*/ + + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); + + /** + * Set Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); + + /** + * Get Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void) ; + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void) ; + + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening(). + */ + void powerDown(void); + + /** + * Test whether there are bytes available to be read + * + * Use this version to discover on which pipe the message + * arrived. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + boolean available(uint8_t* pipe_num); + /** * Enable custom payloads on the acknowledge packets * * Ack payloads are a handy way to return data back to senders without - * manually changing the radio modes on both units. See the - * pingpair_pl.pde example. + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde */ void enableAckPayload(void); @@ -374,6 +399,8 @@ public: * @return True if an ack payload is available. */ boolean isAckPayloadAvailable(void); + + /**@}*/ }; /** @@ -394,10 +421,7 @@ public: * unit will send out the value of millis() once a second. The pong unit will * respond back with a copy of the value. Each ping unit can get that response * back, and determine how long the whole cycle took. - Warning: - Calling this function clears the internal flag which indicates a payload is available. If it returns true, you must read the packet out as the very next interaction with the radio, or the results are undefined. - -* + * * This example requires a bit more complexity to determine which unit is which. * The pong receiver is identified by having its role_pin tied to ground. * The ping senders are further differentiated by a byte in eeprom. From f3369a552cd49d8e57cbc1e11a48a985082026ef Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 25 May 2011 21:02:11 -0700 Subject: [PATCH 055/326] Updating docs --- RF24.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/RF24.h b/RF24.h index c28324a..2bc97c3 100644 --- a/RF24.h +++ b/RF24.h @@ -12,7 +12,7 @@ #include /** - * Driver for nRF24L01 2.4GHz Wireless Transceiver + * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver */ class RF24 @@ -446,7 +446,17 @@ public: */ /** - * @mainpage Driver for nRF24L01 2.4GHz Wireless Transceiver + * @example starping_relay.pde + * + * This sketch is a very complex example of using the RF24 library for Arduino. + * Deploy this on any number of nodes to create a basic mesh network. I have + * tested this on 6 nodes, but it should work on many more. 'Leaf' nodes attempt + * to send a ping every 2 seconds to the 'Base' node. Optional 'Relay' nodes + * will relay these transmissions. + */ + +/** + * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver * * Design Goals: This library is designed to be... * @li Maximally compliant with the intended operation of the chip From bee284f9c5478880a78c019dd1f565e4fc1ffd2c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 25 May 2011 22:22:21 -0700 Subject: [PATCH 056/326] Added carrier detect and auto-ack disable, and an example to use it --- RF24.cpp | 19 +- RF24.h | 21 ++ examples/scanner/makefile | 305 ++++++++++++++++++++++++++ examples/scanner/output/core.a | Bin 0 -> 187630 bytes examples/scanner/output/scanner.cpp | 128 +++++++++++ examples/scanner/output/scanner.elf | Bin 0 -> 100436 bytes examples/scanner/output/scanner.hex | 325 ++++++++++++++++++++++++++++ examples/scanner/printf.h | 33 +++ examples/scanner/scanner.pde | 125 +++++++++++ 9 files changed, 955 insertions(+), 1 deletion(-) create mode 100644 examples/scanner/makefile create mode 100644 examples/scanner/output/core.a create mode 100644 examples/scanner/output/scanner.cpp create mode 100755 examples/scanner/output/scanner.elf create mode 100644 examples/scanner/output/scanner.hex create mode 100644 examples/scanner/printf.h create mode 100644 examples/scanner/scanner.pde diff --git a/RF24.cpp b/RF24.cpp index db39bea..e0fe3c0 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -331,7 +331,7 @@ void RF24::startListening(void) ce(HIGH); // wait for the radio to come up (130us actually only needed) - delay(1); + delayMicroseconds(130); } /******************************************************************/ @@ -591,5 +591,22 @@ boolean RF24::isAckPayloadAvailable(void) return result; } +/******************************************************************/ + +void RF24::setAutoAck(bool enable) +{ + if ( enable ) + write_register(EN_AA, B111111); + else + write_register(EN_AA, 0); +} + +/******************************************************************/ + +boolean RF24::testCarrier(void) +{ + return ( read_register(CD) & 1 ); +} + // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 2bc97c3..6c8fffd 100644 --- a/RF24.h +++ b/RF24.h @@ -399,6 +399,27 @@ public: * @return True if an ack payload is available. */ boolean isAckPayloadAvailable(void); + + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); + + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + + boolean testCarrier(void); /**@}*/ }; diff --git a/examples/scanner/makefile b/examples/scanner/makefile new file mode 100644 index 0000000..15cb43b --- /dev/null +++ b/examples/scanner/makefile @@ -0,0 +1,305 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +PORT3 = /dev/tty.usbserial-A40081RP +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +# Temporary testing of github Arduino environment +OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc +ARDUINO_DIR = /opt/Arduino + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: upload1 upload2 + +upload1: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + +upload2: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +upload3: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/scanner/output/core.a b/examples/scanner/output/core.a new file mode 100644 index 0000000000000000000000000000000000000000..6ae105d90023b9ce818bcb755ee9245f24c98185 GIT binary patch literal 187630 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 NkD<2iO@I%8{}ZTDOPT-x literal 0 HcmV?d00001 diff --git a/examples/scanner/output/scanner.cpp b/examples/scanner/output/scanner.cpp new file mode 100644 index 0000000..5eb1627 --- /dev/null +++ b/examples/scanner/output/scanner.cpp @@ -0,0 +1,128 @@ +#include +#line 1 "scanner.pde" + +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// +// Channel info +// + +const short num_channels = 128; +short values[num_channels]; + +// +// Setup +// + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/scanner/\n\r"); + + // + // Setup and configure rf radio + // + + radio.begin(); + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + // Print out header, high then low digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n\r"); + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n\r"); +} + +// +// Loop +// + +const short num_reps = 100; + +void loop(void) +{ + // Clear measurement values + memset(values,0,num_channels); + + // Scan all channels num_reps times + int rep_counter = num_reps; + while (rep_counter--) + { + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(128); + radio.stopListening(); + + // Did we get a carrier? + if ( radio.testCarrier() ) + ++values[i]; + } + } + + // Print out channel measurements, clamped to a single hex digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,values[i]&0xf)); + ++i; + } + printf("\n\r"); +} + + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/scanner/output/scanner.elf b/examples/scanner/output/scanner.elf new file mode 100755 index 0000000000000000000000000000000000000000..5e0f4b153bcd3684fc9008dac0bf38d4a5fbfd97 GIT binary patch literal 100436 zcmeEv31C&l)&HIQ*6ce81V};xge?h+fQS-YP!t9CrD_NeAR0nU0-{w(2q-FILK1Mn zqFt<7_qBC>w${2-#kH>WVXdN7h!{Yv!2kD~<=y*UKxx1B-}d`@;oX_D%$YN1&YYP$ zcizj=Bc@FeLRjXqtrIN+jRA5-m9SPI9b@HN9;?_&wUQ*g!xTWeBSU?B$g?azS7b8N zBxw_n<;PEy{yYWx1km~6^$-Y#p4c(lO6<7ufAG25r^*-lze^75EwTn%Lz%G#qIJV$ zuFx7}4Y9oXvgPHGm$1FT)co=$m8v_61S}isd%SFbyjr~n1=5Q09)D@;o8dn`@xl{n z;TyyEgg@H)$pUiJf%Ia{iXKz+gFLd2y5NQ?#it5e7(E#!)4*|q2_RF zxHeoDPVRW#{#L99{k$)lHn+6ouRE{n)ysZ;Su^rYZ6DjMZJX`OhZS^pwQsh=?H}7B zE6w+8SK4LPWi8FiW|HR<@O%oM^T6{dcs{Y4+WrFEXS&>H!2QL3R=nT#Qd?eIN?Wk= z#_-+Yt)F*=6FYt%w_6;rwzBQKHY4XQv}KExzxD%AH^=<~oF8r_=c92G#V~P(_*P^u z>$R+(063tFGXSVHc_~1MO)#X=C)F?vM{du-X~X# zIhXI&e(9RhJw;oJt}WWLX;0S1uIootcdZ{)+}%3r^se?%ZQaSC z8%9m-Y8_qOJvm$yIw8D$=<@JaLyJS#gufm-FcjXjd+31B&Tof=E-!65@rts#?j5^* zp=;K+4o@q(COIp#d3_kU&B=~LI`Vs2g@$I=!mVekiG!R-pws-f8&D)3Cp=-uY z?WWG1(76pdUt1X0^~;woFI{m`OIc1=(+MqQ%M-CDcB4WLNvR z3%c45i0e8flpoqInwl?fIaKph3K32Xrvn>mdbw%Gx5`d-sCqqHj zeL&{-a()6%>iXsGK&Vfs=>ucEPdiqRXblImdk1d$GC8zE?EH2`+s@t1Z3~6r*p&Kd zhnSICPOrIO=cl2Lv1wh`j;-Flb9WH6<~NUQKD7D7=H>9kc-e}!=Jn$6!Q*z7cb-(b z4A@(ef7O4(M_&&(bx&MqMrV0vS!Y4#r0LVAS4DZ+@-88^Unp-x@1+~YR(7?H%kRDh*1A2E5^5cHVAnO{;<|1Qc|*nEiVvk$ zX70+InZKzFqbYM&FXTprv-eQLna$@lFKcdZru_*wHMccCDw?2~vE)s#q^kSskH0GU zV!)2w38AyMZXfVg=J@Vz*lXLjzR=ZMz8YYM`i&5w-XpqqCxv#4&y&9B*VNoJx_9@r zTfQC;4CPg9gXefcSC6=KNnBT6MaqaTzs(GF?M?~x&r0pOVR%C5hT%U-Z0pm1gx_WV zyo&x=p)bAU>E6^wgtioU_oRfH{$#9g@97s37$++~ZVe}gR{ps)oDga%y>v-im)K*6 zuKWNF^1;q;uZr|1WJk(g*YQ?n=*zb|zR>V z+I*?qv%qm<+gAJTwl4c~5e|2S%h3-7p_R?oHeZ^(E>PWlMf3H|W$Su(wYMy5@rGL4 zyX>a+t@ds)CNw|$g24Fh$yvwydUq|)?hFLGZx_1-`PYk2ME<%@c93@K7te`HbJrz! z*9E&DmNg6wUDdX}Eo~jkeBC`Kdr_ddJAEy28S8r1v`dUyJ1wMS>=K((zRs&`Kd;?h zcUAlP_VUm!LEl*~-V&+nZrah*_Bo>B7DUC#-A!$UqP49^w6<@7#-H2eZ3D%Own2hA zP3}JSp|c-qYAXQ80C80A=?VELS%#9&iDkL%32b>&*Du6z6&2H|<&3UFQ%=mA0ZHYM z^tF&0glMgoq+NrZw5hc&u6su4GcjOY?y^41j8#+D#eG4W-k$wfAo#^2Ew{Do76ol1 z+dfG7GVjjpCju9AD{B;|9GF*9F=_gNEyXRy8rQd6(L#OC>)r^vhhX>okfGgy9!~3e zSG@YtH{#WnU$+>UFJ`}wUSt0miQkBkE%`0%7xp3Nh+yZp!q-|kqpn<}zPYohv$b<| z%7(mP=et6L7PL>EF{3=ZlRT26HJZnG9cR2No16FKhL-I~3Y9cZYF^cRLvwNVfq^;Q zo14FG7VTfRY;N&~Hm~@4MN?^9=jFpjcCT*U&@9$&Xj$E2hf=oYZGFb&mEVZl1{aqd z|3&`D<}WsPENrh`T3fMn`qk4nOuuXTJ>f$(-XEU0abC)jyvy^h%e$%H{FDpwuE=Za zcT>M}QqJqw+>g2Zjd9)cJ0`Ua4G-FoR#euz?5vUPf86ns2zE{>J+iL+tjX>7g;O>@ zyK~1&Q%a`;K6-Rc*VMB{?kYcFa=X3p)}5cebbt8f&UKxab)Mfjuk+!~XLnlbDt+hq zf}Pik+eLhdzvI--BRj`+4wkf8DGhnc^E&fx>=*33;!kbi`QfREk&6Ps&OK|4jhZ%p z{NWej_eH-oS9E@|)B8~>MizM&j!5p<7GN&9V|lP?Gh#=_AAAbFNaLpjR~8QGVn2$t z*S4%|8L;k~jwiPMwb$1>811)iO_|g`Fr@3ZAHEW1RDA8F-+lOU*b1kGt&P^kJ05!c zq4c$V+mqYV)?MCqZQIp#t#z4e*SD{3&sdk-@s^d+s{Y*@{@s{&0wZ@?8(YF{;m;r1 z{m`10<`%};PF`_>;NpBG?FN&gYC;DS(=cb(Bg0)8b(D;{qllP)KC6R}g zpd`_f4oS4Z-esJ=7a1&X+OOIhEM;qFmL_);2hs)ycBOVcBh*?eZ%xXYlm}DRq!pF6 zPinuqeM9?Q?N78fwHd8U7vv@Y4y4s1KH?ZCDlZ9ma=SKEfRtJ@~Em9|0S$5UGK zT5~$Zx&ymbrd*rHkG)Q;`vPNmNyqXBmd#xL*zyOOabJP_3f!A<-||scxT(2$W>d?i z&0XQtWldOfHnk{B7#L0a3vzdkLfdyW*8Bq@G3jbi&^zxRn z_LuGa_Ko(Qwym<=(_>nG*P18RyuW7a8fOH4CN30Liua1j%bK^G7HA63$7e|(t?Rv_ zS3BCmx0c*9obls=U^?~3%bpSOLB znKOLap6Q$exPB_mJurb*d=|0K;n;HOp6Of_7&j~Gh2e4SANhIZf$=ZW8*cu>CbDB{{!-G*egD3 zMgA@1-?W#@{5#0MZ8ypMd&s|QH_QC{$p6v4ROUA!AGVi=o7z9HTX6rAeHrc_+ADDX zv)zpQM>aK^*)qAM>4Rl08OsPYH8Wbh5HwMJmDo|z1WZ-j3f#|$`${x7Gn&5;R6P^y zRL_H-2^ABU1)8_4ek|$!!0ufy^?%?+Q7L06u%#-x^yFXmJ^Uv2ekOXAdP_|$dFQ3h z>K~~mS?5;orA9s-&MoZSRT-ZCCp%P$_?_JGG-jIcLOtMHzkzw6Of<+)Cad%5Hp-B&+w^;RQm@Aj&OeuIO}bt)>a!qaL~ zb0u&;53(;z&h**Tc0#5E&i#sK5 z3#dp>+pj{;9s``a#4X|&tQ4OS=Y$7>@{KHeXW%QX*$csCdWKh0jw)p=xb&0_)4vr( zZAYJ4G5s5{ylrLSlUBUX$O;&1X`2$yD!|9HQvkBV_c|U?VEzpOSE=1hetdk;yULy&n>)Zr!(`qy>ONIq4-oJhW;QH zv|TJ>OBSrlU)L5X$ru^zTv^z=d({I?v+U3+v|-g_!Ory$v@gA8*m~rKKQO*@blvc! zqlQgtUqF87^;ln1}+ z_~R2PkH7FlT*uIr2d+G7WySK^h`(na@!hJG^S%m5f{o($`3fS8yuQE^;JTR)41Y zIvlBObpDA433+Yn6x5>Rj89bc8kQPKpMX+| zJ_YnynmE&y-U9qK4WEScx+KdQfb>zMV~}`n_0}iHH))b9@QKw9X<6~QshZwsZ2fk^*U5E5;GT^#dCG!4z+-*yg z82+QvRl6@lO8t1DN_iQ(vTt}9KV<*%DpHA6qf_=PFJo_1BClRL9i>y#K7)@<$7;Uu zI?d7bn|?R^B&+dsez|E6arBE{qCNO;J%=0Z{Tfp0y#uLfCqO+8hM{Y{1m$J=gMb<* z??%y2Lcf?xkE5yX^4(fr@+kT{$GYjK^`QUZcsG5f5gsXj3cAA4uP6QWQ{41Ndz60{ zO>)ZL(u4lXGu`xUJ?M|0?WX^z2mSR5H~qFA?YkeL!>PZkM|oeBn|^N(`s4Eqy$@tx z(}RBVIfi~O=tFzZPl1a%^>cifOZAVx*3jQY`G{TvTUm zmAU%r%9@J0MUAtq4wS2}oKwBfdKtOJl{Jg1P#dnxrD%$siyCeNR8g_GYIfto`ihZb zt@i*gsIIB0Zm@cx%?qk$*Dq|arXV|~s;2S+>vNMkR#K{FFRY!@U}Zy6b!~N{H4&M{ z>IGHxWfcn-SJlt0S$KZM?1hVJ8?6%^ik6R>tZaPLeDfi4jrc66M6-B}MoqTGsU{5L zTnhs_dXBd?0Y0j-e$M%o^;IWUvDp(QM8np6SR@+NgM~(mWl@Hm0{g^Ls%qz0)qBBL zF0QPushm|)Wv$u^wXUk_Tbi|wCaY&s@m$>dDbZr{OpEW>xu{@?cwNQ6=S)i$~P{YH?O9uVr~^G zsfXuZ5~0xS6?Lky6*X10^BU({A4aHXZ-v%u1SSY2Om+y{us8-)zr=bXf^rMCn&G#q zu+-61XiZ9t#@AyQqm}j66A=RXV)CNKg=m(wJ%V(6>=-Palw=2YUIbUS@cgR!!xx@k zYpsjmAn>sHT!YqF&qt6-tw{B!Bpay<>#Ax`g`dIhGpg(0(-DenV5G#Z2)?nZq4BWF z`ub`#qYw)mrCn7mhlHzhrbbB7g5HeaS@j9%9#eHamK;hLOIWW)K$N`I~nP!=T$dW*3cYP zmW`365mZtZzh4F^uA3G^_%Z1884S-8Ca4-Njzv7~AY>b?u2{q5AU zDF$ujqbJ>b){Iz0Q!L_-v4~`JVU&zRVh}a8)=zq(KIn-mM<2Q+q2P~tqF(HY@}N&+ z3Le@Mbz@J|?ikb*1g}QxFIeBY#aX%N7B>Ql@mh_KZE<}#1M|`V^x}y#rXE&P+0Zay z0>eDlwhjgZ=+NrM>GgBa5og3w4zFyiln!%AEakA8g|p8+yn1mp%l$Npsyy{z)QwuG zkaa6Qw)MM1;bY+Z4WPP(^^Fyc3o91D8!KiUd93x03t3%@SzcnlbYT}RYBcCvNYUO} z0hgvADUgCVwZ_n9RX0{FsBAb_iae$#T~6wfi0Z67d~A!$<9m=>iBCnv-0CHZY8x8n zHdF&tt9{$z3i(0|tf?3~6%`GOW;M(mZM`Xh$~kig@XEyps{j~c1$Z+gdBLKZXfhYe z--7fsj8;)Gr+Tp=_mQQ!R$dCySJ1tpVqR6l+))&5DJoaW4z;GyQ2Fd)TU;jxP&Ax zICa2WrWR!ZuBWp>Z%2uWirF<6HdL!dC^DDR??XhPRHCkO_PG^G$9}Q~uBML%>G!gR zMYS%50g{2s=~OVZD;4TyFQ~)3RNvU3#i*uc%~Q_UYLuvGtb}A-T1n8?KtdTlbLUho zX3&=89NcYd2D<+gkPefTR4-ITdrKPE<97o;4WBx>jFknITrS3=i4`+kWUj>zNB5tF zvWw@sN-NP^ic{kw@R?m%gOMl8{t$QDlIw6Zyc5s@MdZWd-1@32>uO{w;QB0rq@Jtl zo7}khRh4yI64WOBpdwn|qd2Ng=%V!J!moSR|J(TlYD4!Zc@C#sU zZ-A|}gT612_)~H7G@maq0ouubA4ZyY{xoF0_SM7-|2HTXm;gnSecNcCAeNPu@5{?c zEwtjUKdg9Bm3iA0xnFPHFSXNm+zXtsYXtU)Dh*6Mf7X}ls z#63u-_fnwnq>qR8{%i%>p7i->nm^|fNJ7&*>4(5l z{@#k}^`vj6s6I84>hoj>u1NjEZlKM@hfpDt6^bOb!UDC*@{is_W+70}P0SKi6m{^6 zkT0c+5e^3tNokOj#GIvhk7xr%@lKEC1xE&@O@9q4D2^XSb0kd%#YmI9rv)O?ejLNA zE4_r4n9TsLL_X`V9YO^)+X7mswgE)N*5$CVR}I@@6!9-%*D1Cn*i0D&+Rp^wU;1!F z#!xr0f_hv!FH-VIH}O^yuX-AHC3cQO>?K+$cJ1$Rx5csuL21-Wl)wP~4RKK47B}{w zCZRt6&ySbXUqz^h(DW?-BjOJcoRp`8<_NAR{J;JSiixce4l75TLj3d3!w4b(qHcMP z2!P4|$`ss1o`df#7})$D-2)})isFa}Ey=NcZ_&usfBq_DORmZYm(^SRg|uBIC@pE1 zMQBM*xAehjHE^Ygb)br$IV?3OW^rEy)MBv;qQo-}m8MS8EQPeSR|8e3-?TMlw!hxcET=kzUzJwBDf}2oHlbt2_tSe4*2)W7{ z^YPk4H+fNnCWynaOUMFC$D*sb~8CCcy58{pyih#wY=0a!2uH!gAqunSI?+z5Cm!R@5N za?nJD3|Gh%Ra>Hwz`$vcuo@p?W-0O@F%=^0zW1YLd?UfP5fC##l+$P<2=c`QP~2D< zjDm-;AZJ0VSlr68S;f#=N&_R`Qh<2~X2Kz}L1VxtZ3w2hEC&ALSaRr{xQSs8;vPJN zyh040TxJDEGvFr=pFGkEjFGW__~cPm-~eV*hff}D1;#R)Hhl6JD=?1P^x=~aumaVNKKc}999|w%WN3!s4TKrTNo@32o^R3_D?iGpT8H<~_#v{Y1qUUoD=X3kQ zalqPYKKH8G-;<9*_C+&DP|E_n9*Car?Vz0G^_=b#ucA9J!Jds`;x^p;3oLOQp+F8b znLeDjMxC*O;%)Me5bPhmDMBA{UoZ}(%2;YHfw`6#Nd=eVW5tWlS)^Q6K?E-p;~pZ5 zO=htl?n7WoQv!5-eCCn$RWoxVXcr*^r->?9 z8(oNtM_K3&Qz#@0LCtTd=J|r=<4ZQE$xYG>?0AXA>^}eec2SL$c&2!foVN&a@pT(G zQ>2#Z)(0E5&% zfZ5al2B|%k*|Y!#sXdO_^Z*8_J)YT&00yajAhVeP3{ranvsu==*#EPwMyHMIfiPDq zG8aJ|`)9np!EoYHBgG6MwSe}hm9y=voISd^9Ach}WI?rv_NYa)M-3_z*A_e+AnIp(NP=P^D<`{y5 z6d3Qx+)d`e3QY85E}^Jmw!|(>_GErV>JSB{crt^e4pm^PC-Vmchbb`4lewMXa0RA& zvV;u&!UN9_q(L_k%*Gw>CM5nILEqN|wj@X$_Ijhl;9hQs7hJH3Umrv}pX-RLMggqa z_=G1usVA`|Ld4pN>n_oC5VWQB(eIuSFuC}-n`bPjV*9Bmt9g8q=Xo~|*JxsQy2Im@ zJX;-}K4KMXj~A~i2lGpYRCJ?iI95?yDvBJ@hxOU1mSoUYVUsbczCc;qpGz;GoidoA z7i0-igs7mn0iX!1#TDxO2r5_%f^M_FU!|+fbS80qgwd=y`@iGxcqPvhZXVjjpLdDF z4m0Tw!*lclsyB`pV%qG|@(BCd0g4sNg zLP>{u&ITrbHgX&(1rN}$htePeuvj^qy<`=JaPt*<76Du2K`Ca0mB_(dI1RG=xt=yd zV+J%DZXkVB6rnq1DQ1MK;2u^`u$BtkL;oyzi6&QBuEuosfJiof(VTjNy+9UYFBW_) zi^*)9gFQ2n&0jQX35v-cPvl@3#Hlg{OE0UF9*B zv_>)vvKY??p(x*pIz=~1=?sJ<^XL#*CPuMr2Fo}I%@NZePRzB&-$oB5FOLv`pD$An z9D*JvZZke&pcqmPc*q#s4x}f#DgT4f0tF`UAqEx50PXYOl&*DK;p0LCiJ zf@boSv0lU7j|VIF&frQ6gLB~!dtJx<19t}fU%*)2YMRb$4`QF~CjjM4>K}|HtM9e@ z0LKY`oCwBmrg7~e?*Ar;)A)2a=vmw^2ZspbmdKoyBrYRq=qV^JlEf4)kB9L%4gk)@ z_J|DxlI7fMmoySc5j+WCmp#E6hC#kuC|N0D5Jin#O;LwN(5Ye@(W7jl6{i*dB1B_9 zP+%0NMehfuMehfuMehfuMehfuMehfuMehfuN9_l`4UJ@lLhf2xpgQ}3J7b8MJ@x|& zp|LK`K1k$#oobEdb?pZp5zUL|irD?Y^P)KtZ=guZHN399HU9Y=jp=KBBese&ELuLShmIGSTl*a>6=joJ_F!%!jyiLoe| z%gLV^(RTm?+#o)yV#@)Te!oR-oaY2&`@VxvX^)c;w-4P_yef|axk?|;$oeX~uJ0zs zRhhN=v$VM|Lvj~3?`+&s(Ezy?6La(VvP#TRyqrmSuok>LQ6zcC_v9_0FNjSp$#GZ- z42U#lCW;jDMUvP=l`vTs&_BdxSBa9S5-U-{Dq`n|V=x5O1ub|A$&q^_=mW-p^`nV&ce`$eqJyMA^`k#MDJ$iO z5IoeUE)?PsE2q#~;2OG+Eu1KKuLasajIRv=bCBc_=G#C`KNax6b>u(9=KQmW@Fv2C z%JO#r9)1vneg&eKY-gdiAycTz2x3#&gu`uxv5FX?1-I4I1?_mu%h4_bX(cRYF44!H z7m-*TMahjwg9hdEL=Q)lT5sTLhD|@DNEd86ocwK zZst@{2%)9Yb?&0;90RZ{O5b~pAWn?pMs_I=sY4JCsX2nqjtKfO1zlMK5OXVtnss-m#sTP8=1q^M3B+iFE-k9p(jpfC-Q3v>`0Vw zw9iK!BXNumJgo94fGW~Sa4tZl{SQc84p3U}W{A3*-8}Qw==zC#n1o7Qe-URuagxo- z`Mn1yz;ld_0_z}i=w-Npi8J9{$XZ35x5a~Kp}J6tCmo$qFR;`|mU>Xo6}BTgP>zsa z%3;_Kb*N$auwa;94A?5-DoNbsmddk|j?~K~7Z(%w9T#0SHRkC_hwCPC6>zyD-e!~d zH4ZTGG^NAzD=-cE6#XgQo(A@pWf?BZ#Z>b1s~#wG3Zk=9W-FLfUrc40YzS12HI=?{ z06{r4B=szSs%wrR^A7>auE~Vte0;=lheNpPg)#K_I{M2D(=A-?iI3flp=s_Y%2;gvmxtE|bLBHf!UzTu>v=lsYxmL4Z}ng`6mWZt4OTkC!@JE6G*BM=UYI z#l*9w4%6LWQp%5@wr>)YfoTLCYzIMUeM#+)qRRTEWM=$~4lAR64U}p{^uQcrn#R?% ze4DK_g3>(U>gY2a0*3yE9`vPj>B|Vq9{E!C$UA@!Fs(TVMF%eCcu1C0=S0BP*lL#E zF7C}Viu5F)keb(D7Yy206PC7lU26Js zz~f}UzbOab*dti$dy;0a5>^4%@nWmd9W9@QgPD%}hf#jKeADp-2`HjWy^W^+0L0SU zsPuL@@Byl2RQKk{GD;zK?utQtl^$uN;`nQJ3}ScR7{s1}2;xAw;q|86oc`L z@)%4&91(+w7iYv^l0-!eCRxmh!K8`m7)++9jlpD#mKaPQaZL=SpSU4{u_n;0^)U27 zym_rSJ5a=@S1~|v%<)@g=pr7ZcDm>)Am#eutQ4GgeGspluu?kO2*An-XDcThgHbH% zjT5TLqCtYkkhPLN+9=@?_aa;@5NlAeYw;}GZJZZRhAxsd;T90Mo|m>Pw}Mn!BKf2s zkwdKDZw!(yE0HNy@J)lH+v=_eer}L-U5T`f8w`?uB$4S>a3%7JLjPfmkrba5{4pt!+eCRh<0s%r zo+yuJFq<+_9?xJlb)r0;!ED+@c|3#J^ojC#2D2Fxe)#?nvV)^Rw8ZHfNmsfGW=%ZDuqin$3mfNhC<(h{d9qI3bX338b>fQ6GN z>`Z)k#5nOGR*-)LSdzz(TNV_vtR!;G!DnKGtAJc7l1s{LM%7l*eDEy6rxtw4Qrckh z`D@$7!gisAD$F_U;#{0lNV*8ztMIuJB~s)mj-ga>1I4A1z?LRULMaJcKd0HZf;*jH zP#j7{FBW9u*Gpj(z8XNr%7UQ;N23<(ms@X z3qE7e^U6M#IQEe;XdiOii_aMmuB8r_6fb65N#uAKpJfrQCdZ1h=HIf}nN7Go1-=|W z;0{y@Z&fZgF9* zBWCKu4o1$uwskG=`Zdoyc#^roaD)7Q7$tx2F&59>&aU|E3s8YR!Q!8UUh;eU{Tj1m zU@abBGTt*3=(U4_CB6CL6`w$^=vR$|hk7tPgtFc-Yrw`n6mL2VWq@HQ$#B4xFxQ7{+}C7K zZlW1N5bizjTO*+lhP}3fX1I%{o=}IoY{fwigErIyZ01287)If<-GST{l5UhDm?H0G3K}{p(_g*G-XJ8G zDdvc`3}%{kX$FFnI`<);i6b2D@mM1RnL41er^j_Z{h*WX3KpxPp~FaZi>wp0?7)?Y>J$4 zd^Y2(nSNBByRoy&Gm>fY_Z23$;Y#J8Jee|I9{Ir z0Yvf0dN6v#i4M!LV2qb68S*8muediP^B^3n5=$IT7n3M>oVK@9g=D(HVUkmsk|c`F zp-##4JBR5c);W-BC5ixRD_KJ2K6E&wD{JBbpO8ghr(3s5)}e_LMxYx7H~ckG_7I1s zkv(ZSRXxJtpgGlSrez$yn;i2T4i=B%m|8~tE_XP}VmXfcGiCh3;c(_mUCUVo>?8i> zfV*?tn$DgAj}u!R4%s)FTCNsE49rQOv>|E4?t>4n~@7zk~6}VdS&vn;x(C$56{> zp8-@d{XB>6CRPIM%~!0)Q3BW5c`PDUOc z7m|$32rHTEr@kEHd_dyw72yGh-@ES@Y4urXYyXBu9H}gzKXn4M`f|XNo4*Kz68vJm zQ{pkO|@wu+)yei=))2C`fKh=PN7%d-aC zfxe6-=0bz>G4h=Tf2`F#i%R76GU}eiu09c$>xS2#!jwN$JI)7_nNC>#IPkZ$Cc1_uY6EEXWuP?|it|?@oZ`3Y;;ZQ7FGq^6V(}Dg-4(g}U#u?V z)KYjbZ)0$SbWR24f^`1igd=?%B=kqPX*v}w6#$fSO_Y8grMtaFEGt}0!CZG1&Qa1i zMVSlI`P&ta^miy7!L*U1+mj2vzMS;T1^(Sp{JCK1kFWmJ5`7r?)qv(aV=fdr6v57^ zvr2wg6B~Si>gVL>YZK)tL*$Z;UGbD_#6j@^6Y1mtY*e>tE#dQ2bR2FWus= zw7@oH0nWqbVvC}rkAwtT;KB>QaykH|oczw(`IPSVXK_(a*Wwz_T#(LR?r@sEfdcR5 z9Qg=mf!`67Gr=R%P-ZYTMuvU_ON!r;dIZ5TXfJ+GPrLx&u+z~5@dBxd>I-1%>sHZ$ zF0>9c#A-I&D8)@cM#-mHo26@|q z=HQx!hjCk;57N6%eE(&xTkr^5S_p>k*)`vf?;iRD_sI3JVGj=yd#Y(#KW_nfP}p0< zA6^N{gTh{UPAy(2nDC|`Sg_F54Bhg9nps-gR6jmsVOz?|AEX51vPYELxL%CGo4+jFn zvjVA;U^yW-!8_P#DXdM5CI^335M2C4m^8V|Kbp0P`Bn<67UN0ht}J+dN=Zji(s=UR zOG(L3lRK3hW3co9S30&heG{61j?dukrtcu;6Ee8F>6^%WVmfy>eFrn2l#UlL{?W`P zr{e{Te+;uJ>39L-KY-cPbi9D^k7YJ39WP+~0B(A+`c3(9E~D-`AVy}Xc`HB!&h46 z8yHx{o^TmT#s`XV57IRDK|~}3X&QSX^ND_%#y*(&q`1krhwzE#&5IRtAVF>b&lPU~ zfo9D|61W?;1i1~oSZq=Fiz0Zr8C)y;>`R}g-r(b?1Ali%iq5bSE53L@B}^Uk@y7P0E(aa#dK3MQn;tT z(h@)QF9h&9{nuKIg}QBJAmV7JxI0GS{)54wgN*)N6ZN%9Rd?kPlbFl$$AZpotR_Z(wNtie(GMk${NWRCBYB>x1IAm&>~GIz_7mt~KRtojP0apo8nQmy zK2vtS|7+kZe+%xyKZSJvG?exD$K&p`0$%$(u^k-2ni|Wl7t3VUvKu7Qio1%k(0B2? zVZ+!CBnIs&aU7teO6o9ANcS(al8z)eUy|%Xk+g($RpY=Wl0rMFl+;G4l3nOE<3)e- zJUN-vMIs31M6Z1*zB-JtEsI=ngtX(S&4p4{S}`lTQp^XBmAsmS8)b#*?;tO37Q=z}>;g3dUp@O7 zGgc<7iC{9140kGq{+M*gaED~b!jB_i)hGyf(zlWCA;mXE^F1i}di{i?*|ubaV0ppY z_+wJp@VJ>a+2ZgM64_YjRZ8eQLX8XREHO*MZvoEnq7h<_{Nl>Ue87t-cb-k%e@OU6 z!t*8k6T&0NUv0C0A0gb2@HrCR3b^+NY^J_Mjr|q0?!zCch@sTk>CjYBa%lo{i6u4` zH0sKk%*xwj<%I=U1CU?Rt)iIFI8t?;Z)7ps2g(zf0 zt!5c{c46k+N91{W2pYD3k;nYhk_tDBAJO54!A1Y>f(|!T!02#uBC}4okufF&-2TLv z_ra%Q%=t3Q7(=8LN1xn^Pdsl-AmZKGE14+lId@J+QWIjh8^f=bT;FH_?3KC9`03yZjAffMjsM+fC zij#ehS$^}?>cj7v_3e$D)vr&tgWlspC!h(6C08XFv};+*qaH!an+q|dJ~C(@@sKon zfQcarJ{~Y*$YUNFTMydDHJ}IW6B^WmHY827P!>ewc+$g;H*v&3z5!)W*j(aSPhb8t zQeQrq^gR@}!Z6xUGz+=Jv)e^QyO`OIKOiOs)&ld;0PHaRp2pCZ$oK4Klcm|BDFZz@ zXrZDErVp4)9N;O$d~OX~3gllPvFLJS5>IItr?!jJ+QsSZqNtOYGup)u+Qpge;w)5u z6EJy&P>)iJ?*t^TAu0$h1yYEiw3PJ~6tb-x1gCujp|6I4;lDx1EObJ!+5hPv^ivMd zS9K728lJ`F&C8J>^oj~RGIG48BFb-A;&qjOf%!L7{$b|-AoG3NAShRbll4xPU)au7 z;pARsWjN{E=kW--pF$yS+(Tiby|HoQ6S%jU>_>SSH^yO5sI`ZJPX^4mF~;kP8wY40 zO)WOYdKqCGBqsvJIIlZU7$_!CO!DS3M&!yEfqLat#Mto&JKe|iKM4gp;R%fPRn z(9Dn1sHY%XK7m!vAYDBL=|?x)dGyf_dbacEqaWsM=h4S@9(`=J7U6@HT#G!9%>JxJ zJ_TQo=N%%|^UgQO#PjB?MGk`*%v$6SQ1CEfo_9!ctwoNOU}P;ql^6!h)2jNVE%Usy z3skulxdEljT4WiJ(Q6U;R2a1ukr>xn}NyUSp)=LIO%`7T?*|iKfc;-=2 zh(B*@qR{Qq4Tj>K(>~mQ@BIGj2C%_CqtL$H;9ociy7%hLB>13f5;U`RcmZS1!DeE8`CByS^OxT;3@?`#?#={J7SH ztKNT54k{&z>wSGTOE_2g>unG&L|-{;7K310w_;OnAO5;}O{8ODiTkf0gT)#62CnPS)W&&CvHNEjISV?vuc{B=mMx+52B89bB@5sTymk|3H9GhsC z{|=7-%R4_}8vX1Ku_OBTaGhQa^*B=V2De5ofY)WuX@Hx)$*qwb8Mog$A3pXnD7|Hn zf1C4cAHL`l?=X8L-m2zCZI1q#-$_$Dpa|IN>M!F#vRp=9jS>+;-Vs!+wE3e=_^*4r-r$B{#(&$5O>r{2`#FX)bz#Ld3LTswo$y(~{6p}M z4C#f>it&Ko2b?Kg@kK#j3}auw>b!KFX@{bl^bQ-^xkawx<)@J@TZ#apdcM8 z5qaBMF<_q#b<5a&IMiMiUUrX#m)&FGWjhNmTP?i!^8Yq8Wq&rqo&}%Y4C4YVh+_Z^ zf=DZl)_4n_c;1}NFrIKSn_&q3@P^1{7)h?pupttRY=%*#eY?)Ppr*XTm7RVCe=16+ zVvhS6Gx*+Z8IlzDD|q?52!=Z(LyCJPk8k(NvJXm9x|m77nC>-k0P>&;e)t1y8JHE1 z^OA2idO8C;RSLQiQ07FS%_UCregrVZH2H!rsvPt zWd+aNeebeDxR(`rEwMifh{>p(8`^%}_E-Ts5abkZ|4JSm(5v9Mi$SzjLtEP#ipONF zXdM`@#-|Sm_u_NxcrfURUj4>6GzUihbsoLjyC3JBd=nrm+1|z-S3eJM+g~yZHws+A zv|CCov;+KmJoa|Z0yx+a% zYHh0st2^Bz--65@G1trkWi!6SX8k^Y4q(4~L^wSnqIyI`^@xb-5fR-ZI6$WM$5oGr z=pMlVA9s(4e|V43lI8fM@MiNbSftUSo`D>RaQ2VwCd;~A(z+e1j>Fb6tx6w%7oU%4 z)nXv+Vc65b@v~mcm-1GPQs@l%7pnB{>~q2GRb#-X&V70H2`+s!O+O>6U)=YqU)=Z0 zG2q?iM|A;?0q>X0VhjX12E5&ridi?vG2q>Pl!QES90OjT;Da{CKs?8Q*RMf;Jblg^ z&|V&lr_XtV;&3T9K8|C+8>e@O6Zu=?M5r;4C}ps3l88-W4jjwnozF#zfuam;4D!i# zig*oX5d&p;V?T_6D?v@2y8upK#H`)sOG~fBnr;y0J}Vt3kO#93sG<$j4Az;+I&Z=! z%j(5G^LC20fG`SFe+f*}dKB>M2JTVt!35Os-~QUa{yK^MHCXl+dw^F|e+`Q2uVT|* zL-xPF$`OrhD;aT4_gAa#FODt#Rq3vSz~Guk_V!m{_9JR}_&WlgH;zCH)X8iQeZV`6 zUh9`**26LDt-b&V^GD#ap@aS-aLi)B#c{y-Oo-Ee1Wr#n=8wRsm=^gXa5}`9KLV%D zA4UEMoI3It`6F;Ykk#Oiz}fwnQv)tZhRFkgNG3L!IdF8!dl3M|Kv9Nv1oA2PZs#lP zz&=6>Fcw7*4usXyE+VbBptS2TwWP}t*jteH8^Rody#*Wm7l0XfFv}I=0A&ot0A#2U zeI@)n;m0YgpM)pla7We)g!@ZaoZd@*^D0l2v9s|OPwqZEW%K*UIS-I=IR1PD@-56C zU{7TJDdrEc&q2Po{3{=i+BtU-S8t!<#S~hG8{c?m)Zx&tD6*+`E_x!9VNVQ{Q*t|d zCYNjMzW6keTmGWe5UJNAk~#=j9>?KN;w$QKN&Qe#p8|FxKKwbIzOrnUq*}T6qrPP5 z@EXfuT{+#JhRO=@a@)!!elv-9)F%|VmTTRh%n2dq^<+13Vhk{&~0*oiTZNA6B9=_@4>4 z(E613R{BtvS1=Bbn5^~jeDB|s>aSgb7*8~2 zMJ!|IQ!mj9ZgCja5y~h7pa|MWP_7Tfp)7p}+7LgtZlom+XZg7b9HqhC1ZShYME5u z{sb#u3@~|a9V(Zg{yPsTiz!56QfE`q5OqNod@`HhBZJJ|NiEWb5}rUMv8pK$O&I#b z9K>?K6cV$Jn28!QkeFV?9HKFwV=a_cN6aK@hEiLBNk1DH`y{min4U-AWCd8WAltSI z5DK&p+yyFqGBIdbbMSfhFfh2{x@Rwc?Q1{c`v0Ei1AZM4!;jO2u@U?u1z~p}aZjTw zQ)S2=$Q95Q_N*n+#K7c?1W$5qa+)|noa|5a!>UDALc$?H!trz#_QvlXCnQYG!0a^$ z1^vA&Px6F>gCr)9Aw0w@6%$HW}?~9lb zQCv~d@CaHS&!*+~6C*PSo|bSr+F#(aMglt^A~eBaEgTaOWTUr>0P5bdtbqye2tG#+ zDzRWdV##d?Va+{)NO6 z2SIwN(C$%IfUYbB#&O}jJFweNy^c5g9MuOWPR&pz_B+a8R&r{FOS{>fX2v-M*vxo0 z*HqaZs&^6`S>#JB(VeBJNn_Ai#3x5vEyYy|rbu;nG5b8tbXK~X0_S&$LAgw)D+ec< zp3HJ&PfDbrdmSK0*!P*!u^_$^VurPh2^_6{rz+EYWk~G{@{5|0nB(d&w=5HL5PsYp z#u2XumESQl+-_RJvGM)%a0{3L9#qi-f8~jBC6Su-jyhC<-RBB63gL<~kzk=hjWk~b7aMBa z7KjcwZc236F%+yy_i9Z9ALS7JQ+3!!$T1N?jh1}J&1ArFykUSdEU=<25l3dQ7gVVt zltRR16dp*5!VYpry1`^Y2}}uyFLGOS`{cx{5X`NW&}~e_}G|zpi5Ps zWlhjh!LEknK~6Oa;SA3I*>tVu)S&YHIOEIljxW}4gaXbtk<#+`~sN;MCj9|J^p(C1ZRHWH`4*NjI zb68-Is{lavq>b{$(OzeUmM)`RcNjq9x)31;)yaOWEqrv15fq_7%+PLmq?S5vOKr+r zbOgbXTCyHWqavc@B4@OlnZm|6^vFOXo|8UJo(Q={c28dJ8OsbT%vUNl4%SOKOjAy<+}F|~+D`FK5w@2L z3OPg=A`=0~l^TI06^v-3WCzbO?&SpfCDlYN{Cb&M*r%(AAG>^r?UF1yZq)R}6_n$R z*^X{BXO0Rpy&YwU#g>v<=jRq+4cFHpeuv53cHYM#!8FAm9m@i-LB?ft6`0~oe1V~9 z#(hM;sNkbYCPm>Gcwl+eX8h8nvTUlOj#?(jsKI>7=L2;UnJ>qH1@bfO|R9xDRl6@$#!9Bc*=o;B11j#@Lzu*HgoI<7;|^?*U2 zAMT8X5ys!K5-@H2`yU^ST7K2j0jGo6LoWAn%_!2xxDs_gTCw{Qdd!`8LNJRhIloe9 zw4+HkE3wqlN)0|a<+>3pg}*uF?xr*s2>YZv-NaFu;TV%(rbCTgfUw5cy#n|6+-n?k z#8G-cnJySzXPj|?c;f=*;VH5%a^f+ULD70(qrx2;+$uSbd_E^&;h9CdwVwIDOL)wN zNR)7|kQ*R!e)Jc^=6#dU#B3MVY>S{GY>M=3rg5pn3|FBf9l215yCpbYDtlAPNO5Qh z2}ucw3FJrxQqOh{(k!xMF@ziC5zd3JE99fsO%Tm0(F0yF%8;*WckD+O!=I)wRdNXC z=nh3?1be%NxU>$U4~KqkKP2ovt|}0D%<$~z6i3X7j00Dnu}%|xbR{PG5QGs-^pWl5 z(}kNI8y3av>3$gO|A3yPGr8vx zI87mbLl{_WVX37w7Q*VOZcwb#;TNym-d0RTJXglL#?b$|hT78oXS#;+3QhrV4YGg$< z%jQ`?$7M4$;7Ln`VjKs zB392$Tnl8Hg@xJj)~?yhJw?bx3}=MD$wU85?f6e$7XIC1-*ijtvJm?LdQliVA@}`` z0I?tDcdx(w&dL}obF=;qn#T-hAJo16=Ico=9^o`xT_?KN-*|81%oB7q{n)?PXtp29 z{wwB*f8m(Zj}Gop2P-?v$;c$p=lgx&Z++}z-7yb-eBT#Y(gsb?;2S>O|9EnDSh(WA zN>}Y+B^n<}QoS6t^`<5|{Q~*H8Ex|~#Qj+o7L zH(rf_D0kGG>faSkzAGe}z~wiAD_{bb*^NU#t1Z8{{d0WxV%@#>7yV-0Ou-nUoK2&) z<<+?%?Np@l+>mxEg7L<-30Z;xgCX3!jU)*{H?lyCi6|-25hYc7s}2-t#v18-6=AkO zr2)9f`dv2uuD3t`Ut4aPkYYO2IVq*bA{raJhwIusb$6eK*5u(Y(~rpd^mv3*`J)@g zKGe~gy~IsU?Wv{@U0uqvU#>27j$t@m8guSzUtQ{YGT_Q9jqg2No7w0`<+qu}GFj|g zS71^hXs@07Ut_pD!xf0#b}_!e{S6a4{Kj$qk>>%;s84cS1jnQ}pQNZ!?;I(J3AN@` zhn}+dl<1nH+)s&^!S*ttX}_g=(QiyyfcVQ13glVU8~~D)KyL%!iH3WVdMlLkD6@Re zspZJMFSSF}U%6_Yan*b?h`--Eo!)VG$i4;*SG;;~a@IC0UL6O6%AS93rO{y_Hqx7J zbiEmiUiIPSm^6)6#w@ipXx;&7Fj1-MdL!mutMI)ytxRV0mg~JYUXM65L=6Di9x666 zf`6RH{(E~=kh)lhnL<>JcH`IYr^&abSmDs5c2 zu%@B3a&dj>tm<0GDy^xWRXT6>Y>66EGP-0Gk&*votgfA1vuIA0Rl2aQ(NyLLgkaWQ zI(s30Ww}&C`02yhm9@21^(A#Eh^ww1Ki)pKWIpo^jdK7Xk2aoP4Z=L+@ofj<7b^4# zQ$~$J4oyCFM*YHh^_2@0%Tc-+Csx&0SJo&TLL3C)f`xo`ejL7=p;OP#mJ&4_sD(&N zkSZqTYJeG!G#lwOr2UbeqT#cVW&xh7(^`$cP{S*cdKxKvHF9Z4Z$UZ?=|-gKNW(~b zBi(_NlIZEGZsxppsmAf|T_poUp412rbLGGwcv*dr7HbavJuGq_qVY#-n195H?Ko4z z^EG{yhF2q{mg|vHEB5 z^e2KS`2UI%n#N%P1jGW8!3%BOT+a#y+)^ZBGny%4E|PztecXl0=tqaYjmfSNZ2Vk zQ)5g?lbVtS?iVA8PDnvLC0?v4o+=Ip@)#NWD>K(fnDh5a;Lv5wrbd&}<%u`iD?8AZ zSAfElCGSN_j)#%5eUBpLQ2sSiRf4%kG=>~c0cMHkkg~3gNDGj@q2YHm{1H;t@EKC} z(Kb#03MpG{Yd-?7=&o09QA zZ0IehtgbDYU57vO5H$?|+V?=DNsMWja+Sce94SpWWj{>zBEG<9Tj#-W(R1&=C|dnf z=lZ{EZjYIT*+pvlo>RpsKBj|WxaCwogUbOmeV^D^UuCBE=%C8Jnu9(&Tld{8q@Fqk ziALl&0GA=i0$V3_VJyqIwDk4pd5HM8}vqs<|tZ1WpP2OySEa{}!CIx|ED` zU)EJ6Sk|*uE;X2Cp4+HAq=QBT_OiMyez- z=lQ8*mf2NuX}SzpmRqg)uhZ$xI=xq?{9C@1_nb~Q>hukrzN=II0Yma_MtTv_Pm!Yj z@0=kXfpu)_r#-^mzw}Y#pBnTcPdCW89&nidn;vqa9*FQ9RbN?Ojb|YK5iv_X!7wty zu0SBx;XqVm-onU;Jo#X@p%QiT7BtjV&aP4cbgvIU;Eaxtgg-(zGiA3kr8_ZIZbt_o z$7k9xb|57O|M<4zAZvF_StHBg_^alibF+j)@jU09%C zg2tGYXp)Be_TW5FV@z$)%Cn)KSLC?+hS}FIrnYtpvUf@>zBsF^Wsb`)e5qC12#2hs zRr~0)NT>Xh=#HYEMWjmxG{$*DxI|V7* zeiVsfDRL(uM?q&IRT?pOGca?IQcw+22)aoMsslhli;+^$5)wr{a?6mTpjAknrmX>n ztuO-wg6{9pG_N>Ivw{4p2-zzt9Q3^rbga?6VwPs(;((Qz6w>bbj$*SlyK#nSdr}am zAf{%xL*Ud{Z}EzwG#9AH8LC$tt+7TbsD~S>X@VDjs87`Z>XC-(#qTWlXb66ePvJ~i zqqSEY7gLj|QS}1*gL_5SA!R#$jFd8NMv7Wbqug6G?oOnXdpA=0^1T}WOO1aRsaG5X z%2UX(o{dOVJ9krd5DML$QK{2R99*oKrG-H`?`cP22wR5eIak&+`5DXe-9t=dcD z`XZ&2eA2`a5&e z#iptdi_d_#(~wfRnMlbw3#k_m_89*fbB^YyK}x5n)A+?2zXYjQa9&%cF{_ZOTA1^^ zC!K;>R0Xj0Pq( zH5k}5*WgWR=tj=>@frTu^83Q?uiRL9e=BV2S4N@_dTx@zV7+$Fo3(r1O;QW~LNGSg zm~E0=Pid}aG}ntdeNCrtBV|)Q)ae$Tc5C`K8n)BaAP6E=sxaqyTvn2dEZfyfGvyOPFedjr9$C+LUW#xVs!16UC}ogN zjg;69@Z8`?`)r@mj8rK4ozHgsT`SxA5cU3BY-Rl|FObypQN^6@#)$&Ou7^&DZG?r0j%?H2o5tHtBSwPFEo%-)arF>U6D6 zuS2T*hPhU7GG)!T=#+nOo$$j*sX+)SL+w*YJx|Dm)`yz(nNGh#%9&v|QnLE-hd)^~ zS*JNjCjl--O67(lWfdhlEz{|EolZnbzC$!zuG1+xJsPRw{I`P>u#~vBl#+-y^M&W9 zk=3GcP?NF|ZdQXg=^2`CM1G%RZ^6^FZSg1Z{w*B)pSoS~ckR)t9oFyJnmrYpuaVcU zu07f4kwm8$gZ?=UDf{ajr2ntIuK}{FxX!$9o|sTx8OwwvT7rpCSi5j5_1>HNW}fusy~q0*B!#sVir5N8TsA|oggBUDiHjnbm1W0vh&M$j!a6KP zC|03TS&C(`3$w&=K}Q=x{+>-zYt`cTb1j)`7G;{f&1Lnoa3m2hMWIzB|RXX(@7DD6|R+ zvnAEkWvLumy27{yyHk|PE2h{oEyeFxitG+8f1Qnqx%_DtF74%gUbz{cNagLZQSHQ2 zO1xT&-qn@%Q#M7FpxhM3!v5}zDgR`~lz(U`v#V56RegK;YH-;#O1snMZl`_LrZ~HO zTV`EUn--~4-E_avEUFO2wU7^3*6ANuv)^+?JY-3fR39~CT~1mGEkv2e6koCw@gqE+ ztXKh$_Fd;WzYgl2;NSgg84hMK&v~~sWUd>>9^Cw5hAO(1V^rEKE#ud>_UVxK=8Wjl zXf)RP6)7Eix<(3J_0<-#<-P%&6i--J{!b=W*y*l<$jSKP8n_DQ!op8adL zh>-n@IQDN`tWGi1pJu-hQ@zwiMT16F+GYqeXvY7Bh26BJ`ITOAEnRVi%11|q^Hb$3 zoz_Beg_>WX(kZS``75rAMfxnD@d}l{@>5*XHU3g7uMRbuo~Bnk4V6B-SM71OoRq_K zcKX3Nv(vx$m6_Hh`|=?r&HgC1?oYWNnX+%Y*tA8Z@$Bn|9j`w=O@pfMwEd+Q9u-%5 z)oM4~ym84Mu{hNuR$5#wq#Flagh}Vuk`=z+^3!>b@>7Y`vbBa;oET&J>Z+7vT&~Wk-VS~@c zcJx&jo3;?;mHk>w{j8(bR4qyMsqx)BpQbCl=GT6#=^CHL)ee-urfYuHi1Jsvnr&4Z@Idv?o>xw zSB+Pw@!jBbw$INN-phV0b4-8ksdMD%1mS)>@1-E}RM2ta`#op4YKW@Lw1ZKlX8$&B zqA$B9(p1f@cp7TDLQRjCw3f`$YC`&n@i_G39Dyj5mI1Koe+lW&g%a zdej6JPS=w8xoXMRt=ifYyG8q&u0QI$TpL^)TA_}L=Ua|Sr}?x5h1!IgU+GjuiYwo4 z`7~ak@>M<;qzvP*#iwh6-<+!^$i`2G`s|{0*qD-C>!hQBLZw$7sO~jhpRH7v%17xG zSDh=a={mDjehR14;RohShtInCYWDBr`RJG2b5HhfJH=}Kbf%~J)OgjG>Q!x3G7Xs40hAh0!LcH81YaH7EX_=D7C`>!eH4)6+~;ik{T4n5$oa%0fSecbC_KvZ zvW4Ha@HGp6VBuSUEQiPAu^wjvGT*rtUj!)fTYMcLlEkS5~^SGfG;QIinkTWfQw#Co2_{D(P200BPME|e? zun%yhr5FJu$B$Wjzr`ySZvwJ}TP%JnAb*@a3P_3WwDI=>X7N0GTF-+Bv80Cp*8qOS zQaowlGZsE?;VTxt0Z7ZoVV6?k8Gx+g*%qDyNQ>veiu6bq0**H`?@?#c#3rtrp$^Xx7{AMu;`K50KAp4_b=j7Cs3`j?Y+}$39MH zmj4ALJ;CnoW~o!M&eL(6?94s`+xBK-4C*9}-G@gE{x;x3Kx*ndKx$wyAm#0|@G3xR zaD&BnSh&Z=^C-vZ>H`XVK^nY+D-&csal!mWf+H&6ZXEp5jOo0%@x}>2#km=VpU#ah z-tf}7?ZkN&PxIf0c<#IK8JkX=*SWaKe;;mMn{oU2aS!Jn0B3nPzx1E9c)Gmj5YNxc zRo-+u<>n{Ld7Ga&uYI^#ekYut>hkH#Pv;||;`|nNv4``s*Fg{Gm#rH-oFA)luPx2b zPgF1TaDIB)w0K(nV{l5mw0^|-`Jj(8oxFJ~J^F3}r^Wc`S|6pqE0vBWcl7*P&*wi6 zpNU-Lr6Y((%$S}yPo?(DyDEkEc{o4v@^OBv<>Md?j(GIMKkniD7-*e`^GltxJRCv! z@y?gWuOHKWoFB?8K%CO^vlu?7D^7Z~N9vDXoyZBIh$qgELVSLt=Z}Ye{xpthc_Mu< zMbGJ>=I0CkbuayNyobEr!x0RgwRqZ|0!)wi682`BPMohqAM10 z^QY;j!-rD*R;2KSsrqYwO_!I}|Kpf=@Y4QFoNtBUzv!Dd-}zGeBhELyRG-B89+$RP zn*SmSf=lf)oxaMZ-_v8;*W!8Pp?zw)_Lp@2BbJ^|o}^Ew->>C&Nq^j?@3ZBl^S?Yz zdcdgwE>5vKmA}ZQKh!0ilNeqnad*nU&!*qkCH;s^f4EEi?zic0bjkm?O+Vfx{bi(c z$?u*n=`bDB%Tu6G%`lGb|_aU8632N`@^dm^;q{LN%f4-4t}+^qMvZ2@ zRp35uVIOkkM#Gw69OCr&vF3Qts-UodKRqpyKdnlmG**d57eS(6GFNUzjUc(hN^tGg zor~69a>*ju&CLndKL|1E&CR6-Hi$#Lh9`qY)Lh+aR#z7ecmY+;YLAiQ?-^=npd4#SOMxVOI_tB|Nw$Z2U|WGD=ar7{#Ynrr49pZ+U4R1E@X zIV+0gR%1MD9`xd_z_xXzaxPzvhDXMY-XW+}yEM>lw1qa1a#}78G(55ijS$8fkHVTO ztbb5w!g@4DUDSuiM>dUv+BRbUfGmEki<&6D0f7q0T&freTqY>28nYbeFEwJ}Ry)Bb z?V@U!M_Coj*Zl@C&8<>+<@>r4YjUCuqQyMJ8y-g!I^_p>Z0+aRSe4ePQrf0V@bn7t zVBCGElvsM08y!XaHiAlP0&%ql1g_Sc!2})a81t=FZ;tBgsyA0qBIInv>givWV1#tj z-t~il%jEd<4@DKWdaO^-OOr@HYQg?&OD!ujDlkt)`%T+>`OvU5gJi>6F3?>Qoo{Wf zjy*f-=w`NC6~=S*pfO&pH>HQU%tJvpm(CK<>A9 zEbTI5rPdu-i`J?vr-SnBfB2BJew3>Ob+lzwnh;gN7L4UsVg1XbC^b14zGwH##YQ1c z(#B$e`eJu6?AC(z7pib*OSj8jlWucJEOywvP-uEfql3ALS{XZ|*i9jA^EVGBH0Aoc z?P|8>u>ex(ZLwD#9zR4+9Givhx<%97V)xSD7n7labmlAI%ZugQSVOcQm%&P|HG1gTrQ3X&0%&S3(&8l z3g}1cL312B3F`+7rHFp89);o>P+A)kGv*e6jqzD&Iaym{R55dU127*42wIIOYz-U~#jy@n0H>f1)iTo^bhB2WQO96U zkndD~>slr{rD+X2@Lv{HD4sM?!0!~pe5+WL8I{`b3V$+EKj;YdZe4*dW7d{)jRq$u z%{8Uw_4QGX^+9N6CWQtEbtMY&hcK44f~g;DhI7b74$bxC82SWNmmES4&gvKlvSw$|1X?uUZunrD&a(~aiCM0)LRm^T)%yz zmcc=iT4N_hzqtaV&l>4E!}~b^H1n9QH|t^ZkTJVWmhSRWWi&c?eQ~$|vb|e1`ie#? zkH-`+LUv3%>N&W>^RI?6m!wM@?|ZpSkjj(G$hG7l1 z_b2o}NRu#nSDD6|P9}}=?a5cc74$MxL34P2qR|sdqjbt?D7vYY!$X=hZL_+w2IFMY z!-tSf;5D$HZ7ec5>UcuuBR;HBY>hOwmMhq0FeWtc@afB#+wQ89Xq<_X>3cHSR`HU? zny6BLpF9kD1}1H=wKb7P-@wERuk^(2;Eaha=}fG@Px7qCIz)4LbR6!}7%W=RbppTI zZDq#2`Rc1<19731W^M^~&M@P`?PN>3Y|19l&BICfmnFu`RFmi=;$1jrH{NJgE?Ai9 z3`RAK#Q|x1j6BqvI;W4`V5jFa0#Q;~#`Kh+evh(!z$#ezS1f<0bJr5+ELz$nS9@xa z(uPf^x3+{wKJ=<&w_ta7=E|W@47rU@!k4O0rGxOpqtzB3lAR1IMFyq7GqmvAI99>L zRh*%yNrV%7iA(D5msn$Y!xPe=%4f**BaRwzr%PG2q$T=7p=7HQ7axR$95%PKPco@y zFz!XmyrNK@fGdr5R9#QAdX##5f()4@GHB0bp`4a_$aEsZBu}TH?d3I>Yfkjv_BKXE zyyD8%lO-Nl;wN29CAssbSSUtpnQr1?9HW0C102)4J>g(-5btV(zK+$jgIJqr6dR>r z`+6C$S=n-Qa1kEx;3IZ!-zc&1yBX`E$*9b_F5RGIt=_fmvYp%4crOMn&9^XLZBEpd zRH}KbSF~#UAoenI0EDDnR?Y9nlcG5*0vnVr=SdO#)Yttgjev==9|2p8-vBp8OT}Wa zcJu1(+rz!X+paeg=CGc-8C7bIV_sJY8%3-rl`2IuYiksj1-TIBiuWeM=wQue{_xh- zTZ8qRuMb-lu8~Sr@zz<0=ZsUNeY1YDU7iZxDc;b2Vp`A{BrnSzFHKx~Jb8gJaXe&g1SJ3fLZ)Uk?Xxpn83 zHN)G2of}MZ75Hd^nsXL}nvdtoMPVW{vSMwbg&7*=6L>(t0@b15(0FMKJu^f+$+7ed zMHXwLrAbQ`Vxy!@1QKL8jSZ)$F#!JO!P$(a6 z!N6Kj7gEHq8YQ%kn4j3*7}7AMG-+HclT_qul*sHLIRguuxqPtm!={c968b>`dj5fs z+*%bEEY=DWH6uKdoK3-??2E@sz4wlPdgF!xUq;@S^)DG(GAO(L0wrATUT}thc!Rg2 zEauJL5YLyTGL{s=NjyAZ@fbPADy@P;J5@uxYY~0I1j3Py1=vKsfQqR$Hbf69ctpUK zz%uXdn8kZb6Hgsgn0c!x&p+58Via>}WDU#cBPcj31WFxXMKEgTM`gIzu}mN+)G87C zYq1_hwzm2D0X+I54r>NAJf6`c%!^I{b*^#CDqd&)?u{s$U1mT5xwDXR4gJg%XgNw5 zhRDRkg}LI3{Mn(sxKeegq(Gh5;gyp|aQ6oLrZTxH{1{{|o{{nv?%tq0m6?d+`O3-r zaA$&Xf`t;?r@5FqA9rt1oXU*Fxp?5mtxOBXr!u9f%>Jp&fjE`Nfbi&#Oi&7|@RAzK46UBLz1oCK5;dB{FN~o|S4^t5%wi3QO=gyVXyqRE3g9{hGSzl1r>!K7$N6uZ%7k(L z-XMv`bRV0_+~CtCaiz=1YafDn!8nyaO3Iay-zOCl_={LKz8LkKw5KslhHuBqPU+rC zx{DA$KQO(<7&Bw;lvvX$$)5ir^2`@rroUZ?@EQ+SAZ5U8QsvQ94f>8N6wnx_WH~(r9sf-Y_J%=kSy+Bkwy3mI-du zScUDfnJyQnCyh?yJ+2zn&MGx-Tf&*rb<{z+OLQ*6%gPrDxl_EB%J3e+lUcc8{9fE% zR=#^!7E)6x!$$>AX5|*~x8wG*&R!=;)9DQ`5%Oj)g2U>RuR?Et%><#wDx8r(Gi`4- zl6wUuig%Nl1VN|RSU$&8qqLb~JgMtA{ufhWT0c*-e5I9p+N)|9ep>LPYDjk$P?)IvZKuQ~Y!F*noKTnuj( zJUKxn-F(EOISI~_pli;Ui%{}{7PYfRwJZbi%`xtKMiGZ8+d(BMr z_GHu@K)FZ1GS&hWrsLBp?M|0$K&6}Qu%4`5%*8#9X55z(n&{V9*Q`!@uQVA zUGMG>kV!Mv{9aeqsCJgqT0)#P-73ksWp6OI>HMDHey?*#7~ce>wZ!iXxP8A?o#D+L z=;C^&J#(^NF2j6MYXxt*5vMwTrUGgbKX}-I+Z#o=;e5?R-2=c+AiPv=H_xr* zGl84RkmCL%6}OCEgm>vdwE=*iMR*yw+dFr5_o7rDucSE2>*_8Tr#d-0uuE5$f^b`K zjc53v;Kt4v<~JLfuBF8JOnWe2Q|OZJiFvu1xaMd0xZs9A!(aE(m4BSi@MpfS3Z3aX z44ZsOdx-OyagX^Pm3&4nhE2ZCi*zcd%Aqhl4~gSX)ozn6pgaGhKD(E@Z7Q=pp2;X( za#FGJB^oA8nU7-2%Nqaw#^A$6Wmdq<4?i z)bwIw8jtCctsKWEt#5u}9E+)vdXD3hHaOeT6T@w?h?xn}@p=E$Wa>5v>Sj|$+~im` z8`@Zh>F3kg;W3|?wQ#p-Zu{y>QjY1$@!Ik|ZIz^kyGhY)6z$w1F_)Ruu^aca&zo=O zGD%ZT&v4?>Pt{U8>%?n;bdfU?EQ9mVQrD?k#Y{D(#kKp)&UH*{e75=L)*jN$5KH?r zYx(!I4LgtAopspRVb2fD7@3?4caBx!))@v*XSwj zrpk=jC9&Ofdk#{S7sLEnQD;O9PYQ1O2gCQ^u~*YSiO1qk<5rwuew(ZOK<+M6j+xys zd;#v>V8>Ku=TwH_F9>eZ8NOZkm~@7@C6js^hD|vp9pvsZ=T`BS!oMGxl zTZ~${2)E*wB3$-xT9VO=;jhvuJ&KZxt;5Z~9zG?21cP~;okZZ4~AYAuw+Qd;0e-(QBuIMf);Ul0~gZQLl*>9EV*L!*y5}{=2j5#T>ooccQDX-)1WUc2(F5g-i!@4JR zg$TL0@5T`B=wd zsv~@hhl5nEv+%cxJ6S8naD+>?3uB&Wqh$Dj6=?t6FO*+Hmt@#;dv+2r%Hle25VrAyU8K|z)P;?_-vVX5chHbd< zn79XEp9!*wj^p-z+98omWs}@x2-)QxHo+GF{=o!a1Neyv;;&pf2ThI#KSAv{i%-UC z9&W-*%TvESig3}xYY5jp{4l~aeWh1Ew5}D1hsl!N8WTJYkTOH=ZoAJ^I}cDXL1ja@ z5VzWpAMd+BjbDTS-I~%fyu`y1bk_oel#byYViLyXDiO=RK)&>BXSCNcfw0hW(+0^6ZdwQ%{DCvoZ7x@AJ|b zeoSza&M;-x_Fl$i+>d1r#zfp{xuJO8{v5{RxMxo zR1k0Snf74VxDHbu!zN#{z4jr#NBFfb|8csY5FmPOtag?G;XfRo118+FUE26%=E=LzT=&~ z7{`~a^|E= z>nH+DXFUD7Uc?swUx?d}ryuXbeKz9h%iOi3acF`c0WQJkX9kXINH$(s(u@u^gC%)< z{kUW_%D3=+CiX5v`CSQq zPlDgqlnLTAc&QggQ0NvYb1&_;y)fxcoD&4!@?5a;+mxdF+J$&tXfcPmlukCJ)=0n~ z{}I+FE{P+QE<%)snfm|!8>a8;+Uage(LFdn6Ff%+*lV7J9|t5qniH?)zs&^yoyQZ~ zi)p-n-$EN5T(dM2T-dKzEEDzb%NOm-1RJz4FAbi;w5?_FMI4a#fDIEU4?=B2#y>Zf z2}XV`2HE z(CMG7vq}?_4X^>GcV)#QJ2G=-}H{anZ#7YnU z4TP`uaJrH~4_7-dQE$F;6#}vD-h5{xa86S=D)!*tgdvo_b5Z`}9GCa|C{Oq8W_|CQ zwML%xPp5P*v#=i8kx1VKmT;5f4 zT;8>FT;9kWm$zq*%gdp>>wAuf$!6ej`DmxtT-fW)b6nn$IWF(Bb6nmZ&2f4E8RhMD z^TD3r%fMH8_+JD6zK5TfWBFeCWqI%E{N$JVd~fG_zv%k>Wq<$qwwLexg8R?8w$GDu ztj`~!JT7DLT7>5-F3Vqw`5vFFKZyC>Il#I6xeoZ5NFM^O=im_kFz|7t@k#lE!1KU; zeiOitga5@gKaZo7Wps=G0dPG>gZ%CW{<@tHECBu>@M9kS81PM=eBT7F=Rz_6w}5kb zlh545`9tp$2=gMp`B;F`^(o@N3S7^bBK&11GJH7mrhdZF@zyALLIG2@=+46>fa~WC7 zzYaKmj^Hye`4@rfxktn+z`0C||B`+?@J}MlD$-sa0PdH^<16F#dGnpefj%RXF(p*;ULMgIR9~6kJ3=qy zpvM|$0*8O-HS&>S0p$4raJ{I%MZkT1Tmt-6um4^NT+f-K{MP{I&n{|D+nH_|vOYQB z&wF?cIG6XefBznET_0QkeSH@AYpB2WmwSNgxpge>kAd@Ng2muT{J#SC<^3jby%wSW z{f|`oODX*S0OwB-^nc`c8g&ofz>E6$U<&^b@H*1eU-IxqJ$Hxshk)xjfW)r?9^3o8 zU?Xt;oTT+H0DnqKGwpv<3jdG5`EwY5MI^uf1pFQNCtj@I=YijXu*&l#;ITe4!PkNF zXDhAWbHIIh{tmcbU!IHfDEO&;{RlXJ4%>@3md_&@Pg;ARem(@8KUZ$B=_`Q$bC23% zHdqN%_^8Y<>{%lBnF#kUTKj`&`9*iw7!e02k2h;xz za6Oli_=kY&xlP0u1OFj-@nZdhMuF#?)a{WOJf9-OuGHc7k#K_sa99yy)~+29h;2`K zkdK79>!%_!IM>MSVW@knZkGdiOxbb%_$Y$ZhOSFSt2mUSc1TVk;Nbw+Gdvk52MbA5 zIm*>T{7~kTxj08QR>lUd<&bB3_>_rgA@=subbC_ju8`O#v7qPG>c)xggpR}+AMOYa zchErF-uNRtj6*b{`{82ePi#+$o%b81!7#$XKt66xQbHxG*bfv33gDN#0NYVwt6A$Yl>u;~&b^u~x)G0(hE81F=@I_WTt{n?5+iI~>FAu&f)p zb5rsV=Q&K&aQ;pO+>9xOk^H>cfmQOD5j_M3=R%44?LlI3MY*Y~>?(>gv~ZY=>_wiI z&ffxDcW*X(G>3T{WCkNJ=VgGEIc@^yrI|fLZM&@PANCF)N*n^F0uBYb_2=HU(|vU3 zQE#8nq)yEKq}EE7>EQ_GNCv1VrBc~hy{(SsRHO-aj0EzF-CP9PeYgZxB144mJ=U=Hl{GAU#aw zaxZ*E9KJj-KF*#j74F@!E3GANlP$eTkK_vJ=G0elCt8Y)t#8o}BJ}YIsbWf9iDNF@ zwz6)gSL`o*Fo#p-a1hUAsouoC|4u}1q%0PeP~2!#%<(u5S3xS$b;<$sKT({D?jM+j zvOISR=AP4LU7ZEE!&lHm&QVW96FA8LhFeM>B*Oy^lEHT1H#zpOaKK3r zA8!&y@+OEX9$VqZ2eHBZCe43^c3V9SB|h=QF=#uTOAhpl4s!G94o=k`dt~+;_J<-A zEqR>&*eI&YK7_eFbR08;u$Z0g$5s`dO=w;mQ>4zyZj0N$%EH;C|K1D ZC5_yqj3YXVcR0Mu?yd|2_9q0v{|C_~feiov literal 0 HcmV?d00001 diff --git a/examples/scanner/output/scanner.hex b/examples/scanner/output/scanner.hex new file mode 100644 index 0000000..00a3175 --- /dev/null +++ b/examples/scanner/output/scanner.hex @@ -0,0 +1,325 @@ +:100000000C9463000C948B000C948B000C948B006C +:100010000C948B000C948B000C948B000C948B0034 +:100020000C948B000C948B000C948B000C948B0024 +:100030000C948B000C948B000C948B000C948B0014 +:100040000C948F050C948B000C9423060C948B005D +:100050000C948B000C948B000C948B000C948B00F4 +:100060000C948B000C948B000000000024002700EF +:100070002A0000000000250028002B0000000000DE +:1000800023002600290004040404040404040202DA +:100090000202020203030303030301020408102007 +:1000A0004080010204081020010204081020000012 +:1000B0000007000201000003040600000000000029 +:1000C000000090043B0711241FBECFEFD8E0DEBF35 +:1000D000CDBF11E0A0E0B1E0E6EFF3E102C0059092 +:1000E0000D92AA33B107D9F712E0AAE3B1E001C03B +:1000F0001D92A13FB107E1F710E0C6ECD0E004C0CB +:100100002297FE010E94F509C23CD107C9F70E945F +:100110001C060C94F9090C9400000F931F93CF93C5 +:10012000DF938C01EB01009731F46115710519F42F +:1001300020E030E038C081E090E06EE070E00E94A6 +:10014000CB02FC019C01009771F180E8838320972A +:1001500071F0D387C28781E883838091E702909111 +:10016000E802892B21F4F093E802E093E7020115FD +:100170001105C9F011870087838182608383809194 +:10018000E9029091EA02892B71F4F093EA02E0937C +:10019000E9028091EB029091EC02892B21F4F0931B +:1001A000EC02E093EB02C901DF91CF911F910F9117 +:1001B0000895A0E0B0E0EFEDF0E00C94CC09FE0172 +:1001C0003596619171918091E9029091EA02AF01B7 +:1001D0000E94EE002096E2E00C94E809ABE0B0E06B +:1001E000E4EFF0E00C94BC093C012B015A01FC0146 +:1001F00017821682838181FD03C06FEF7FEFC6C136 +:100200009AE0892E1E010894211C311CF3012381E0 +:10021000F20123FD859123FF81912F01882309F4A9 +:10022000B2C1853239F423FD859123FF81912F01DD +:10023000853229F490E0B3010E940604E7CF982F9D +:10024000FF24EE249924FFE1FF15D0F09B3269F0E2 +:100250009C3228F4903259F0933291F40EC09D32C2 +:1002600049F0903369F441E024C052E0F52A84E07B +:10027000F82A28C098E0F92A25C0E0E1FE2A22C029 +:10028000F7FC29C0892F80538A3070F4F6FE05C030 +:10029000989C902C1124980E15C0E89CE02C1124F9 +:1002A000E80EF0E2FF2A0EC09E3229F4F6FC6BC184 +:1002B00040E4F42A07C09C3619F450E8F52A02C03D +:1002C000983649F4F20123FD959123FF91912F0176 +:1002D000992309F0B8CF892F8554833018F08052C4 +:1002E000833038F444E050E0A40EB51E5FE3598338 +:1002F0000FC0933631F0933779F0933509F056C03B +:1003000020C0F5018081898342E050E0A40EB51E33 +:10031000610101E010E012C0F501C080D180F6FC5F +:1003200003C06FEF7FEF02C0692D70E042E050E044 +:10033000A40EB51EC6010E94FB038C015FE7F522E7 +:1003400014C0F501C080D180F6FC03C06FEF7FEFD1 +:1003500002C0692D70E042E050E0A40EB51EC60157 +:100360000E94E9038C0150E8F52AF3FE07C01AC089 +:1003700080E290E0B3010E940604EA948E2D90E0A2 +:1003800008171907A8F30EC0F601F7FC8591F7FED0 +:1003900081916F0190E0B3010E940604E110EA949C +:1003A000015010400115110579F7EAC0943611F09B +:1003B000993669F5F7FE08C0F50120813181428147 +:1003C000538184E090E00AC0F501808191819C0115 +:1003D000442737FD4095542F82E090E0A80EB91EC7 +:1003E0009FE6F92257FF09C0509540953095219519 +:1003F0003F4F4F4F5F4FE0E8FE2ACA01B901A1010C +:100400002AE030E00E943204D82ED21840C095373E +:1004100029F41F2D1F7E2AE030E01DC01F2D197FFB +:100420009F3661F0903720F4983509F0ACC00FC0CA +:10043000903739F0983709F0A6C004C028E030E0C2 +:100440000AC0106114FD146020E130E004C014FD06 +:10045000166020E132E017FF08C0F501608171816C +:100460008281938144E050E008C0F5018081918150 +:10047000BC0180E090E042E050E0A40EB51EA10176 +:100480000E943204D82ED2188FE7F82EF122F6FE01 +:100490000BC05EEFF522D91438F4F4FE07C0F2FC6D +:1004A00005C08FEEF82202C01D2D01C0192DF4FEEB +:1004B0000DC0FE01ED0DF11D8081803319F499EE20 +:1004C000F92208C01F5FF2FE05C003C08F2D867899 +:1004D00009F01F5F0F2DF3FC14C0F0FE0FC01E15B6 +:1004E00010F09D2C0BC09D2C9E0C911A1E2D06C049 +:1004F00080E290E0B3010E9406041F5F1E15C0F366 +:1005000004C01E1510F4E11A01C0EE2404FF0FC050 +:1005100080E390E0B3010E94060402FF1DC001FDCC +:1005200003C088E790E00EC088E590E00BC0802F04 +:10053000867891F001FF02C08BE201C080E2F7FCF7 +:100540008DE290E0B3010E94060406C080E390E0D3 +:10055000B3010E9406049A94D914C0F3DA94F1010D +:10056000ED0DF11D808190E0B3010E940604DD20B5 +:10057000A9F706C080E290E0B3010E940604EA9465 +:10058000EE20C1F743CEF30166817781CB012B9634 +:10059000E2E10C94D8090F931F93CF93DF93689FE8 +:1005A0008001699F100D789F100D1124C8010E94D1 +:1005B000E702EC01009729F060E070E0A8010E94DA +:1005C000F403CE01DF91CF911F910F910895CF9346 +:1005D000DF93BC018230910510F462E070E0A091DD +:1005E000EF02B091F002ED01E0E0F0E040E050E019 +:1005F00021C0888199818617970769F48A819B8138 +:10060000309719F09383828304C09093F002809313 +:10061000EF02FE0134C06817790738F4411551051F +:1006200019F08417950708F4AC01FE018A819B81BB +:100630009C01E9012097E9F641155105A9F1CA018C +:10064000861B970B049708F4BA01E0E0F0E02AC09B +:100650008D919C91119784179507F9F4641775078C +:1006600081F412968D919C911397309719F0938392 +:10067000828304C09093F0028093EF02FD013296D2 +:100680004CC0CA01861B970BFD01E80FF91F61934F +:10069000719302978D939C9340C0FD018281938159 +:1006A0009C01D9011097A1F68091ED029091EE0284 +:1006B000892B41F480912301909124019093EE02C3 +:1006C0008093ED024091250150912601411551057D +:1006D00041F44DB75EB78091210190912201481BF2 +:1006E000590B2091ED023091EE02CA01821B930B4F +:1006F0008617970780F0AB014E5F5F4F8417950711 +:1007000050F0420F531F5093EE024093ED02F90157 +:100710006193719302C0E0E0F0E0CF01DF91CF91EF +:100720000895CF93DF93009709F450C0EC0122970E +:100730001B821A82A091EF02B091F002109709F18A +:1007400040E050E0AC17BD0708F1BB83AA83FE016F +:1007500021913191E20FF31FAE17BF0779F48D910C +:100760009C911197280F391F2E5F3F4F39832883A3 +:1007700012968D919C9113979B838A834115510505 +:1007800071F4D093F002C093EF0220C012968D91C5 +:100790009C911397AD01009711F0DC01D3CFFA01C2 +:1007A000D383C28321913191E20FF31FCE17DF076C +:1007B00069F488819981280F391F2E5F3F4FFA0114 +:1007C000318320838A819B8193838283DF91CF91C0 +:1007D0000895FC010590615070400110D8F7809594 +:1007E00090958E0F9F1F0895DC0101C06D934150BD +:1007F0005040E0F70895FC016150704001900110F5 +:10080000D8F7809590958E0F9F1F08950F931F9393 +:10081000CF93DF938C01EB018B8181FF1BC082FFA3 +:100820000DC02E813F818C819D812817390764F48A +:10083000E881F9810193F983E88306C0E885F985A9 +:10084000802F0995892B31F48E819F8101969F839A +:100850008E8302C00FEF1FEFC801DF91CF911F9170 +:100860000F910895FA01AA27283051F1203181F122 +:10087000E8946F936E7F6E5F7F4F8F4F9F4FAF4FA8 +:10088000B1E03ED0B4E03CD0670F781F891F9A1FBB +:10089000A11D680F791F8A1F911DA11D6A0F711D6F +:1008A000811D911DA11D20D009F468943F912AE07B +:1008B000269F11243019305D3193DEF6CF01089563 +:1008C000462F4770405D4193B3E00FD0C9F7F6CF94 +:1008D000462F4F70405D4A3318F0495D31FD40525C +:1008E000419302D0A9F7EACFB4E0A69597958795F2 +:1008F00077956795BA95C9F70097610571050895D1 +:100900009B01AC010A2E069457954795379527957C +:10091000BA95C9F7620F731F841F951FA01D089514 +:100920008AE391E068E049E00E9475070895FF922C +:100930000F931F93CF93DF9380E8E7E4F1E0DF01AB +:100940001D928A95E9F704E610E021C08AE391E060 +:100950006F2D0E94F2078AE391E00E94840880E8EC +:1009600090E00E94D7058AE391E00E947C078AE329 +:1009700091E00E944608882329F088819981019698 +:10098000998388832297FA94BFEFFB16F9F60150FA +:100990001040EFEF0F3F1E0729F0C5E4D2E08FE7CC +:1009A000F82ED4CFC7E4D1E000E011E000D000D0B1 +:1009B000ADB7BEB712961C930E931197899199917A +:1009C0008F70907014969C938E9313970E94D90009 +:1009D0000F900F900F900F90B2E0C734DB0731F704 +:1009E00000D083E091E0EDB7FEB7928381830E944F +:1009F000D9000F900F90DF91CF911F910F91FF9031 +:100A0000089580E895E060E070E00E948D00089510 +:100A10000F931F93CF93DF9384ED92E040E051EE6C +:100A200060E070E00E9454060E94010500D086E05C +:100A300091E0EDB7FEB7928381830E94D9000F90B9 +:100A40000F908AE391E00E944B088AE391E060E016 +:100A50000E94E8078AE391E00E9484088AE391E01B +:100A60000E947C07C0E0D0E000E011E000D000D0A0 +:100A7000EDB7FEB712830183CE0124E095958795EB +:100A80002A95E1F7948383830E94D90021960F90E1 +:100A90000F900F900F90C038D10541F700D083E040 +:100AA00091E0EDB7FEB7928381830E94D900C0E048 +:100AB000D0E00F900F9000D000D0EDB7FEB70183CB +:100AC0001283CE018F709070948383830E94D9002B +:100AD00021960F900F900F900F90C038D10559F7C5 +:100AE00000D083E091E0EDB7FEB7928381830E944E +:100AF000D9000F900F90DF91CF911F910F91089522 +:100B00000F931F93082F84ED92E0602F0E94280717 +:100B1000112707FD1095C8011F910F9108951F928D +:100B20000F920FB60F9211242F933F938F939F93A1 +:100B3000AF93BF9380914B0290914C02A0914D02D4 +:100B4000B0914E0230914F020196A11DB11D232F8D +:100B50002D5F2D3720F02D570196A11DB11D20933B +:100B60004F0280934B0290934C02A0934D02B0939E +:100B70004E028091470290914802A0914902B091A3 +:100B80004A020196A11DB11D80934702909348022D +:100B9000A0934902B0934A02BF91AF919F918F9168 +:100BA0003F912F910F900FBE0F901F9018950197B6 +:100BB00039F0880F991F880F991F02970197F1F755 +:100BC0000895789484B5826084BD84B5816084BDC5 +:100BD00085B5826085BD85B5816085BDEEE6F0E0B6 +:100BE000808181608083E1E8F0E010828081826012 +:100BF0008083808181608083E0E8F0E08081816093 +:100C00008083E1EBF0E0808184608083E0EBF0E0C2 +:100C1000808181608083EAE7F0E080818460808366 +:100C20008081826080838081816080838081806810 +:100C300080831092C10008950E94E1050E9408057A +:100C40000E949704FDCF1F920F920FB60F921124AE +:100C50002F933F934F938F939F93EF93FF934091E5 +:100C6000C600E091D002F091D10231969F012F771A +:100C7000307031978091D2029091D30228173907B2 +:100C800039F0E05BFD4F40833093D1022093D002D6 +:100C9000FF91EF919F918F914F913F912F910F90E5 +:100CA0000FBE0F901F901895AF92BF92DF92EF92F8 +:100CB000FF920F931F93CF93DF93EC017A018B0187 +:100CC000DD24403081EE580780E0680780E0780737 +:100CD00011F0DD24D39491E0A92EB12CE885F9859B +:100CE000DD2069F0C5010A8802C0880F991F0A94A7 +:100CF000E2F7808360E079E08DE390E005C0108248 +:100D000060E874E88EE190E0A80197010E949A09DA +:100D10002150304040405040569547953795279593 +:100D200080E12030380720F0DD2011F0DD24D6CF1F +:100D3000EC81FD813083EE81FF812083EA85FB8594 +:100D4000208141E050E0CA010E8402C0880F991F43 +:100D50000A94E2F7282B2083EA85FB852081CA01CB +:100D60000F8402C0880F991F0A94E2F7282B208372 +:100D7000EA85FB858081088802C0440F551F0A94CC +:100D8000E2F7842B8083DF91CF911F910F91FF9029 +:100D9000EF90DF90BF90AF900895DC011296ED9137 +:100DA000FC911397E058FF4F2191319180819181FF +:100DB000281B390B2F773070C9010895DC0112967A +:100DC000ED91FC911397EE57FF4F20813181929165 +:100DD0008291E058F0408217930719F42FEF3FEF0C +:100DE00005C0E20FF31F8081282F30E0C90108956C +:100DF000DC011296ED91FC911397DF01AE57BF4FC6 +:100E00002D913C911197E058FF4F80819181E058DE +:100E1000F0408217930719F42FEF3FEF0BC0E20F5A +:100E2000F31F80812F5F3F4F2F7730702D933C93BE +:100E3000282F30E0C9010895DC011296ED91FC9154 +:100E40001397EE57FF4F808191819293829308957B +:100E5000FC01A085B18521898C9190E0022E02C011 +:100E6000959587950A94E2F780FFF6CF0484F5857F +:100E7000E02D608308958BE291E09093D5028093FA +:100E8000D40280E592E09093D7028093D60285EC5D +:100E900090E09093D9028093D80284EC90E09093F4 +:100EA000DB028093DA0280EC90E09093DD02809385 +:100EB000DC0281EC90E09093DF028093DE0286EC0E +:100EC00090E09093E1028093E00284E08093E2025C +:100ED00083E08093E30287E08093E40285E08093DF +:100EE000E50281E08093E6020895FC01608341837E +:100EF00080E2828313820895FC01808160E00E9479 +:100F0000CD080895FF920F931F938C01F62E80E079 +:100F10000E94650985E00E946A09F80181816F2DB0 +:100F20000E94CD081F910F91FF9008951F93CF93BA +:100F3000DF93EC0160E070E00E94820781EE8EBDDD +:100F40000DB407FEFDCF1EB5CE0161E070E00E943A +:100F50008207812FDF91CF911F9108951F93CF9327 +:100F6000DF93EC0160E070E00E94820782EE8EBDAC +:100F70000DB407FEFDCF1EB5CE0161E070E00E940A +:100F80008207812FDF91CF911F9108950F931F93B7 +:100F9000CF93DF93EC01162F042F60E070E00E94E6 +:100FA00082071F7110621EBD0DB407FEFDCF1EB576 +:100FB0000EBD0DB407FEFDCF8EB5CE0161E070E031 +:100FC0000E948207812FDF91CF911F910F91089589 +:100FD000662319F061E04FE302C061E040E00E9447 +:100FE000C6070895462F603808F04FE765E00E9475 +:100FF000C6070895EF92FF920F931F93CF93DF934D +:101000007C01162FEA01022F60E070E00E94820747 +:101010001F7110621EBD0DB407FEFDCF1EB508C0C6 +:1010200088818EBD0DB407FEFDCF21968EB501508F +:101030000023B1F7C70161E070E00E948207812FB1 +:10104000DF91CF911F910F91FF90EF9008951F9323 +:10105000CF93DF93EC01162F60E070E00E948207CF +:101060001F711EBD0DB407FEFDCF8EB58FEF8EBD77 +:101070000DB407FEFDCF1EB5CE0161E070E00E9409 +:101080008207812FDF91CF911F91089569E00E941F +:101090002708817008950F931F938C01FC018081B4 +:1010A00061E00E94A708F801818161E00E94A70821 +:1010B000F801808160E00E94CD08C80161E070E025 +:1010C0000E9482070E947B0981E00E945C0980E007 +:1010D0000E94650985E00E946A09C80164E04FEF3B +:1010E0000E94C607C80167E040E70E94C607C80122 +:1010F0000E94AE07C8010E949607C80161E00E94E5 +:10110000F2071F910F9108950F931F938C0160E0D8 +:101110004BE00E94C607C80167E040E70E94C6078F +:10112000A8014B5F5F4FC8016AE025E00E94FA0703 +:10113000C8010E94AE07F801808161E00E94CD08DD +:1011400082E890E00E94D7051F910F910895482FE3 +:1011500050E0CA0186569F4FFC0124914A575F4FC9 +:10116000FA0184918823C1F0E82FF0E0EE0FFF1F11 +:10117000E859FF4FA591B491662341F49FB7F894C5 +:101180008C91209582238C939FBF08959FB7F894EC +:101190008C91822B8C939FBF0895482F50E0CA01F9 +:1011A00082559F4FFC012491CA0186569F4FFC0136 +:1011B00034914A575F4FFA019491992309F444C03E +:1011C000222351F1233071F0243028F42130A1F092 +:1011D000223011F514C02630B1F02730C1F0243090 +:1011E000D9F404C0809180008F7703C08091800083 +:1011F0008F7D8093800010C084B58F7702C084B546 +:101200008F7D84BD09C08091B0008F7703C080912D +:10121000B0008F7D8093B000E92FF0E0EE0FFF1F4C +:10122000EE58FF4FA591B491662341F49FB7F8940F +:101230008C91309583238C939FBF08959FB7F8942A +:101240008C91832B8C939FBF08950F931F93CF9303 +:10125000DF938C01EB0109C02196D801ED91FC913F +:101260000190F081E02DC801099568816623A1F7FE +:10127000DF91CF911F910F910895EF92FF920F93FD +:101280001F93CF93DF938C017B01EA010CC0D70140 +:101290006D917D01D801ED91FC910190F081E02DDF +:1012A000C80109952197209791F7DF91CF911F9160 +:1012B0000F91FF90EF900895882319F48CB5806208 +:1012C00002C08CB58F7D8CBD08959CB5937F982B03 +:1012D0009CBD08952CB5382F33702C7F322B3CBD2C +:1012E0002DB590E0959587959595879581702E7F82 +:1012F000822B8DBD08958DE061E00E94A7088BE0F0 +:1013000061E00E94A7088AE061E00E94A7088DE0E2 +:1013100060E00E94CD088BE060E00E94CD088AE08A +:1013200061E00E94CD088CB580618CBD8CB5806475 +:101330008CBD0895A1E21A2EAA1BBB1BFD010DC096 +:10134000AA1FBB1FEE1FFF1FA217B307E407F50775 +:1013500020F0A21BB30BE40BF50B661F771F881F51 +:10136000991F1A9469F760957095809590959B01E7 +:10137000AC01BD01CF0108952F923F924F925F9231 +:101380006F927F928F929F92AF92BF92CF92DF9295 +:10139000EF92FF920F931F93CF93DF93CDB7DEB7FA +:1013A000CA1BDB0B0FB6F894DEBF0FBECDBF09948E +:1013B0002A88398848885F846E847D848C849B84E5 +:1013C000AA84B984C884DF80EE80FD800C811B81F3 +:1013D000AA81B981CE0FD11D0FB6F894DEBF0FBE22 +:1013E000CDBFED010895EE0FFF1F0590F491E02DA4 +:0613F0000994F894FFCF00 +:1013F6002578000A0D000A0D524632342F657861B1 +:101406006D706C65732F7363616E6E65722F0A0D56 +:10141600002000F102000000000000280725093D19 +:0A14260009CD06F806DE061C0700DB +:00000001FF diff --git a/examples/scanner/printf.h b/examples/scanner/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/scanner/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.pde new file mode 100644 index 0000000..efefa17 --- /dev/null +++ b/examples/scanner/scanner.pde @@ -0,0 +1,125 @@ + +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// +// Channel info +// + +const short num_channels = 128; +short values[num_channels]; + +// +// Setup +// + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/scanner/\n\r"); + + // + // Setup and configure rf radio + // + + radio.begin(); + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + // Print out header, high then low digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n\r"); + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n\r"); +} + +// +// Loop +// + +const short num_reps = 100; + +void loop(void) +{ + // Clear measurement values + memset(values,0,num_channels); + + // Scan all channels num_reps times + int rep_counter = num_reps; + while (rep_counter--) + { + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(128); + radio.stopListening(); + + // Did we get a carrier? + if ( radio.testCarrier() ) + ++values[i]; + } + } + + // Print out channel measurements, clamped to a single hex digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,values[i]&0xf)); + ++i; + } + printf("\n\r"); +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp From a6e7444059c516115a31f988e657be5672a1ed71 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 25 May 2011 22:23:25 -0700 Subject: [PATCH 057/326] Added carrier detect and auto ack enable/disable --- RF24.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/RF24.h b/RF24.h index 6c8fffd..14cb59c 100644 --- a/RF24.h +++ b/RF24.h @@ -476,6 +476,17 @@ public: * will relay these transmissions. */ +/** + * @example scanner.pde + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + /** * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver * From 4131420de79456be80e218523bf1c3c0ab36b2a1 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 26 May 2011 20:48:30 -0700 Subject: [PATCH 058/326] Added setDataRate() --- RF24.cpp | 10 ++++++++++ RF24.h | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index e0fe3c0..5f89db7 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -608,5 +608,15 @@ boolean RF24::testCarrier(void) return ( read_register(CD) & 1 ); } +/******************************************************************/ + +void RF24::setDataRate(rf24_datarate_e speed) +{ + uint8_t setup = read_register(RF_SETUP) & RF_DR; + if (speed == RF24_2MBPS) + setup |= RF_DR; + write_register(RF_SETUP,setup); +} + // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 14cb59c..53f71ff 100644 --- a/RF24.h +++ b/RF24.h @@ -11,6 +11,8 @@ #include +typedef enum { RF24_1MBPS = 0, RF24_2MBPS } rf24_datarate_e; + /** * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver */ @@ -420,6 +422,13 @@ public: */ boolean testCarrier(void); + + /** + * Set the transmission data rate + * + * @param speed RF24_1MBPS for 1Mbps or RF24_2MBPS for 2Mbps + */ + void setDataRate(rf24_datarate_e speed); /**@}*/ }; From af9261cf930da1733c43466a2e88a34556ed281f Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 27 May 2011 19:32:36 -0700 Subject: [PATCH 059/326] Added 3rd FTDI port for my own use --- examples/pingpair/makefile | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/examples/pingpair/makefile b/examples/pingpair/makefile index a1c6d4c..15cb43b 100644 --- a/examples/pingpair/makefile +++ b/examples/pingpair/makefile @@ -64,8 +64,9 @@ ARDUINO_VERSION = 21 ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs +PORT = /dev/tty.usbserial-A600eHIs PORT2 = /dev/tty.usbserial-A9007LmI +PORT3 = /dev/tty.usbserial-A40081RP else ARDUINO_VERSION = 22 ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) @@ -75,6 +76,12 @@ PORT = /dev/ttyUSB0 PORT2 = /dev/ttyUSB1 endif +# Temporary testing of github Arduino environment +OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc +ARDUINO_DIR = /opt/Arduino + PROJECT_NAME = $(notdir $(PWD)) PROJECT_DIR = . ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino @@ -103,6 +110,7 @@ $(ARDUINO_CORE)/WMath.cpp \ $(ARDUINO_CORE)/Print.cpp \ $(ARDUINO_CORE)/HardwareSerial.cpp \ $(ARDUINO_LIB)/SPI/SPI.cpp \ +$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ ../../RF24.cpp CXX_APP = output/$(PROJECT_NAME).cpp @@ -127,7 +135,7 @@ CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) # Place -I options here -CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. +CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) @@ -208,10 +216,17 @@ lss: output/$(PROJECT_NAME).lss #sym: output/$(PROJECT_NAME).sym # Upload HEX file to Arduino -upload: output/$(PROJECT_NAME).hex +upload: upload1 upload2 + +upload1: output/$(PROJECT_NAME).hex $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + +upload2: output/$(PROJECT_NAME).hex $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) +upload3: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) + sym: $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym From ec33f0336880ac530623f4e94ba32234140e94b0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 27 May 2011 19:32:59 -0700 Subject: [PATCH 060/326] Made all CONFIG register changes incremental not absolute --- RF24.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 5f89db7..531401e 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -305,6 +305,9 @@ void RF24::begin(void) // Reset current status write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Initialize CRC + write_register(CONFIG, _BV(EN_CRC) ); // Flush buffers flush_rx(); @@ -318,7 +321,7 @@ void RF24::begin(void) void RF24::startListening(void) { - write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP) | _BV(PRIM_RX)); + write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Restore the pipe0 adddress @@ -345,7 +348,7 @@ void RF24::stopListening(void) void RF24::powerDown(void) { - write_register(CONFIG,0); + write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); } /******************************************************************/ @@ -355,7 +358,7 @@ boolean RF24::write( const void* buf, uint8_t len ) boolean result = false; // Transmitter power-up - write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP)); + write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); delay(2); // Send the payload @@ -401,7 +404,7 @@ boolean RF24::write( const void* buf, uint8_t len ) ce(LOW); // Power down - write_register(CONFIG, _BV(EN_CRC) ); + write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); // Reset current status write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); From 34efd5ac94a5c5fd173a26fd11d5ef9b50c25b65 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 27 May 2011 20:09:29 -0700 Subject: [PATCH 061/326] Added setCRCLength --- RF24.cpp | 11 +++++++++++ RF24.h | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 531401e..e5110f1 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -619,6 +619,17 @@ void RF24::setDataRate(rf24_datarate_e speed) if (speed == RF24_2MBPS) setup |= RF_DR; write_register(RF_SETUP,setup); + +} + +/******************************************************************/ + +void RF24::setCRCLength(rf24_crclength_e length) +{ + uint8_t config = read_register(CONFIG) & _BV(CRCO); + if (length == RF24_CRC_16) + config |= _BV(CRCO); + write_register(CONFIG,config); } // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 53f71ff..3165947 100644 --- a/RF24.h +++ b/RF24.h @@ -12,6 +12,7 @@ #include typedef enum { RF24_1MBPS = 0, RF24_2MBPS } rf24_datarate_e; +typedef enum { RF24_CRC_8 = 0, RF24_CRC_16 } rf24_crclength_e; /** * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver @@ -420,7 +421,6 @@ public: * * @return true if was carrier, false if not */ - boolean testCarrier(void); /** @@ -429,6 +429,13 @@ public: * @param speed RF24_1MBPS for 1Mbps or RF24_2MBPS for 2Mbps */ void setDataRate(rf24_datarate_e speed); + + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); /**@}*/ }; From 731c472abc08540a7cb85cd72819ed3930f1f95f Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 27 May 2011 20:11:38 -0700 Subject: [PATCH 062/326] Fixed a bug in setDataRate --- RF24.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index e5110f1..d3854fb 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -617,7 +617,7 @@ void RF24::setDataRate(rf24_datarate_e speed) { uint8_t setup = read_register(RF_SETUP) & RF_DR; if (speed == RF24_2MBPS) - setup |= RF_DR; + setup |= _BV(RF_DR); write_register(RF_SETUP,setup); } From ad99581285b9097fd90de288e73a97843a1091ee Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 3 Jun 2011 06:42:47 -0700 Subject: [PATCH 063/326] Added a new example --- examples/led_remote/.gitignore | 1 + examples/led_remote/led_remote.pde | 247 +++++++++++++++++++++++ examples/led_remote/makefile | 305 +++++++++++++++++++++++++++++ examples/led_remote/printf.h | 33 ++++ 4 files changed, 586 insertions(+) create mode 100644 examples/led_remote/.gitignore create mode 100644 examples/led_remote/led_remote.pde create mode 100644 examples/led_remote/makefile create mode 100644 examples/led_remote/printf.h diff --git a/examples/led_remote/.gitignore b/examples/led_remote/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/led_remote/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde new file mode 100644 index 0000000..e3cb84d --- /dev/null +++ b/examples/led_remote/led_remote.pde @@ -0,0 +1,247 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example LED Remote + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * On the 'remote', connect any number of buttons or switches from + * an arduino pin to ground. Update 'button_pins' to reflect the + * pins used. + * + * On the 'led' board, connect the same number of LED's from an + * arduino pin to a resistor to ground. Update 'led_pins' to reflect + * the pins used. Also connect a separate pin to ground and change + * the 'role_pin'. This tells the sketch it's running on the LED board. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver +// Leave open to be the 'remote' transmitter +const int role_pin = A4; + +// Pins on the remote for buttons +const uint8_t button_pins[] = { 2,3,4,5,6,7 }; +const uint8_t num_button_pins = sizeof(button_pins); + +// Pins on the LED board for LED's +const uint8_t led_pins[] = { 2,3,4,5,6,7 }; +const uint8_t num_led_pins = sizeof(led_pins); + +// +// Topology +// + +// Single radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_remote = 1, role_led } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Remote", "LED Board"}; + +// The role of the current running sketch +role_e role; + +// +// Payload +// + +uint8_t button_states[num_button_pins]; + +// +// Setup +// + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_remote; + else + role = role_led; + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/led_remote/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens a single pipes for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. + + if ( role == role_remote ) + { + radio.openWritingPipe(pipe); + } + else + { + radio.openReadingPipe(1,pipe); + } + + // + // Start listening + // + + if ( role == role_led ) + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Set up buttons / LED's + // + + // Set pull-up resistors for all buttons + if ( role == role_remote ) + { + int i = num_button_pins; + while(i--) + { + pinMode(button_pins[i],INPUT); + digitalWrite(button_pins[i],HIGH); + } + } + + // Turn LED's ON until we start getting keys + if ( role == role_led ) + { + int i = num_button_pins; + while(i--) + { + pinMode(button_pins[i],OUTPUT); + digitalWrite(button_pins[i],HIGH); + } + } + +} + +// +// Loop +// + +void loop(void) +{ + // + // Remote role. If the state of any button has changed, send the whole state of + // all buttons. + // + + if ( role == role_remote ) + { + // Get the current state of buttons, and + // Test if the current state is different from the last state we sent + int i = num_button_pins; + bool different = false; + while(i--) + { + uint8_t state = ! digitalRead(button_pins[i]); + if ( state != button_states[i] ) + { + different = true; + button_states[i] = state; + } + } + + // Send the state of the buttons to the LED board + if ( different ) + { + printf("Now sending..."); + bool ok = radio.write( button_states, num_button_pins ); + if (ok) + printf("ok\n\r"); + else + printf("failed\n\r"); + } + + // Try again in a short while + delay(20); + } + + // + // LED role. Receive the state of all buttons, and reflect that in the LEDs + // + + if ( role == role_led ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( button_states, num_button_pins ); + + // Spew it + printf("Got buttons\n\r"); + + // Set all the LED's according to the buttons + int i = num_led_pins; + while(i--) + digitalWrite(led_pins[i],button_states[i]); + } + } + } +} +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/led_remote/makefile b/examples/led_remote/makefile new file mode 100644 index 0000000..493a159 --- /dev/null +++ b/examples/led_remote/makefile @@ -0,0 +1,305 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +PORT3 = /dev/tty.usbserial-A40081RP +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +# Temporary testing of github Arduino environment +OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc +ARDUINO_DIR = /opt/Arduino + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I$(MY_LIB)/Bounce -I$(MY_LIB)/ButtonSpace -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: upload1 upload2 + +upload1: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + +upload2: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +upload3: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/led_remote/printf.h b/examples/led_remote/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/led_remote/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From a62338f3a7a855aca9f8af7cd7d3479faa7f149e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 4 Jun 2011 15:20:53 -0700 Subject: [PATCH 064/326] Added led_remote to docs --- RF24.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/RF24.h b/RF24.h index 3165947..465a0e7 100644 --- a/RF24.h +++ b/RF24.h @@ -440,6 +440,16 @@ public: /**@}*/ }; +/** + * @example led_remote + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + /** * @example pingpair.pde * From bec84a79075b86c71695e668c5cc281c81d4ec96 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 4 Jun 2011 15:25:47 -0700 Subject: [PATCH 065/326] Fixed led_remote in docs --- RF24.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.h b/RF24.h index 465a0e7..29264c4 100644 --- a/RF24.h +++ b/RF24.h @@ -441,7 +441,7 @@ public: }; /** - * @example led_remote + * @example led_remote.pde * * This is an example of how to use the RF24 class to control a remote * bank of LED's using buttons on a remote control. From 26c45e85a9dab5586d56fa649657c3a383bc9753 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 4 Jun 2011 16:01:43 -0700 Subject: [PATCH 066/326] astyle -A1 --- RF24.h | 788 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 394 insertions(+), 394 deletions(-) diff --git a/RF24.h b/RF24.h index 29264c4..386b628 100644 --- a/RF24.h +++ b/RF24.h @@ -1,6 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. @@ -17,427 +17,427 @@ typedef enum { RF24_CRC_8 = 0, RF24_CRC_16 } rf24_crclength_e; /** * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver */ - + class RF24 { private: - uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ - uint8_t csn_pin; /**< SPI Chip select */ - uint8_t payload_size; /**< Fixed size of payloads */ - boolean ack_payload_available; /**< Whether there is an ack payload waiting */ - uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ - uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + uint8_t payload_size; /**< Fixed size of payloads */ + boolean ack_payload_available; /**< Whether there is an ack payload waiting */ + uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ -protected: - /** - * @name Low-level internal interface. - * - * Protected methods that address the chip directly. Regular users cannot - * ever call these. They are documented for completeness and for developers who - * may want to extend this class. - */ - /**@{*/ +protected: + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. + */ + /**@{*/ - /** - * Set chip select pin - * - * @param mode HIGH to take this unit off the SPI bus, LOW to put it on - */ - void csn(int mode) ; + /** + * Set chip select pin + * + * @param mode HIGH to take this unit off the SPI bus, LOW to put it on + */ + void csn(int mode) ; - /** - * Set chip enable - * - * @param mode HIGH to actively begin transmission or LOW to put in standby. Please see data sheet - * for a much more detailed description of this pin. - */ - void ce(int mode); + /** + * Set chip enable + * + * @param mode HIGH to actively begin transmission or LOW to put in standby. Please see data sheet + * for a much more detailed description of this pin. + */ + void ce(int mode); - /** - * Read a chunk of data in from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to put the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; - - /** - * Read single byte from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @return Current value of register @p reg - */ - uint8_t read_register(uint8_t reg); + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; - /** - * Write a chunk of data to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to get the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); - /** - * Write a single byte to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param value The new value to write - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, uint8_t value); + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); - /** - * Write the transmit payload - * - * The size of data written is the fixed payload size, see getPayloadSize() - * - * @param buf Where to get the data - * @param len Number of bytes to be sent - * @return Current value of status register - */ - uint8_t write_payload(const void* buf, uint8_t len); + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); - /** - * Read the receive payload - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @param buf Where to put the data - * @param len Maximum number of bytes to read - * @return Current value of status register - */ - uint8_t read_payload(void* buf, uint8_t len) ; + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @param len Number of bytes to be sent + * @return Current value of status register + */ + uint8_t write_payload(const void* buf, uint8_t len); - /** - * Read the payload length - * - * For dynamic payloads, this pulls the size of the payload off - * the chip - * - * @return Payload length of last-received dynamic payload - */ - uint8_t read_payload_length(void); - - /** - * Empty the receive buffer - * - * @return Current value of status register - */ - uint8_t flush_rx(void); - - /** - * Empty the transmit buffer - * - * @return Current value of status register - */ - uint8_t flush_tx(void); + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register + */ + uint8_t read_payload(void* buf, uint8_t len) ; - /** - * Retrieve the current status of the chip - * - * @return Current value of status register - */ - uint8_t get_status(void) ; + /** + * Read the payload length + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @return Payload length of last-received dynamic payload + */ + uint8_t read_payload_length(void); - /** - * Decode and print the given status to stdout - * - * @param status Status value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_status(uint8_t status) ; + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); - /** - * Decode and print the given 'observe_tx' value to stdout - * - * @param value The observe_tx value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_observe_tx(uint8_t value) ; + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); - /** - * Turn on or off the special features of the chip - * - * The chip has certain 'features' which are only available when the 'features' - * are enabled. See the datasheet for details. - */ - void toggle_features(void); - /**@}*/ + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void) ; + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status) ; + + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value) ; + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); + /**@}*/ public: - /** - * @name Primary public interface - * - * These are the main methods you need to operate the chip - */ - /**@{*/ + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ - /** - * Constructor - * - * Creates a new instance of this driver. Before using, you create an instance - * and send in the unique pins that this chip is connected to. - * - * @param _cepin The pin attached to Chip Enable on the RF module - * @param _cspin The pin attached to Chip Select - */ - RF24(uint8_t _cepin, uint8_t _cspin); + /** + * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(uint8_t _cepin, uint8_t _cspin); - /** - * Begin operation of the chip - * - * Call this in setup(), before calling any other methods. - */ - void begin(void); + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + */ + void begin(void); - /** - * Start listening on the pipes opened for reading. - * - * Be sure to call openReadingPipe() first. Do not call write() while - * in this mode, without first calling stopListening(). Call - * isAvailable() to check for incoming traffic, and read() to get it. - */ - void startListening(void); + /** + * Start listening on the pipes opened for reading. + * + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. + */ + void startListening(void); - /** - * Stop listening for incoming messages - * - * Do this before calling write(). - */ - void stopListening(void); + /** + * Stop listening for incoming messages + * + * Do this before calling write(). + */ + void stopListening(void); - /** - * Write to the open writing pipe - * - * Be sure to call openWritingPipe() first to set the destination - * of where to write to. - * - * This blocks until the message is successfully acknowledged by - * the receiver or the timeout/retransmit maxima are reached. In - * the current configuration, the max delay here is 60ms. - * - * The maximum size of data written is the fixed payload size, see - * getPayloadSize(). However, you can write less, and the remainder - * will just be filled with zeroes. - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - boolean write( const void* buf, uint8_t len ); + /** + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + boolean write( const void* buf, uint8_t len ); - /** - * Test whether there are bytes available to be read - * - * @return True if there is a payload available, false if none is - */ - boolean available(void) ; + /** + * Test whether there are bytes available to be read + * + * @return True if there is a payload available, false if none is + */ + boolean available(void) ; - /** - * Read the payload - * - * Return the last payload received - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @note I specifically chose 'void*' as a data type to make it easier - * for beginners to use. No casting needed. - * - * @param buf Pointer to a buffer where the data should be written - * @param len Maximum number of bytes to read into the buffer - * @return True if the payload was delivered successfully false if not - */ - boolean read( void* buf, uint8_t len ) ; + /** + * Read the payload + * + * Return the last payload received + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer + * @return True if the payload was delivered successfully false if not + */ + boolean read( void* buf, uint8_t len ) ; - /** - * Open a pipe for writing - * - * Only one pipe can be open at once, but you can change the pipe - * you'll listen to. Do not call this while actively listening. - * Remember to stopListening() first. - * - * Addresses are 40-bit hex values, e.g.: - * - * @code - * openWritingPipe(0xF0F0F0F0F0); - * @endcode - * - * @param address The 40-bit address of the pipe to open. This can be - * any value whatsoever, as long as you are the only one writing to it - * and only one other radio is listening to it. Coordinate these pipe - * addresses amongst nodes on the network. - */ - void openWritingPipe(uint64_t address); + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param address The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); - /** - * Open a pipe for reading - * - * Up to 5 pipes can be open for reading at once. Open all the - * reading pipes, and then call startListening(). - * - * @see openWritingPipe - * - * @warning Pipes 1-5 should share the first 32 bits. - * Only the least significant byte should be unique, e.g. - * - * @code - * openReadingPipe(1,0xF0F0F0F0AA); - * openReadingPipe(2,0xF0F0F0F066); - * @endcode - * - * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits - * - * @param number Which pipe# to open, 0-5. - * @param address The 40-bit address of the pipe to open. - */ - void openReadingPipe(uint8_t number, uint64_t address); + /** + * Open a pipe for reading + * + * Up to 5 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning Pipes 1-5 should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * + * @code + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); + * @endcode + * + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits + * + * @param number Which pipe# to open, 0-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); - /**@}*/ - /** - * @name Optional public interface - * - * Methods you may want to use but are not needed for regular operation - */ - /**@{*/ + /**@}*/ + /** + * @name Optional public interface + * + * Methods you may want to use but are not needed for regular operation + */ + /**@{*/ - /** - * Set RF communication channel - * - * @param channel Which RF channel to communicate on, 0-127 - */ - void setChannel(uint8_t channel); + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); - /** - * Set Payload Size - * - * This implementation uses a pre-stablished fixed payload size for all - * transmissions. If this method is never called, the driver will always - * transmit the maximum payload size (32 bytes), no matter how much - * was sent to write(). - * - * @todo Implement variable-sized payloads feature - * - * @param size The number of bytes in the payload - */ - void setPayloadSize(uint8_t size); + /** + * Set Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); - /** - * Get Payload Size - * - * @see setPayloadSize() - * - * @return The number of bytes in the payload - */ - uint8_t getPayloadSize(void) ; + /** + * Get Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void) ; - /** - * Print a giant block of debugging information to stdout - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void printDetails(void) ; - - /** - * Enter low-power mode - * - * To return to normal power mode, either write() some data or - * startListening(). - */ - void powerDown(void); + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void) ; - /** - * Test whether there are bytes available to be read - * - * Use this version to discover on which pipe the message - * arrived. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - boolean available(uint8_t* pipe_num); + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening(). + */ + void powerDown(void); - /** - * Enable custom payloads on the acknowledge packets - * - * Ack payloads are a handy way to return data back to senders without - * manually changing the radio modes on both units. - * - * @see examples/pingpair_pl/pingpair_pl.pde - */ - void enableAckPayload(void); + /** + * Test whether there are bytes available to be read + * + * Use this version to discover on which pipe the message + * arrived. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + boolean available(uint8_t* pipe_num); - /** - * Write an ack payload for the specified pipe - * - * The next time a message is received on @p pipe, the data in @p buf will - * be sent back in the acknowledgement. - * - * @warning According to the data sheet, only three of these can be pending - * at any time. I have not tested this. - * - * @param pipe Which pipe# (typically 1-5) will get this response. - * @param buf Pointer to data that is sent - * @param len Length of the data to send, up to 32 bytes max. Not affected - * by the static payload set by setPayloadSize(). - */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde + */ + void enableAckPayload(void); - /** - * Determine if an ack payload was received in the most recent call to - * write(). - * - * Call read() to retrieve the ack payload. - * - * @warning Calling this function clears the internal flag which indicates - * a payload is available. If it returns true, you must read the packet - * out as the very next interaction with the radio, or the results are - * undefined. - * - * @return True if an ack payload is available. - */ - boolean isAckPayloadAvailable(void); + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time. I have not tested this. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); - /** - * Enable or disable auto-acknowlede packets - * - * This is enabled by default, so it's only needed if you want to turn - * it off for some reason. - * - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck(bool enable); + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @warning Calling this function clears the internal flag which indicates + * a payload is available. If it returns true, you must read the packet + * out as the very next interaction with the radio, or the results are + * undefined. + * + * @return True if an ack payload is available. + */ + boolean isAckPayloadAvailable(void); - /** - * Test whether there was a carrier on the line for the - * previous listening period. - * - * Useful to check for interference on the current channel. - * - * @return true if was carrier, false if not - */ - boolean testCarrier(void); + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); - /** - * Set the transmission data rate - * - * @param speed RF24_1MBPS for 1Mbps or RF24_2MBPS for 2Mbps - */ - void setDataRate(rf24_datarate_e speed); - - /** - * Set the CRC length - * - * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - void setCRCLength(rf24_crclength_e length); - - /**@}*/ + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + boolean testCarrier(void); + + /** + * Set the transmission data rate + * + * @param speed RF24_1MBPS for 1Mbps or RF24_2MBPS for 2Mbps + */ + void setDataRate(rf24_datarate_e speed); + + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); + + /**@}*/ }; /** @@ -452,8 +452,8 @@ public: /** * @example pingpair.pde - * - * This is an example of how to use the RF24 class. Write this sketch to two + * + * This is an example of how to use the RF24 class. Write this sketch to two * different nodes, connect the role_pin to ground on one. The ping node sends * the current time to the pong node, which responds by sending the value back. * The ping node can then see how long the whole cycle took. @@ -462,10 +462,10 @@ public: /** * @example starping.pde * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will + * unit will send out the value of millis() once a second. The pong unit will * respond back with a copy of the value. Each ping unit can get that response * back, and determine how long the whole cycle took. * @@ -475,16 +475,16 @@ public: */ /** - * @example pingpair_pl.pde + * @example pingpair_pl.pde * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within * the Ack packet of each transmission. Note that the payload is set BEFORE * the sender's message arrives. */ /** - * @example pingpair_sleepy.pde + * @example pingpair_sleepy.pde * * This is an example of how to use the RF24 class to create a battery- * efficient system. It is just like the pingpair.pde example, but the @@ -495,7 +495,7 @@ public: /** * @example starping_relay.pde * - * This sketch is a very complex example of using the RF24 library for Arduino. + * This sketch is a very complex example of using the RF24 library for Arduino. * Deploy this on any number of nodes to create a basic mesh network. I have * tested this on 6 nodes, but it should work on many more. 'Leaf' nodes attempt * to send a ping every 2 seconds to the 'Base' node. Optional 'Relay' nodes @@ -503,7 +503,7 @@ public: */ /** - * @example scanner.pde + * @example scanner.pde * * Example to detect interference on the various channels available. * This is a good diagnostic tool to check whether you're picking a From 1a37c1cd0ff12675ea983f921b445771b0e699da Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Tue, 21 Jun 2011 10:26:16 -0500 Subject: [PATCH 067/326] New P variant feature support with corresponding defines. RF_DR is now broken into two bits to support P variant and is now obsolete. RF_DR is now RF_DR_HIGH and RF_DR_LOW; with RF_DR_HIGH corresponding to the original RF_DR bit. Data rate can now select 250kbs mode on supporting hardware. Its now possible to both get and set the current PA level. LNA is obsolete in P variant. CD is obsoleted in P variant. Can now check both CD and RDP levels. Will follow through in the future to determine if there is any real difference. Aside from dBm floor and slight changes in internal behavior, they appear identical in interface and function. Fixes a bug in openReadingPipe which was excluding the 5th pipline. We have 6-pipelines, 0-5, so excluding the 5th (<5) would only allow for four active pipelines. --- RF24.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++---- RF24.h | 34 ++++++++++++++++++-- nRF24L01.h | 14 ++++++-- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index d3854fb..f443dbe 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -514,7 +514,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) if (child == 0) pipe0_reading_address = value; - if (child < 5) + if (child < 6) { // For pipes 2-5, only write the LSB if ( child < 2 ) @@ -524,7 +524,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) write_register(child_payload_size[child],payload_size); - // Note this is kind of an inefficient way to set up these enable bits, bit I thought it made + // Note this is kind of an inefficient way to set up these enable bits, but I thought it made // the calling code more simple uint8_t en_rx; read_register(EN_RXADDR,&en_rx,1); @@ -613,13 +613,95 @@ boolean RF24::testCarrier(void) /******************************************************************/ +boolean RF24::testRPD(void) +{ + return ( read_register(RPD) & 1 ) ; +} + +/******************************************************************/ + +void RF24::setPALevel(rf24_pa_dbm_e level) +{ + uint8_t setup = read_register(RF_SETUP) ; + setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; + + switch( level ) + { + case RF24_PA_MAX: + setup |= RF_PWR_0DB ; + break ; + + case RF24_PA_HIGH: + setup |= RF_PWR_6DB ; + break ; + + case RF24_PA_LOW: + setup |= RF_PWR_12DB ; + break ; + + case RF24_PA_MIN: + setup |= RF_PWR_18DB ; + break ; + } + + write_register( RF_SETUP, setup ) ; +} + +/******************************************************************/ + +rf24_pa_dbm_e RF24::getPALevel(void) +{ + rf24_pa_dbm_e result = RF24_PA_ERROR ; + uint8_t power = read_register(RF_SETUP) & RF_PWR ; + + switch( power ) + { + case RF_PWR_0DB: + result = RF24_PA_MAX ; + break ; + + case RF_PWR_6DB: + result = RF24_PA_HIGH ; + break ; + + case RF_PWR_12DB: + result = RF24_PA_LOW ; + break ; + + case RF_PWR_18DB: + result = RF24_PA_MIN ; + break ; + } + + return result ; +} + +/******************************************************************/ + void RF24::setDataRate(rf24_datarate_e speed) { - uint8_t setup = read_register(RF_SETUP) & RF_DR; - if (speed == RF24_2MBPS) - setup |= _BV(RF_DR); - write_register(RF_SETUP,setup); + uint8_t setup = read_register(RF_SETUP) ; + // HIGH and LOW '00' is 1Mbs - our default + setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; + if( speed == RF24_250KBPS ) + { + // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 + // Making it '10'. + setup |= _BV( RF_DR_LOW ) ; + } + else + { + // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 + // Making it '01' + if ( speed == RF24_2MBPS ) + { + setup |= _BV(RF_DR_HIGH); + } + + } + + write_register(RF_SETUP,setup); } /******************************************************************/ diff --git a/RF24.h b/RF24.h index 386b628..70c5028 100644 --- a/RF24.h +++ b/RF24.h @@ -11,7 +11,8 @@ #include -typedef enum { RF24_1MBPS = 0, RF24_2MBPS } rf24_datarate_e; +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; typedef enum { RF24_CRC_8 = 0, RF24_CRC_16 } rf24_crclength_e; /** @@ -423,10 +424,39 @@ public: */ boolean testCarrier(void); + /** + * Test whether a signal (carrier or otherwise) greater than + * or equal to -64dBm is present on the channel. Valid only + * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * + * Useful to check for interference on the current channel and + * channel hopping strategies. + * + * @return true if signal => -64dBm, false if not + */ + boolean testRPD(void); + + /** + * Set Power Amplifier (PA) level to one of four levels. + * Relative mnemonics have been used to allow for future PA level + * changes. According to 6.5 of the nRF24L01+ specification sheet, + * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, + * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. + * + * @param Desired PA level. + */ + void setPALevel( rf24_pa_dbm_e level ) ; + + /** + * Fetches the current PA level. See setPALevel for + * return value definitions. + */ + rf24_pa_dbm_e getPALevel( void ) ; + /** * Set the transmission data rate * - * @param speed RF24_1MBPS for 1Mbps or RF24_2MBPS for 2Mbps + * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps */ void setDataRate(rf24_datarate_e speed); diff --git a/nRF24L01.h b/nRF24L01.h index 5f4d1b8..cc3b5bd 100644 --- a/nRF24L01.h +++ b/nRF24L01.h @@ -33,6 +33,7 @@ #define STATUS 0x07 #define OBSERVE_TX 0x08 #define CD 0x09 +#define RPD 0x09 #define RX_ADDR_P0 0x0A #define RX_ADDR_P1 0x0B #define RX_ADDR_P2 0x0C @@ -73,10 +74,17 @@ #define AW 0 #define ARD 4 #define ARC 0 +#define RF_DR_LOW 5 #define PLL_LOCK 4 -#define RF_DR 3 -#define RF_PWR 1 -#define LNA_HCURR 0 +#define RF_DR_HIGH 3 +#define RF_PWR 6 +#define RF_PWR_LOW 1 +#define RF_PWR_HIGH 2 +#define RF_PWR_0DB 6 +#define RF_PWR_6DB 4 +#define RF_PWR_12DB 2 +#define RF_PWR_18DB 0 +#define LNA_HCURR 0 #define RX_DR 6 #define TX_DS 5 #define MAX_RT 4 From b1a7dc7d54d51d5ffa7291ecbd6d9dd0f2e53b64 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Tue, 21 Jun 2011 14:36:02 -0500 Subject: [PATCH 068/326] Adds constantness to method declarations and arguments where possible. Added the concept of wide band. Currently not used but state is properly tracked in accordance with data rate selection. Default channel is changed to 64 in begin method. That needs to move anyways. Automatic ACKs can now be adjusted on a per pipeline bases. --- RF24.cpp | 95 ++++++++++++++++++++++++++++++++++---------------------- RF24.h | 74 +++++++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 67 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index f443dbe..155908a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -26,7 +26,7 @@ /******************************************************************/ -void RF24::csn(int mode) +void RF24::csn(const int mode) const { SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV8); @@ -35,14 +35,14 @@ void RF24::csn(int mode) /******************************************************************/ -void RF24::ce(int mode) +void RF24::ce(const int mode) const { digitalWrite(ce_pin,mode); } /******************************************************************/ -uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +uint8_t RF24::read_register(const uint8_t reg, uint8_t* buf, uint8_t len) const { uint8_t status; @@ -58,7 +58,7 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) /******************************************************************/ -uint8_t RF24::read_register(uint8_t reg) +uint8_t RF24::read_register(const uint8_t reg) const { csn(LOW); SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); @@ -70,7 +70,7 @@ uint8_t RF24::read_register(uint8_t reg) /******************************************************************/ -uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +uint8_t RF24::write_register(const uint8_t reg, const uint8_t* buf, uint8_t len) const { uint8_t status; @@ -86,7 +86,7 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) /******************************************************************/ -uint8_t RF24::write_register(uint8_t reg, uint8_t value) +uint8_t RF24::write_register(const uint8_t reg, const uint8_t value) const { uint8_t status; @@ -144,7 +144,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) /******************************************************************/ -uint8_t RF24::flush_rx(void) +uint8_t RF24::flush_rx(void) const { uint8_t status; @@ -157,7 +157,7 @@ uint8_t RF24::flush_rx(void) /******************************************************************/ -uint8_t RF24::flush_tx(void) +uint8_t RF24::flush_tx(void) const { uint8_t status; @@ -170,7 +170,7 @@ uint8_t RF24::flush_tx(void) /******************************************************************/ -uint8_t RF24::get_status(void) +uint8_t RF24::get_status(void) const { uint8_t status; @@ -183,7 +183,7 @@ uint8_t RF24::get_status(void) /******************************************************************/ -void RF24::print_status(uint8_t status) +void RF24::print_status(uint8_t status) const { printf_P(PSTR("STATUS=%02x: RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), status, @@ -197,7 +197,7 @@ void RF24::print_status(uint8_t status) /******************************************************************/ -void RF24::print_observe_tx(uint8_t value) +void RF24::print_observe_tx(uint8_t value) const { printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r"), value, @@ -209,7 +209,7 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) + wide_band(true), ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) { } @@ -217,12 +217,16 @@ RF24::RF24(uint8_t _cepin, uint8_t _cspin): void RF24::setChannel(uint8_t channel) { - write_register(RF_CH,min(channel,127)); + if( wide_band ) { + write_register(RF_CH,min(channel,127)); + } else { + write_register(RF_CH,min(channel,127)); + } } /******************************************************************/ -void RF24::setPayloadSize(uint8_t size) +void RF24::setPayloadSize(const uint8_t size) { payload_size = min(size,32); } @@ -236,7 +240,7 @@ uint8_t RF24::getPayloadSize(void) /******************************************************************/ -void RF24::printDetails(void) +void RF24::printDetails(void) const { uint8_t buffer[5]; uint8_t status = read_register(RX_ADDR_P0,buffer,5); @@ -314,18 +318,19 @@ void RF24::begin(void) flush_tx(); // Set up default configuration. Callers can always change it later. - setChannel(1); + // Hardware default is channel 2 - even spacing implied. + setChannel(64); } /******************************************************************/ -void RF24::startListening(void) +void RF24::startListening(void) const { write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Restore the pipe0 adddress - write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); // Flush buffers flush_rx(); @@ -339,14 +344,14 @@ void RF24::startListening(void) /******************************************************************/ -void RF24::stopListening(void) +void RF24::stopListening(void) const { ce(LOW); } /******************************************************************/ -void RF24::powerDown(void) +void RF24::powerDown(void) const { write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); } @@ -431,14 +436,14 @@ uint8_t RF24::read_payload_length(void) /******************************************************************/ -boolean RF24::available(void) +boolean RF24::available(void) const { return available(NULL); } /******************************************************************/ -boolean RF24::available(uint8_t* pipe_num) +boolean RF24::available(uint8_t* pipe_num) const { uint8_t status = get_status(); IF_SERIAL_DEBUG(print_status(status)); @@ -489,7 +494,7 @@ boolean RF24::read( void* buf, uint8_t len ) void RF24::openWritingPipe(uint64_t value) { - // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01 + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) // expects it LSB first too, so we're good. write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); @@ -499,7 +504,7 @@ void RF24::openWritingPipe(uint64_t value) /******************************************************************/ -void RF24::openReadingPipe(uint8_t child, uint64_t value) +void RF24::openReadingPipe(const uint8_t child, const uint64_t address) { const uint8_t child_pipe[] = { RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 }; @@ -512,15 +517,15 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) // openWritingPipe() will overwrite the pipe 0 address, so // startListening() will have to restore it. if (child == 0) - pipe0_reading_address = value; + pipe0_reading_address = address; if (child < 6) { // For pipes 2-5, only write the LSB if ( child < 2 ) - write_register(child_pipe[child], reinterpret_cast(&value), 5); + write_register(child_pipe[child], reinterpret_cast(&address), 5); else - write_register(child_pipe[child], reinterpret_cast(&value), 1); + write_register(child_pipe[child], reinterpret_cast(&address), 1); write_register(child_payload_size[child],payload_size); @@ -535,7 +540,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) /******************************************************************/ -void RF24::toggle_features(void) +void RF24::toggle_features(void) const { csn(LOW); SPI.transfer( ACTIVATE ); @@ -545,7 +550,7 @@ void RF24::toggle_features(void) /******************************************************************/ -void RF24::enableAckPayload(void) +void RF24::enableAckPayload(void) const { // // enable ack payload and dynamic payload features @@ -572,7 +577,7 @@ void RF24::enableAckPayload(void) /******************************************************************/ -void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +void RF24::writeAckPayload(const uint8_t pipe, const void* buf, uint8_t len) const { const uint8_t* current = (const uint8_t*)buf; @@ -596,7 +601,7 @@ boolean RF24::isAckPayloadAvailable(void) /******************************************************************/ -void RF24::setAutoAck(bool enable) +void RF24::setAutoAck(const bool enable) const { if ( enable ) write_register(EN_AA, B111111); @@ -606,21 +611,30 @@ void RF24::setAutoAck(bool enable) /******************************************************************/ -boolean RF24::testCarrier(void) +void RF24::setAutoAck( const uint8_t pipe, const bool enable ) const +{ + uint8_t en_aa = read_register( EN_AA ) ; + en_aa &= ~((enable?0:1)< -64dBm, false if not */ - boolean testRPD(void); + boolean testRPD(void) const ; /** * Set Power Amplifier (PA) level to one of four levels. @@ -445,27 +459,27 @@ public: * * @param Desired PA level. */ - void setPALevel( rf24_pa_dbm_e level ) ; + void setPALevel( const rf24_pa_dbm_e level ) const ; /** * Fetches the current PA level. See setPALevel for * return value definitions. */ - rf24_pa_dbm_e getPALevel( void ) ; + rf24_pa_dbm_e getPALevel( void ) const ; /** * Set the transmission data rate * * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps */ - void setDataRate(rf24_datarate_e speed); + void setDataRate(const rf24_datarate_e speed); /** * Set the CRC length * * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit */ - void setCRCLength(rf24_crclength_e length); + void setCRCLength(const rf24_crclength_e length) const ; /**@}*/ }; From aac2c3dba33b163521dc6a00bdb7d81761af1fbc Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Tue, 21 Jun 2011 14:58:19 -0500 Subject: [PATCH 069/326] Added two default values to the RF24 constructor; speed and channel. Some additional constantness cleanup. --- RF24.cpp | 11 +++++------ RF24.h | 11 ++++++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 155908a..c23b18b 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -208,14 +208,17 @@ void RF24::print_observe_tx(uint8_t value) const /******************************************************************/ -RF24::RF24(uint8_t _cepin, uint8_t _cspin): +RF24::RF24(const uint8_t _cepin, const uint8_t _cspin, + const rf24_datarate_e speed, const uint8_t channel): wide_band(true), ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) { + setDataRate( speed ) ; + setChannel( channel ) ; } /******************************************************************/ -void RF24::setChannel(uint8_t channel) +void RF24::setChannel(const uint8_t channel) { if( wide_band ) { write_register(RF_CH,min(channel,127)); @@ -316,10 +319,6 @@ void RF24::begin(void) // Flush buffers flush_rx(); flush_tx(); - - // Set up default configuration. Callers can always change it later. - // Hardware default is channel 2 - even spacing implied. - setChannel(64); } /******************************************************************/ diff --git a/RF24.h b/RF24.h index ffe6554..956fa27 100644 --- a/RF24.h +++ b/RF24.h @@ -188,8 +188,17 @@ public: * * @param _cepin The pin attached to Chip Enable on the RF module * @param _cspin The pin attached to Chip Select + * @param speed The desired data rate of this network; default is 2Mbs + * @param channel The channel to use for this network; default is 64 + * + * @warning Addition features enabled/set in the begin method should be + * migrated into our constructor. Basically features which are network + * specific should be variable and assignable here; including timeouts, + * retries, and CRC length. */ - RF24(uint8_t _cepin, uint8_t _cspin); + RF24(const uint8_t _cepin, const uint8_t _cspin, + const rf24_datarate_e speed=RF24_2MBPS, + const uint8_t channel=64 ) ; /** * Begin operation of the chip From 634d0efde5930e79d1bc2a51972f83e942394d62 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Tue, 21 Jun 2011 15:01:10 -0500 Subject: [PATCH 070/326] Moved the wide_band variable and initializer cleanup. --- RF24.cpp | 2 +- RF24.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index c23b18b..fed43c3 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -210,7 +210,7 @@ void RF24::print_observe_tx(uint8_t value) const RF24::RF24(const uint8_t _cepin, const uint8_t _cspin, const rf24_datarate_e speed, const uint8_t channel): - wide_band(true), ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) + ce_pin(_cepin), csn_pin(_cspin), wide_band(true), payload_size(32), ack_payload_available(false) { setDataRate( speed ) ; setChannel( channel ) ; diff --git a/RF24.h b/RF24.h index 956fa27..84feb6d 100644 --- a/RF24.h +++ b/RF24.h @@ -22,9 +22,9 @@ typedef enum { RF24_CRC_8 = 0, RF24_CRC_16 } rf24_crclength_e; class RF24 { private: - boolean wide_band; /* 2Mbs data rate in use? */ uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ uint8_t csn_pin; /**< SPI Chip select */ + boolean wide_band; /* 2Mbs data rate in use? */ uint8_t payload_size; /**< Fixed size of payloads */ boolean ack_payload_available; /**< Whether there is an ack payload waiting */ uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ From 3cc856aa952840e67f1d09290bde0635d570195a Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 25 Jun 2011 21:14:51 -0700 Subject: [PATCH 071/326] Changed the logic. Button press now toggles the LED. --- examples/led_remote/led_remote.pde | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index e3cb84d..e2b6152 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -81,6 +81,7 @@ role_e role; // uint8_t button_states[num_button_pins]; +uint8_t led_states[num_led_pins]; // // Setup @@ -165,11 +166,12 @@ void setup(void) // Turn LED's ON until we start getting keys if ( role == role_led ) { - int i = num_button_pins; + int i = num_led_pins; while(i--) { pinMode(button_pins[i],OUTPUT); - digitalWrite(button_pins[i],HIGH); + led_states[i] = HIGH; + digitalWrite(led_pins[i],led_states[i]); } } @@ -236,10 +238,16 @@ void loop(void) // Spew it printf("Got buttons\n\r"); - // Set all the LED's according to the buttons + // For each button, if the button now on, then toggle the LED int i = num_led_pins; while(i--) - digitalWrite(led_pins[i],button_states[i]); + { + if ( button_states[i] ) + { + led_states[i] ^= HIGH; + digitalWrite(led_pins[i],led_states[i]); + } + } } } } From 748fde239b8b363bad0509bcf8a04ab2c7c53c81 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 25 Jun 2011 21:17:53 -0700 Subject: [PATCH 072/326] Add RF_SETUP to printDetails --- RF24.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index d3854fb..d4f11ea 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -282,7 +282,10 @@ void RF24::printDetails(void) printf_P(PSTR("EN_RXADDR = %02x\n\r"),*buffer); read_register(RF_CH,buffer,1); - printf_P(PSTR("RF_CH = %02x\n\r"),*buffer); + printf_P(PSTR("RF_CH = %02x\n\r"),*buffer); + + read_register(RF_SETUP,buffer,1); + printf_P(PSTR("RF_SETUP = %02x\n\r"),*buffer); } /******************************************************************/ From 6e6264be43e9e95c9b25838ea16377650e8fc215 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 25 Jun 2011 21:18:13 -0700 Subject: [PATCH 073/326] Fix bug in openReadingPipe which disallowed pipe 5 --- RF24.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index d4f11ea..1e4eabf 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -517,7 +517,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) if (child == 0) pipe0_reading_address = value; - if (child < 5) + if (child <= 5) { // For pipes 2-5, only write the LSB if ( child < 2 ) From 14dcc343dc457fa4b6752969f5b7505acab0b907 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 25 Jun 2011 21:22:20 -0700 Subject: [PATCH 074/326] Fixed unused parameter warning --- examples/led_remote/printf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/led_remote/printf.h b/examples/led_remote/printf.h index 63501e4..df6c46a 100644 --- a/examples/led_remote/printf.h +++ b/examples/led_remote/printf.h @@ -18,7 +18,7 @@ #include "WProgram.h" -int serial_putc( char c, FILE *t ) +int serial_putc( char c, FILE * ) { Serial.write( c ); From 8d5f020c78d61a118c3e00be2f1dea43a727d667 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 25 Jun 2011 21:23:25 -0700 Subject: [PATCH 075/326] Added Jamfile for led_remote --- .gitignore | 4 + examples/led_remote/Jamfile | 222 ++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 examples/led_remote/Jamfile diff --git a/.gitignore b/.gitignore index f49947e..5dae4ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ *.o .*.swp docs/ +output/ +ojam/ +version.h +Session.vim diff --git a/examples/led_remote/Jamfile b/examples/led_remote/Jamfile new file mode 100644 index 0000000..eb98917 --- /dev/null +++ b/examples/led_remote/Jamfile @@ -0,0 +1,222 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +#This is not working :( +#HDRSCAN on $(OUT).pde = "^#[ \\t]*include[ \\t]*[<\\"](.*)[\\">].*$" ; +#HDRRULE on $(OUT).pde = HDER ; +#rule HDER +#{ +# Echo $(<) INCLUDES $(>:B) ; +# +# _LIB = [ GLOB $(ARDUINO_LIB)/$(>:B) $(SKETCH_LIB)/$(>:B) : *.cpp ] ; +# Includes $(<) : $(OUT_DIR)/$(_LIB:B).o ; +# Echo [ GLOB $(ARDUINO_LIB)/$(>:B) $(SKETCH_LIB)/$(>:B) : *.cpp ] ; +#} + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +# Could not get headerscan to work :( +# HDRRULE on $(>) = HDER ; +# HDRSCAN on $(>) = $(HDRPATTERN) ; +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + From aa7a7c647ae5700d7a3e47f2fe713fd87e6f477b Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 25 Jun 2011 21:24:42 -0700 Subject: [PATCH 076/326] Cleaned up some reinterpret casts to use new-style casts --- RF24.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 1e4eabf..8874820 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -106,7 +106,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) { uint8_t status; - const uint8_t* current = (const uint8_t*)buf; + const uint8_t* current = reinterpret_cast(buf); csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); @@ -127,7 +127,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) uint8_t RF24::read_payload(void* buf, uint8_t len) { uint8_t status; - uint8_t* current = (uint8_t*)buf; + uint8_t* current = reinterpret_cast(buf); csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); @@ -577,7 +577,7 @@ void RF24::enableAckPayload(void) void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) { - const uint8_t* current = (const uint8_t*)buf; + const uint8_t* current = reinterpret_cast(buf); csn(LOW); SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); From 059efa5ffa03d544c80d0101c6c6de14a1e5f582 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Mon, 27 Jun 2011 15:02:33 -0500 Subject: [PATCH 077/326] Re-organized nRF24L01.h file to place 'P' features in one location. Changed some bit value interpretation. Now auto-detects P-vaiants and is queryable. setDataRate now returns a boolean indicating success or failure of the new data rate request. The wide_band state indicator has been added and now tracks the state, including on error fallback. Its now possible to query the current data rate via getDataRate(). The AA retry duration has been changed to 1500us, which is the lowest interval allowed when running at 250Kbs, with AA enabled, and 32-byte packets. --- RF24.cpp | 110 +++++++++++++++++++++++++++++++++++++++++------------ RF24.h | 30 +++++++++++++-- nRF24L01.h | 23 ++++++----- 3 files changed, 125 insertions(+), 38 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index fed43c3..9c897ff 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -8,8 +8,8 @@ #include #include -#include "RF24.h" #include "nRF24L01.h" +#include "RF24.h" #undef SERIAL_DEBUG #ifdef SERIAL_DEBUG @@ -29,7 +29,7 @@ void RF24::csn(const int mode) const { SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV8); + SPI.setClockDivider(SPI_CLOCK_DIV2); digitalWrite(csn_pin,mode); } @@ -210,8 +210,10 @@ void RF24::print_observe_tx(uint8_t value) const RF24::RF24(const uint8_t _cepin, const uint8_t _cspin, const rf24_datarate_e speed, const uint8_t channel): - ce_pin(_cepin), csn_pin(_cspin), wide_band(true), payload_size(32), ack_payload_available(false) + ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), + payload_size(32), ack_payload_available(false) { + begin() ; setDataRate( speed ) ; setChannel( channel ) ; } @@ -283,13 +285,17 @@ void RF24::printDetails(void) const printf_P(PSTR("RX_PW_P1 = 0x%02x\n\r"),*buffer); read_register(EN_AA,buffer,1); - printf_P(PSTR("EN_AA = %02x\n\r"),*buffer); + printf_P(PSTR("EN_AA = 0x%02x\n\r"),*buffer); read_register(EN_RXADDR,buffer,1); - printf_P(PSTR("EN_RXADDR = %02x\n\r"),*buffer); + printf_P(PSTR("EN_RXADDR = 0x%02x\n\r"),*buffer); read_register(RF_CH,buffer,1); - printf_P(PSTR("RF_CH = %02x\n\r"),*buffer); + printf_P(PSTR("RF_CH = 0x%02x\n\r"),*buffer); + + read_register(RF_SETUP,buffer,1); + printf_P(PSTR("RF_SETUP = 0x%02x (data rate: %d)\n\r"),*buffer,getDataRate()); + printf_P(PSTR("Hardware; isPVariant: %d\n\r"),isPVariant()); } /******************************************************************/ @@ -307,8 +313,10 @@ void RF24::begin(void) SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV8); - // Set generous timeouts, to make testing a little easier - write_register(SETUP_RETR,(B1111 << ARD) | (B1111 << ARC)); + // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier + // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet + // sizes must never be used. See documentation for a more complete explanation. + write_register(SETUP_RETR,(B0100 << ARD) | (B1111 << ARC)); // Reset current status write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); @@ -318,7 +326,16 @@ void RF24::begin(void) // Flush buffers flush_rx(); - flush_tx(); + flush_tx(); + + // Determine if this is a p or non-p RF24 module and then + // reset our data rate back to default value. This works + // because a non-P variant won't allow the data rate to + // be set to 250KBS. + if( setDataRate( RF24_250KBPS ) ) { + p_variant = true ; + } + setDataRate( RF24_2MBPS ) ; } /******************************************************************/ @@ -372,7 +389,7 @@ boolean RF24::write( const void* buf, uint8_t len ) ce(HIGH); // IN the end, the send should be blocking. It comes back in 60ms worst case, or much faster - // if I tighted up the retry logic. (Default settings will be 750us. + // if I tighted up the retry logic. (Default settings will be 1500us. // Monitor the send uint8_t observe_tx; uint8_t status; @@ -600,6 +617,12 @@ boolean RF24::isAckPayloadAvailable(void) /******************************************************************/ +boolean RF24::isPVariant(void) const { + return p_variant ; +} + +/******************************************************************/ + void RF24::setAutoAck(const bool enable) const { if ( enable ) @@ -613,7 +636,7 @@ void RF24::setAutoAck(const bool enable) const void RF24::setAutoAck( const uint8_t pipe, const bool enable ) const { uint8_t en_aa = read_register( EN_AA ) ; - en_aa &= ~((enable?0:1)< Date: Mon, 27 Jun 2011 12:54:42 -0700 Subject: [PATCH 078/326] Refactor repeated parts of printDetails into own methods --- README.md | 2 ++ RF24.cpp | 81 ++++++++++++++++++++++++------------------------------- RF24.h | 16 +++++++++++ 3 files changed, 53 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index c0e71c0..b473b22 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Arduino driver for nRF24L01 2.4GHz Wireless Transceiver +CLEANUPS BRANCH: Bits and pieces of cleanup that I haven't tested on the radios yet. + Design Goals: This library is designed to be... * Maximally compliant with the intended operation of the chip diff --git a/RF24.cpp b/RF24.cpp index 8874820..e8b7bdf 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -208,6 +208,27 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ +void RF24::print_byte_register(prog_char* name, uint8_t reg) +{ + printf_P(PSTR("%S\t = 0x%02x\n\r"),read_register(reg)); +} + +/******************************************************************/ + +void RF24::print_address_register(prog_char* name, uint8_t reg) +{ + uint8_t buffer[5]; + read_register(reg,buffer,sizeof buffer); + printf_P(PSTR("%S\t = 0x"),name); + + uint8_t* bufptr = buffer + sizeof buffer; + while( bufptr-- > buffer ) + printf_P(PSTR("%02x"),*bufptr); + printf_P(PSTR("\n\r")); +} + +/******************************************************************/ + RF24::RF24(uint8_t _cepin, uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) { @@ -238,54 +259,22 @@ uint8_t RF24::getPayloadSize(void) void RF24::printDetails(void) { - uint8_t buffer[5]; - uint8_t status = read_register(RX_ADDR_P0,buffer,5); - print_status(status); - printf_P(PSTR("RX_ADDR_P0 = 0x")); - uint8_t *bufptr = buffer + 5; - while( bufptr-- > buffer ) - printf_P(PSTR("%02x"),*bufptr); - printf_P(PSTR("\n\r")); + print_status(get_status()); - status = read_register(RX_ADDR_P1,buffer,5); - printf_P(PSTR("RX_ADDR_P1 = 0x")); - bufptr = buffer + 5; - while( bufptr-- > buffer ) - printf_P(PSTR("%02x"),*bufptr); - printf_P(PSTR("\n\r")); + print_address_register(PSTR("RX_ADDR_P0"),RX_ADDR_P0); + print_address_register(PSTR("RX_ADDR_P1"),RX_ADDR_P1); + print_byte_register(PSTR("RX_ADDR_P2"),RX_ADDR_P2); + print_byte_register(PSTR("RX_ADDR_P3"),RX_ADDR_P3); + print_byte_register(PSTR("RX_ADDR_P4"),RX_ADDR_P4); + print_byte_register(PSTR("RX_ADDR_P5"),RX_ADDR_P5); + print_address_register(PSTR("TX_ADDR"),TX_ADDR); - status = read_register(RX_ADDR_P2,buffer,1); - printf_P(PSTR("RX_ADDR_P2 = 0x%02x"),*buffer); - printf_P(PSTR("\n\r")); - - status = read_register(RX_ADDR_P3,buffer,1); - printf_P(PSTR("RX_ADDR_P3 = 0x%02x"),*buffer); - printf_P(PSTR("\n\r")); - - status = read_register(TX_ADDR,buffer,5); - printf_P(PSTR("TX_ADDR = 0x")); - bufptr = buffer + 5; - while( bufptr-- > buffer ) - printf_P(PSTR("%02x"),*bufptr); - printf_P(PSTR("\n\r")); - - status = read_register(RX_PW_P0,buffer,1); - printf_P(PSTR("RX_PW_P0 = 0x%02x\n\r"),*buffer); - - status = read_register(RX_PW_P1,buffer,1); - printf_P(PSTR("RX_PW_P1 = 0x%02x\n\r"),*buffer); - - read_register(EN_AA,buffer,1); - printf_P(PSTR("EN_AA = %02x\n\r"),*buffer); - - read_register(EN_RXADDR,buffer,1); - printf_P(PSTR("EN_RXADDR = %02x\n\r"),*buffer); - - read_register(RF_CH,buffer,1); - printf_P(PSTR("RF_CH = %02x\n\r"),*buffer); - - read_register(RF_SETUP,buffer,1); - printf_P(PSTR("RF_SETUP = %02x\n\r"),*buffer); + print_byte_register(PSTR("RX_PW_P0"),RX_PW_P0); + print_byte_register(PSTR("RX_PW_P1"),RX_PW_P1); + print_byte_register(PSTR("EN_AA"),EN_AA); + print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); + print_byte_register(PSTR("RF_CH"),RF_CH); + print_byte_register(PSTR("RF_SETUP"),RF_SETUP); } /******************************************************************/ diff --git a/RF24.h b/RF24.h index 386b628..9b39768 100644 --- a/RF24.h +++ b/RF24.h @@ -161,6 +161,22 @@ protected: */ void print_observe_tx(uint8_t value) ; + /** + * Print the name and value of an 8-bit register to stdout + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + */ + void print_byte_register(prog_char* name, uint8_t reg); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + */ + void print_address_register(prog_char* name, uint8_t reg); + /** * Turn on or off the special features of the chip * From 91df6f6df5d6d9b2b181e486b1a6141ed48e5033 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 27 Jun 2011 21:01:58 -0700 Subject: [PATCH 079/326] Fixed bug of not including name. Also added extra tab on short strings. --- RF24.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index e8b7bdf..c08bbdd 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -210,7 +210,8 @@ void RF24::print_observe_tx(uint8_t value) void RF24::print_byte_register(prog_char* name, uint8_t reg) { - printf_P(PSTR("%S\t = 0x%02x\n\r"),read_register(reg)); + char extra_tab = strlen_P(name) < 8 ? '\t' : 0; + printf_P(PSTR("%S\t%c = 0x%02x\n\r"),name,extra_tab,read_register(reg)); } /******************************************************************/ @@ -219,7 +220,9 @@ void RF24::print_address_register(prog_char* name, uint8_t reg) { uint8_t buffer[5]; read_register(reg,buffer,sizeof buffer); - printf_P(PSTR("%S\t = 0x"),name); + + char extra_tab = strlen_P(name) < 8 ? '\t' : 0; + printf_P(PSTR("%S\t%c = 0x"),name,extra_tab); uint8_t* bufptr = buffer + sizeof buffer; while( bufptr-- > buffer ) From fea8c0e82f831d0442c38e84b4ae2fea1614deae Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 27 Jun 2011 21:06:08 -0700 Subject: [PATCH 080/326] Move examples to 57600 baud --- examples/led_remote/led_remote.pde | 2 +- examples/pingpair/pingpair.pde | 2 +- examples/pingpair_pl/pingpair_pl.pde | 2 +- examples/pingpair_sleepy/pingpair_sleepy.pde | 2 +- examples/starping/starping.pde | 2 +- examples/starping_relay/starping_relay.pde | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index e2b6152..53fc68b 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -108,7 +108,7 @@ void setup(void) // Print preamble // - Serial.begin(9600); + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/led_remote/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index b0b3f0e..17c9343 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -79,7 +79,7 @@ void setup(void) // Print preamble // - Serial.begin(9600); + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/pingpair/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index 952648b..b014e9b 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -79,7 +79,7 @@ void setup(void) // Print preamble // - Serial.begin(9600); + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/pingpair_pl/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index 08e5a99..c8edc6b 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -102,7 +102,7 @@ void setup(void) // Print preamble // - Serial.begin(9600); + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index 2100c31..e887aeb 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -128,7 +128,7 @@ void setup(void) // Print preamble // - Serial.begin(9600); + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/starping/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde index 304917b..a5c3ecb 100644 --- a/examples/starping_relay/starping_relay.pde +++ b/examples/starping_relay/starping_relay.pde @@ -228,7 +228,7 @@ void setup(void) // Print preamble // - Serial.begin(9600); + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/starping_relay/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); From ce881ba0dcd5f06fc302c55cd02e4bdc52b87051 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 27 Jun 2011 21:16:26 -0700 Subject: [PATCH 081/326] Removed unused file --- Jamfile | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 Jamfile diff --git a/Jamfile b/Jamfile deleted file mode 100644 index c17bfbd..0000000 --- a/Jamfile +++ /dev/null @@ -1,42 +0,0 @@ -# -# Jamfile for generating the documentation -# - -DOXYGEN = doxygen ; -XSLT = xsltproc ; - -DOCSOURCES = Doxyfile RF24.h ; - -rule Doxygen -{ - Depends $(<) : $(>) ; - Clean clean : $(<) ; - Depends docs : $(<) ; -} - -actions Doxygen -{ - $(DOXYGEN) -} - -rule Xslt -{ - Depends $(<) : $(>) ; - Clean clean : $(<) ; -} - -actions Xslt -{ - $(XSLT) $(STYLESHEET) $(>) > $(<) -} - -Doxygen docs/xml/index.xml : $(DOCSOURCES) ; - -Xslt docs/xml/combined.xml : docs/xml/index.xml ; -STYLESHEET on docs/xml/combined.xml = docs/xml/combine.xslt ; -Depends combined : docs/xml/combined.xml ; - -Xslt docs/xml/wikidocs.txt : docs/xml/combined.xml ; -STYLESHEET on docs/xml/wikidocs.txt = wikidoc.xslt ; -Depends docs/xml/combined.xml : wikidoc.xslt ; -Depends wiki : docs/xml/wikidocs.txt ; From e7c30f265fca9933710c99ef636a1faa948d6ba5 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 27 Jun 2011 21:22:39 -0700 Subject: [PATCH 082/326] Moved completely over to jam. bye bye make\! --- examples/led_remote/Jamfile | 16 -- examples/led_remote/makefile | 305 ------------------------------ examples/pingpair/Jamfile | 206 ++++++++++++++++++++ examples/pingpair/makefile | 305 ------------------------------ examples/pingpair/printf.h | 2 +- examples/pingpair_pl/Jamfile | 206 ++++++++++++++++++++ examples/pingpair_pl/makefile | 290 ---------------------------- examples/pingpair_pl/printf.h | 2 +- examples/pingpair_sleepy/Jamfile | 206 ++++++++++++++++++++ examples/pingpair_sleepy/makefile | 290 ---------------------------- examples/pingpair_sleepy/printf.h | 2 +- examples/scanner/Jamfile | 206 ++++++++++++++++++++ examples/scanner/makefile | 305 ------------------------------ examples/scanner/printf.h | 2 +- examples/starping/Jamfile | 206 ++++++++++++++++++++ examples/starping/makefile | 295 ----------------------------- examples/starping/printf.h | 2 +- examples/starping/starping.pde | 2 +- 18 files changed, 1036 insertions(+), 1812 deletions(-) delete mode 100644 examples/led_remote/makefile create mode 100644 examples/pingpair/Jamfile delete mode 100644 examples/pingpair/makefile create mode 100644 examples/pingpair_pl/Jamfile delete mode 100644 examples/pingpair_pl/makefile create mode 100644 examples/pingpair_sleepy/Jamfile delete mode 100644 examples/pingpair_sleepy/makefile create mode 100644 examples/scanner/Jamfile delete mode 100644 examples/scanner/makefile create mode 100644 examples/starping/Jamfile delete mode 100644 examples/starping/makefile diff --git a/examples/led_remote/Jamfile b/examples/led_remote/Jamfile index eb98917..901f8da 100644 --- a/examples/led_remote/Jamfile +++ b/examples/led_remote/Jamfile @@ -62,21 +62,8 @@ PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; OUT = $(OUT_DIR)/$(PROJECT_NAME) ; # AvrDude setup - AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; -#This is not working :( -#HDRSCAN on $(OUT).pde = "^#[ \\t]*include[ \\t]*[<\\"](.*)[\\">].*$" ; -#HDRRULE on $(OUT).pde = HDER ; -#rule HDER -#{ -# Echo $(<) INCLUDES $(>:B) ; -# -# _LIB = [ GLOB $(ARDUINO_LIB)/$(>:B) $(SKETCH_LIB)/$(>:B) : *.cpp ] ; -# Includes $(<) : $(OUT_DIR)/$(_LIB:B).o ; -# Echo [ GLOB $(ARDUINO_LIB)/$(>:B) $(SKETCH_LIB)/$(>:B) : *.cpp ] ; -#} - rule GitVersion { Always $(<) ; @@ -128,9 +115,6 @@ rule Pde Depends $(<) : $(<:D) ; Clean clean : $(<) ; -# Could not get headerscan to work :( -# HDRRULE on $(>) = HDER ; -# HDRSCAN on $(>) = $(HDRPATTERN) ; } actions Pde diff --git a/examples/led_remote/makefile b/examples/led_remote/makefile deleted file mode 100644 index 493a159..0000000 --- a/examples/led_remote/makefile +++ /dev/null @@ -1,305 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -PORT3 = /dev/tty.usbserial-A40081RP -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -# Temporary testing of github Arduino environment -OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc -ARDUINO_DIR = /opt/Arduino - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I$(MY_LIB)/Bounce -I$(MY_LIB)/ButtonSpace -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: upload1 upload2 - -upload1: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - -upload2: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -upload3: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair/Jamfile b/examples/pingpair/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/examples/pingpair/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/pingpair/makefile b/examples/pingpair/makefile deleted file mode 100644 index 15cb43b..0000000 --- a/examples/pingpair/makefile +++ /dev/null @@ -1,305 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -PORT3 = /dev/tty.usbserial-A40081RP -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -# Temporary testing of github Arduino environment -OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc -ARDUINO_DIR = /opt/Arduino - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: upload1 upload2 - -upload1: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - -upload2: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -upload3: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair/printf.h b/examples/pingpair/printf.h index 63501e4..df6c46a 100644 --- a/examples/pingpair/printf.h +++ b/examples/pingpair/printf.h @@ -18,7 +18,7 @@ #include "WProgram.h" -int serial_putc( char c, FILE *t ) +int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair_pl/Jamfile b/examples/pingpair_pl/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/examples/pingpair_pl/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/pingpair_pl/makefile b/examples/pingpair_pl/makefile deleted file mode 100644 index a1c6d4c..0000000 --- a/examples/pingpair_pl/makefile +++ /dev/null @@ -1,290 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair_pl/printf.h b/examples/pingpair_pl/printf.h index 63501e4..df6c46a 100644 --- a/examples/pingpair_pl/printf.h +++ b/examples/pingpair_pl/printf.h @@ -18,7 +18,7 @@ #include "WProgram.h" -int serial_putc( char c, FILE *t ) +int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair_sleepy/Jamfile b/examples/pingpair_sleepy/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/examples/pingpair_sleepy/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/pingpair_sleepy/makefile b/examples/pingpair_sleepy/makefile deleted file mode 100644 index a1c6d4c..0000000 --- a/examples/pingpair_sleepy/makefile +++ /dev/null @@ -1,290 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair_sleepy/printf.h b/examples/pingpair_sleepy/printf.h index 63501e4..df6c46a 100644 --- a/examples/pingpair_sleepy/printf.h +++ b/examples/pingpair_sleepy/printf.h @@ -18,7 +18,7 @@ #include "WProgram.h" -int serial_putc( char c, FILE *t ) +int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/scanner/Jamfile b/examples/scanner/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/examples/scanner/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/scanner/makefile b/examples/scanner/makefile deleted file mode 100644 index 15cb43b..0000000 --- a/examples/scanner/makefile +++ /dev/null @@ -1,305 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -PORT3 = /dev/tty.usbserial-A40081RP -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -# Temporary testing of github Arduino environment -OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc -ARDUINO_DIR = /opt/Arduino - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: upload1 upload2 - -upload1: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - -upload2: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -upload3: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/scanner/printf.h b/examples/scanner/printf.h index 63501e4..df6c46a 100644 --- a/examples/scanner/printf.h +++ b/examples/scanner/printf.h @@ -18,7 +18,7 @@ #include "WProgram.h" -int serial_putc( char c, FILE *t ) +int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/starping/Jamfile b/examples/starping/Jamfile new file mode 100644 index 0000000..de9b1f6 --- /dev/null +++ b/examples/starping/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = EEPROM SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/starping/makefile b/examples/starping/makefile deleted file mode 100644 index 2ea7538..0000000 --- a/examples/starping/makefile +++ /dev/null @@ -1,295 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: upload1 upload2 - -upload1: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - -upload2: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/starping/printf.h b/examples/starping/printf.h index 63501e4..df6c46a 100644 --- a/examples/starping/printf.h +++ b/examples/starping/printf.h @@ -18,7 +18,7 @@ #include "WProgram.h" -int serial_putc( char c, FILE *t ) +int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index e887aeb..1d02bcd 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -286,7 +286,7 @@ void loop(void) // And we are done right now (no easy way to soft reset) printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); - while(1); + while(1) ; } } } From 549d4054fba01f1b3d63ea1761aa136ad86b218b Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 27 Jun 2011 21:24:17 -0700 Subject: [PATCH 083/326] Deleted starping relay. Superseded by RF24Network. --- examples/starping_relay/.gitignore | 1 - examples/starping_relay/makefile | 305 ----------- examples/starping_relay/printf.h | 33 -- examples/starping_relay/starping_relay.pde | 562 --------------------- 4 files changed, 901 deletions(-) delete mode 100644 examples/starping_relay/.gitignore delete mode 100644 examples/starping_relay/makefile delete mode 100644 examples/starping_relay/printf.h delete mode 100644 examples/starping_relay/starping_relay.pde diff --git a/examples/starping_relay/.gitignore b/examples/starping_relay/.gitignore deleted file mode 100644 index ea1472e..0000000 --- a/examples/starping_relay/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ diff --git a/examples/starping_relay/makefile b/examples/starping_relay/makefile deleted file mode 100644 index 15cb43b..0000000 --- a/examples/starping_relay/makefile +++ /dev/null @@ -1,305 +0,0 @@ -# Arduino Makefile -# Arduino adaptation by mellis, eighthave, oli.keller -# Modified by Kerry Wong to support NetBeans -# Modified by Rob Gray (Graynomad) for use with Windows and no IDE -# This works in my environment and I use it to program two different -# 328-based boards and a Mega2560. It's not necessarily robust and -# I may have broken something in the original file that I don't use. -# -# This makefile allows you to build sketches from the command line -# without the Arduino environment. -# -# Instructions for using the makefile: -# -# 1. Copy this file into the folder with your sketch. The project code file -# should have a .c extension however the file gets copied to a .cpp before -# compilation so you still write in C++. -# -# 2. Modify the lines between the double ### rows to set the paths -# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 -# for the Arduino IDE, Note the use of short folder name, don't use -# "Program files" because spaces will break the build. -# -# Set the line containing "MCU" to match your board's processor. -# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, -# change F_CPU to 8000000. -# -# 3. At the command line, change to the directory containing your -# program's file and the makefile. -# -# 4. Type "make" and press enter to compile/verify your program. -# The default make target will also perform the uplode using avrdude. -# -# The first time this is done all required libraries will be built -# and a core.a file will be created in the output folder. -# -# NOTES: -# All output goes into a folder called "output" underneath the working folder. -# The default all: target creates symbol (.sym) and expanded assembly -# (.lss) files and uploads the program. -# -# -########################################################## -########################################################## -# Select processor here -MCU = atmega328p -#MCU = atmega2560 - -ifeq ($(MCU),atmega2560) -UPLOAD_RATE = 115200 -AVRDUDE_PROTOCOL = stk500v2 -COM = 39 -endif - -ifeq ($(MCU),atmega328p) -UPLOAD_RATE = 57600 -AVRDUDE_PROTOCOL = stk500v1 -COM = 33 -endif - -UNAME := $(shell uname) - -ifeq ($(UNAME),Darwin) -ARDUINO_VERSION = 21 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc -PORT = /dev/tty.usbserial-A600eHIs -PORT2 = /dev/tty.usbserial-A9007LmI -PORT3 = /dev/tty.usbserial-A40081RP -else -ARDUINO_VERSION = 22 -ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = /usr/bin -AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools -PORT = /dev/ttyUSB0 -PORT2 = /dev/ttyUSB1 -endif - -# Temporary testing of github Arduino environment -OLD_DIR = /opt/arduino-00$(ARDUINO_VERSION) -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc -ARDUINO_DIR = /opt/Arduino - -PROJECT_NAME = $(notdir $(PWD)) -PROJECT_DIR = . -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino -ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr -ARDUINO_LIB = $(ARDUINO_DIR)/libraries -F_CPU = 16000000 - -########################################################## -########################################################## - -# Note that if your program has dependencies other than those -# already listed below, you will need to add them accordingly. -C_MODULES = \ -$(ARDUINO_CORE)/wiring_pulse.c \ -$(ARDUINO_CORE)/wiring_analog.c \ -$(ARDUINO_CORE)/pins_arduino.c \ -$(ARDUINO_CORE)/wiring.c \ -$(ARDUINO_CORE)/wiring_digital.c \ -$(ARDUINO_CORE)/WInterrupts.c \ -$(ARDUINO_CORE)/wiring_shift.c \ - -CXX_MODULES = \ -$(ARDUINO_CORE)/Tone.cpp \ -$(ARDUINO_CORE)/main.cpp \ -$(ARDUINO_CORE)/WMath.cpp \ -$(ARDUINO_CORE)/Print.cpp \ -$(ARDUINO_CORE)/HardwareSerial.cpp \ -$(ARDUINO_LIB)/SPI/SPI.cpp \ -$(ARDUINO_LIB)/EEPROM/EEPROM.cpp \ -../../RF24.cpp - -CXX_APP = output/$(PROJECT_NAME).cpp -MODULES = $(C_MODULES) $(CXX_MODULES) -SRC = $(C_MODULES) -CXXSRC = $(CXX_MODULES) $(CXX_APP) -FORMAT = ihex - -# Name of this Makefile (used for "make depend"). -MAKEFILE = Makefile - -# Debugging format. -# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. -# AVR (extended) COFF requires stabs, plus an avr-objcopy run. -#DEBUG = stabs -DEBUG = - -OPT = s - -# Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) -CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) - -# Place -I options here -CINCS = -I$(ARDUINO_LIB)/EEPROM -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. - -CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) - -# Compiler flag to set the C Standard level. -# c89 - "ANSI" C -# gnu89 - c89 plus GCC extensions -# c99 - ISO C99 standard (not yet fully implemented) -# gnu99 - c99 plus GCC extensions -#CSTANDARD = -std=gnu99 -CDEBUG = -g$(DEBUG) -#CWARN = -Wall -Wstrict-prototypes -CWARN = -Wall # show all warnings -#CWARN = -w # suppress all warnings -CMAP = -Wl,-Map,output.map -####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -CTUNING = -ffunction-sections -fdata-sections -CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections -#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) -MMCU = -mmcu=$(MCU) - -CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) -CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) -#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs -LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map - -# Programming support using avrdude. Settings and variables. -AVRDUDE_PORT = $(PORT) -AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i - -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ --p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) - -# Program settings -CC = $(AVR_TOOLS_PATH)/avr-gcc -CXX = $(AVR_TOOLS_PATH)/avr-g++ -LD = $(AVR_TOOLS_PATH)/avr-gcc -OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy -OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump -AR = $(AVR_TOOLS_PATH)/avr-ar -SIZE = $(AVR_TOOLS_PATH)/avr-size -NM = $(AVR_TOOLS_PATH)/avr-nm -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude -REMOVE = rm -f -MV = mv -f - -# Define all object files. -OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) -OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) - -# Define all listing files. -LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) - -# Combine all necessary flags and optional flags. -# Add target processor to flags. -ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) -ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) -ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) -ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) - -# Default target. -# This is th etarget that gets executed with a make command -# that has no parameters, ie "make". -all: applet_files build sym lss size upload - -build: elf hex - -output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde - test -d output || mkdir output - echo "#include " > $@ - echo "#line 1 \"$<\"" >> $@ - cat $< >> $@ - -elf: output/$(PROJECT_NAME).elf -hex: output/$(PROJECT_NAME).hex -eep: output/$(PROJECT_NAME).eep -lss: output/$(PROJECT_NAME).lss -#sym: output/$(PROJECT_NAME).sym - -# Upload HEX file to Arduino -upload: upload1 upload2 - -upload1: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) - -upload2: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) - -upload3: output/$(PROJECT_NAME).hex - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT3) $(AVRDUDE_WRITE_FLASH) - -sym: - $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym - -# Display size of file. -size: - $(SIZE) output/$(PROJECT_NAME).elf - -# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. -COFFCONVERT=$(OBJCOPY) --debugging \ ---change-section-address .data-0x800000 \ ---change-section-address .bss-0x800000 \ ---change-section-address .noinit-0x800000 \ ---change-section-address .eeprom-0x810000 - -coff: output/$(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - - extcoff: $(PROJECT_NAME).elf - $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof - -.SUFFIXES: .elf .hex .eep .lss .sym - -.elf.hex: - $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ - -.elf.eep: - $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ - --no-change-warnings \ - --change-section-lma .eeprom=0 $< $@ - -# Create extended listing file from ELF output file. -.elf.lss: - $(OBJDUMP) -h -S $< > $@ - -# Link: create ELF output file from library. -#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a -output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a - $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a - -output/core.a: $(OBJ_MODULES) - @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done - -# Compile: create object files from C++ source files. -.cpp.o: - $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ - -# Compile: create object files from C source files. -.c.o: - $(CC) -c $(ALL_CFLAGS) $< -o $@ - -# Compile: create assembler files from C source files. -.c.s: - $(CC) -S $(ALL_CFLAGS) $< -o $@ - -# Assemble: create object files from assembler source files. -.S.o: - $(CC) -c $(ALL_ASFLAGS) $< -o $@ - -# Automatic dependencies -%.d: %.c - $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -%.d: %.cpp - $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ - -# Target: clean project. -clean: - $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ - output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ - $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) - -#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter -.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter - -#include $(SRC:.c=.d) -#include $(CXXSRC:.cpp=.d) diff --git a/examples/starping_relay/printf.h b/examples/starping_relay/printf.h deleted file mode 100644 index 63501e4..0000000 --- a/examples/starping_relay/printf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#include "WProgram.h" - -int serial_putc( char c, FILE *t ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde deleted file mode 100644 index a5c3ecb..0000000 --- a/examples/starping_relay/starping_relay.pde +++ /dev/null @@ -1,562 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example RF Radio Ping Star Group with Relay - * - * This sketch is a very complex example of using the RF24 library for Arduino. - * Deploy this on any number of nodes to create a basic mesh network. I have - * tested this on 6 nodes, but it should work on many more. Although if there - * are a lot more nodes, increase the ping_interval, or the base will be - * overwhelmed! - * - * There are three different roles a node can be: - * - * @li Leaf. Leaf nodes send a ping to the base unit, and wait for a pong in - * return - * - * @li Relay. Relay nodes do the same as a leaf node, AND they relay pings - * from leaf nodes toward the base, and relay pongs toward the leaves. - * - * @li Base. One node is the base station, which receives pings, and sends - * a pong back out. - * - * The address of each node is a number from 0 to n (the # of known nodes). - * It is set in EEPROM. To change a node's address, send the character code - * for that address. e.g. send the character '5' to set address 5. - * - * The role is determined from the topology table. Leafs have no children. - * The base node has no parent. Relays have parents and children. - */ - -#include -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// -// Topology -// - -struct node_info -{ - uint64_t talking_pipe; // Pipe used to talk to parent node - uint64_t listening_pipe; // Pipe used to listen to parent node - uint8_t parent_node; // Address of parent node -}; - -const node_info topology[] = -{ - { 0x0000000000LL, 0x0000000000LL,-1 }, // Base - { 0xF0F0F0F0E1LL, 0x3A3A3A3AE1LL, 0 }, // Relay - { 0xF0F0F0F0D2LL, 0x3A3A3A3AD2LL, 1 }, // Leaf - { 0xF0F0F0F0C3LL, 0x3A3A3A3AC3LL, 1 }, // Leaf - { 0xF0F0F0F0B4LL, 0x3A3A3A3AB4LL, 1 }, // Leaf - { 0xF0F0F0F0A5LL, 0x3A3A3A3AA5LL, 0 }, // Leaf, direct to Base -}; -const short num_nodes = sizeof(topology)/sizeof(node_info); - -/** - * Find where to send a message to reach the target node - * - * Given the @p target_node, find the child or parent of - * the @p current_node which will relay messages for the target. - * - * This is needed in a multi-hop system where the @p current_node - * is not adjacent to the @p target_node in the topology - */ -uint8_t find_node( uint8_t current_node, uint8_t target_node ) -{ - uint8_t out_node = target_node; - bool found_target = false; - while ( ! found_target ) - { - if ( topology[out_node].parent_node == current_node ) - { - found_target = true; - } - else - { - out_node = topology[out_node].parent_node; - - // If we've made it all the way back to the base without finding - // common lineage with the to_node, we will just send it to our parent - if ( out_node == 0 || out_node == -1 ) - { - out_node = topology[current_node].parent_node; - found_target = true; - } - } - } - return out_node; -} - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. Role is -// determined by the topology table. -// - -// The various roles supported by this sketch -typedef enum { role_invalid = 0, role_base, role_relay, role_leaf } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Base", "Relay", "Leaf" }; - -// The role of the current running sketch -role_e role; - -// -// Address management -// - -// Where in EEPROM is the address stored? -const uint8_t address_at_eeprom_location = 0; - -// What flag value is stored there so we know the value is valid? -const uint8_t valid_eeprom_flag = 0xdf; - -// What is our address (SRAM cache of the address from EEPROM) -// This is an index into the topology[] table above -uint8_t node_address = -1; - -// -// Payload -// - -struct payload_t -{ - uint8_t from_node; - uint8_t to_node; - uint16_t id; - unsigned long time; - - static uint16_t next_id; - - payload_t(void) {} - payload_t(uint8_t _from, uint8_t _to, const unsigned long& _time): from_node(_from), to_node(_to), id(next_id++), time(_time) {} -}; - -uint16_t payload_t::next_id; - -void payload_printf(const char* name, const payload_t& pl) -{ - printf("%s Payload from:%u to:%u id:%u time:%lu",name,pl.from_node,pl.to_node,pl.id,pl.time); -} - -// -// Setup/loop shared statics -// - -static unsigned long last_ping_sent_at; -static bool waiting_for_pong = false; -static short consecutive_timeouts; -const unsigned long ping_interval = 2000; // ms -const unsigned long pong_timeout = 250; // ms -const unsigned long ping_phase_shift = 100; // ms -const short timeout_shift_threshold = 3; - -// Space to track the last packet we received from each node, useful -// for tracking lost packets -static uint16_t last_id_received[num_nodes]; - -void setup(void) -{ - // - // Address - // - - // Look for the token in EEPROM to indicate the following value is - // a validly set node address - if ( EEPROM.read(address_at_eeprom_location) == valid_eeprom_flag ) - { - // Read the address from EEPROM - uint8_t reading = EEPROM.read(address_at_eeprom_location+1); - - // If it is in a valid range for node addresses, it is our - // address. - if ( reading <= 5 ) - node_address = reading; - } - - // - // Role - // - - // Role is determined by address. - if ( node_address != -1 ) - { - // Node #0 is the base, by definition - if ( node_address == 0 ) - role = role_base; - else - { - // Otherwise, it is probably a leaf node - role = role_leaf; - - // If there are any nodes in the topology table which consider this - // a parent, then we are a relay. - int i = num_nodes; - while (i--) - { - if ( topology[i].parent_node == node_address ) - { - role = role_relay; - break; - } - } - } - } - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/starping_relay/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("ADDRESS: %i\n\r",node_address); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // Each leaf node has a talking pipe that it will ping into, and a listening - // pipe that it will listen for the pong. Relay nodes also do this. - if ( role == role_leaf ) - { - // Write on our talking pipe - radio.openWritingPipe(topology[node_address].talking_pipe); - - // Listen on our listening pipe - radio.openReadingPipe(1,topology[node_address].listening_pipe); - } - - // Relay nodes have a special function. They open their listening pipe on pipe - // #0. This will get over-written every time we open a writing pipe. So - // Remember to re-open the reading pipe whenever we start to listen again. - if ( role == role_relay ) - { - // Write on our talking pipe - radio.openWritingPipe(topology[node_address].talking_pipe); - - // Listen on our listening pipe - radio.openReadingPipe(0,topology[node_address].listening_pipe); - } - - // The base and relay nodes listen on all their children node's talking pipes - // and sends the pong back on the child node's specific listening pipe. - if ( role == role_base || role == role_relay ) - { - // First child listening pipe is #1 - uint8_t current_pipe = 1; - - // The topology table tells us who our children are - int i = num_nodes; - while (i--) - { - if ( topology[i].parent_node == node_address ) - radio.openReadingPipe(current_pipe++,topology[i].talking_pipe); - } - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Prompt the user to assign a node address if we don't have one - // - - if ( role == role_invalid ) - { - printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 0 through 5 to assign an address\n\r"); - } -} - -void ping_if_ready(void); -void handle_pong(const payload_t& payload); -void check_pong_timeout(void); - -void loop(void) -{ - // - // Leaf role. Repeatedly send the current time - // - - if ( role == role_leaf ) - { - ping_if_ready(); - check_pong_timeout(); - - // Did we get a pong? - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - payload_t payload; - boolean done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &payload, sizeof(payload_t) ); - - handle_pong(payload); - } - } - } - - // - // Relay role. Forward packets to the appropriate destination - // - - if ( role == role_relay ) - { -#if 1 - // Relay role is ALSO a ping sender!! - ping_if_ready(); - check_pong_timeout(); -#endif - // if there is data ready - uint8_t pipe_num; - if ( radio.available(&pipe_num) ) - { - // Dump the payloads until we've gotten everything - payload_t payload; - boolean done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &payload, sizeof(payload_t) ); - - // Is this for us? - if ( payload.to_node == node_address ) - { - handle_pong(payload); - } - else - { - // Relay it - - // Spew it - printf("%lu ",millis()); - payload_printf("RELAY",payload); - printf(" on pipe %u. ",pipe_num); - - // Which pipe should we use to get the message to the "to_node"? - // We need to find a node who is OUR CHILD that either IS the to_node - // or has the to_node as one of ITS children. Failing that, we'll just - // send it back to the parent to deal with. - uint8_t out_node = find_node(node_address,payload.to_node); - - // First, stop listening so we can talk - radio.stopListening(); - - // If this node is our child, we talk on it's listening pipe. - uint64_t out_pipe; - if ( topology[out_node].parent_node == node_address ) - out_pipe = topology[out_node].listening_pipe; - - // Otherwise, it's our parent so we talk on OUR talking pipe - else - out_pipe = topology[node_address].talking_pipe; - - // Open the correct pipe for writing. - radio.openWritingPipe(out_pipe); - - // Send the payload back out - bool ok = radio.write( &payload, sizeof(payload_t) ); - - // Debug spew - uint16_t pipe_id = out_pipe & 0xffff; - printf("OUT on pipe %04x %s.\n\r",pipe_id,ok?"ok":"failed"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } - } - } - - // - // Base role. Receive each packet, dump it out, and send it back - // - - if ( role == role_base ) - { - // if there is data ready - uint8_t pipe_num; - if ( radio.available(&pipe_num) ) - { - // Dump the payloads until we've gotten everything - payload_t ping; - boolean done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &ping, sizeof(payload_t) ); - - // Spew it - printf("%lu ",millis()); - payload_printf("PING",ping); - printf(" on pipe %u. ",pipe_num); - - // Track the packets lost since we last heard from this node - // Packet loss is generally a sign of poor system health - uint16_t* last_id_ptr = &last_id_received[ping.from_node]; - if ( *last_id_ptr && ping.id > *last_id_ptr ) - { - uint16_t lost = ping.id - *last_id_ptr - 1; - if ( lost ) - printf(" lost %u",lost); - } - *last_id_ptr = ping.id; - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Construct the return payload (pong) - payload_t pong(node_address,ping.from_node,ping.time); - - // Find the correct pipe for writing. We can only talk on one of our - // direct children's listening pipes. If the to_node is further out, - // it will get relayed. - uint8_t out_node = find_node(node_address,pong.to_node); - - // Open the correct pipe for writing - radio.openWritingPipe(topology[out_node].listening_pipe); - - // Retain the low 2 bytes to identify the pipe for the spew - uint16_t pipe_id = topology[out_node].listening_pipe & 0xffff; - - // Send the final one back. - bool ok = radio.write( &pong, sizeof(payload_t) ); - payload_printf(" ...PONG",pong); - printf(" on pipe %04x %s.\n\r",pipe_id,ok?"ok":"failed"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } - - - // - // Listen for serial input, which is how we set the address - // - if (Serial.available()) - { - // If the character on serial input is in a valid range... - char c = Serial.read(); - if ( c >= '0' && c <= '5' ) - { - // It is our address - EEPROM.write(address_at_eeprom_location,valid_eeprom_flag); - EEPROM.write(address_at_eeprom_location+1,c-'0'); - - // And we are done right now (no easy way to soft reset) - printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); - while(1); - } - } -} - -void ping_if_ready(void) -{ - // Is it time to ping again? - unsigned long now = millis(); - if ( now - last_ping_sent_at >= ping_interval ) - { - last_ping_sent_at = now; - waiting_for_pong = true; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Write on our talking pipe. The relay has to do this every time, because - // we ALSO use pipe 0 as a listening pipe. - radio.openWritingPipe(topology[node_address].talking_pipe); - - // Take the time, and send it to the base. This will block until complete - payload_t ping(node_address,0,millis()); - - // Print details. - printf("%lu ",millis()); - payload_printf(">PING",ping); - bool ok = radio.write( &ping, sizeof(payload_t) ); - if (ok) - printf(" ok\n\r"); - else - printf(" failed\n\r"); - - // Now, continue listening - radio.startListening(); - } -} - -void handle_pong(const payload_t& payload) -{ - // Not waiting anymore, got one. - waiting_for_pong = false; - consecutive_timeouts = 0; - - // Print details. - printf("%lu ",millis()); - payload_printf(">PONG",payload); - printf(" Round-trip delay: %lu\n\r",millis()-payload.time); -} - -void check_pong_timeout(void) -{ - // Have we timed out waiting for our pong? - if ( waiting_for_pong && ( millis() - last_ping_sent_at > pong_timeout ) ) - { - // Not waiting anymore, timed out. - waiting_for_pong = false; - - // Timeouts usually happen because of collisions with other nodes - // getting a pong just as we are trying to send a ping. The best thing - // to do right now is offset our ping timing to search for a slot - // that's not occupied. - // - // Only do this after getting a few timeouts, so we aren't always skittishly - // moving around the cycle. - if ( ++consecutive_timeouts > timeout_shift_threshold ) - last_ping_sent_at += ping_phase_shift; - - // Print details - printf("%lu ",millis()); - printf("TIMED OUT.\n\r"); - } -} -// vim:ai:cin:sts=2 sw=2 ft=cpp From 86798b65e4407764f2d69eac7168ca5331d9c7af Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 27 Jun 2011 21:25:32 -0700 Subject: [PATCH 084/326] Purge useless gitignores --- examples/led_remote/.gitignore | 1 - examples/pingpair/.gitignore | 1 - examples/pingpair_pl/.gitignore | 1 - examples/pingpair_sleepy/.gitignore | 1 - examples/starping/.gitignore | 1 - 5 files changed, 5 deletions(-) delete mode 100644 examples/led_remote/.gitignore delete mode 100644 examples/pingpair/.gitignore delete mode 100644 examples/pingpair_pl/.gitignore delete mode 100644 examples/pingpair_sleepy/.gitignore delete mode 100644 examples/starping/.gitignore diff --git a/examples/led_remote/.gitignore b/examples/led_remote/.gitignore deleted file mode 100644 index ea1472e..0000000 --- a/examples/led_remote/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ diff --git a/examples/pingpair/.gitignore b/examples/pingpair/.gitignore deleted file mode 100644 index ea1472e..0000000 --- a/examples/pingpair/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ diff --git a/examples/pingpair_pl/.gitignore b/examples/pingpair_pl/.gitignore deleted file mode 100644 index ea1472e..0000000 --- a/examples/pingpair_pl/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ diff --git a/examples/pingpair_sleepy/.gitignore b/examples/pingpair_sleepy/.gitignore deleted file mode 100644 index ea1472e..0000000 --- a/examples/pingpair_sleepy/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ diff --git a/examples/starping/.gitignore b/examples/starping/.gitignore deleted file mode 100644 index ea1472e..0000000 --- a/examples/starping/.gitignore +++ /dev/null @@ -1 +0,0 @@ -output/ From 81f85bd5215704269e11a31619d41ba20c58420c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 28 Jun 2011 06:27:36 -0700 Subject: [PATCH 085/326] Prettied up printDetails some more --- RF24.cpp | 39 +++++++++++++++++++++------------------ RF24.h | 26 ++++++++++++++++++-------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index c08bbdd..f34e760 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -185,7 +185,7 @@ uint8_t RF24::get_status(void) void RF24::print_status(uint8_t status) { - printf_P(PSTR("STATUS=%02x: RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), + printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), status, (status & _BV(RX_DR))?1:0, (status & _BV(TX_DS))?1:0, @@ -208,25 +208,33 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ -void RF24::print_byte_register(prog_char* name, uint8_t reg) +void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR("%S\t%c = 0x%02x\n\r"),name,extra_tab,read_register(reg)); + printf_P(PSTR("%S\t%c ="),name,extra_tab); + while (qty--) + printf_P(PSTR(" 0x%02x"),read_register(reg++)); + printf_P(PSTR("\n\r")); } /******************************************************************/ -void RF24::print_address_register(prog_char* name, uint8_t reg) +void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) { - uint8_t buffer[5]; - read_register(reg,buffer,sizeof buffer); - char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR("%S\t%c = 0x"),name,extra_tab); + printf_P(PSTR("%S\t%c ="),name,extra_tab); - uint8_t* bufptr = buffer + sizeof buffer; - while( bufptr-- > buffer ) + while (qty--) + { + uint8_t buffer[5]; + read_register(reg++,buffer,sizeof buffer); + + printf_P(PSTR(" 0x")); + uint8_t* bufptr = buffer + sizeof buffer; + while( --bufptr >= buffer ) printf_P(PSTR("%02x"),*bufptr); + } + printf_P(PSTR("\n\r")); } @@ -264,16 +272,11 @@ void RF24::printDetails(void) { print_status(get_status()); - print_address_register(PSTR("RX_ADDR_P0"),RX_ADDR_P0); - print_address_register(PSTR("RX_ADDR_P1"),RX_ADDR_P1); - print_byte_register(PSTR("RX_ADDR_P2"),RX_ADDR_P2); - print_byte_register(PSTR("RX_ADDR_P3"),RX_ADDR_P3); - print_byte_register(PSTR("RX_ADDR_P4"),RX_ADDR_P4); - print_byte_register(PSTR("RX_ADDR_P5"),RX_ADDR_P5); + print_address_register(PSTR("RX_ADDR_P0-1"),RX_ADDR_P0,2); + print_byte_register(PSTR("RX_ADDR_P2-5"),RX_ADDR_P2,4); print_address_register(PSTR("TX_ADDR"),TX_ADDR); - print_byte_register(PSTR("RX_PW_P0"),RX_PW_P0); - print_byte_register(PSTR("RX_PW_P1"),RX_PW_P1); + print_byte_register(PSTR("RX_PW_P0-6"),RX_PW_P0,6); print_byte_register(PSTR("EN_AA"),EN_AA); print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); print_byte_register(PSTR("RF_CH"),RF_CH); diff --git a/RF24.h b/RF24.h index 9b39768..468aa9f 100644 --- a/RF24.h +++ b/RF24.h @@ -164,18 +164,28 @@ protected: /** * Print the name and value of an 8-bit register to stdout * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - */ - void print_byte_register(prog_char* name, uint8_t reg); - - /** - * Print the name and value of a 40-bit address register to stdout + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. * * @param name Name of the register * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print */ - void print_address_register(prog_char* name, uint8_t reg); + void print_byte_register(prog_char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(prog_char* name, uint8_t reg, uint8_t qty = 1); /** * Turn on or off the special features of the chip From 04e72775bb4d92e39ea51bc63b0018dd6eaff5bb Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 28 Jun 2011 06:30:03 -0700 Subject: [PATCH 086/326] Add dynpd/feature to printDetails --- RF24.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/RF24.cpp b/RF24.cpp index f34e760..00469fc 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -281,6 +281,7 @@ void RF24::printDetails(void) print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); print_byte_register(PSTR("RF_CH"),RF_CH); print_byte_register(PSTR("RF_SETUP"),RF_SETUP); + print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); } /******************************************************************/ From 1c6cc96fb74a406786bab4f9841d82f65a65c861 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 28 Jun 2011 06:31:16 -0700 Subject: [PATCH 087/326] docupdate --- RF24.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/RF24.h b/RF24.h index 468aa9f..2a196e8 100644 --- a/RF24.h +++ b/RF24.h @@ -518,16 +518,6 @@ public: * ping/pong cycle. */ -/** - * @example starping_relay.pde - * - * This sketch is a very complex example of using the RF24 library for Arduino. - * Deploy this on any number of nodes to create a basic mesh network. I have - * tested this on 6 nodes, but it should work on many more. 'Leaf' nodes attempt - * to send a ping every 2 seconds to the 'Base' node. Optional 'Relay' nodes - * will relay these transmissions. - */ - /** * @example scanner.pde * From a22906f9617215f0448daa3e9c6c871e0ab0c9b1 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 28 Jun 2011 06:44:41 -0700 Subject: [PATCH 088/326] Added setRetries() --- RF24.cpp | 7 +++++++ RF24.h | 9 +++++++++ examples/pingpair/pingpair.pde | 8 +++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 00469fc..62c6e93 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -631,5 +631,12 @@ void RF24::setCRCLength(rf24_crclength_e length) write_register(CONFIG,config); } +/******************************************************************/ + +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR,(delay&0xf)< Date: Tue, 28 Jun 2011 06:46:45 -0700 Subject: [PATCH 089/326] Fix bug in setDataRate --- RF24.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 62c6e93..efc773a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -614,7 +614,7 @@ boolean RF24::testCarrier(void) void RF24::setDataRate(rf24_datarate_e speed) { - uint8_t setup = read_register(RF_SETUP) & RF_DR; + uint8_t setup = read_register(RF_SETUP) & _BV(RF_DR); if (speed == RF24_2MBPS) setup |= _BV(RF_DR); write_register(RF_SETUP,setup); From c151ffbf67c5b64baef1852ea9e7ec83ba7dc70f Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 28 Jun 2011 06:56:24 -0700 Subject: [PATCH 090/326] Tidied up EN_RXADDR setting --- RF24.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index efc773a..86c8880 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -523,12 +523,10 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) write_register(child_payload_size[child],payload_size); - // Note this is kind of an inefficient way to set up these enable bits, bit I thought it made - // the calling code more simple - uint8_t en_rx; - read_register(EN_RXADDR,&en_rx,1); - en_rx |= _BV(child_pipe_enable[child]); - write_register(EN_RXADDR,en_rx); + // Note it would be more efficient to set all of the bits for all open + // pipes at once. However, I thought it would make the calling code + // more simple to do it this way. + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(child_pipe_enable[child])); } } From ca4820cd27fb5466b73c96dfaf23d6c6d0899a65 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 28 Jun 2011 21:21:45 -0700 Subject: [PATCH 091/326] Documented some questionable behaviour as pointed out by mdenzien --- README.md | 2 -- RF24.h | 10 ++++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b473b22..c0e71c0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Arduino driver for nRF24L01 2.4GHz Wireless Transceiver -CLEANUPS BRANCH: Bits and pieces of cleanup that I haven't tested on the radios yet. - Design Goals: This library is designed to be... * Maximally compliant with the intended operation of the chip diff --git a/RF24.h b/RF24.h index a12919f..d9f844a 100644 --- a/RF24.h +++ b/RF24.h @@ -252,6 +252,9 @@ public: * getPayloadSize(). However, you can write less, and the remainder * will just be filled with zeroes. * + * @todo Write a non-blocking write to support users who want to + * check on progress separately or use an interrupt. + * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not @@ -304,19 +307,22 @@ public: /** * Open a pipe for reading * - * Up to 5 pipes can be open for reading at once. Open all the + * Up to 6 pipes can be open for reading at once. Open all the * reading pipes, and then call startListening(). * * @see openWritingPipe * * @warning Pipes 1-5 should share the first 32 bits. * Only the least significant byte should be unique, e.g. - * * @code * openReadingPipe(1,0xF0F0F0F0AA); * openReadingPipe(2,0xF0F0F0F066); * @endcode * + * @warning Pipe 0 is also used by the writing pipe. So if you open + * pipe 0 for reading, and then startListening(), it will overwrite the + * writing pipe. Ergo, do an openWritingPipe() again before write(). + * * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits * * @param number Which pipe# to open, 0-5. From c5823c2605e80ffd73e31d6a5b6a76249be13228 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 29 Jun 2011 21:38:17 -0700 Subject: [PATCH 092/326] Made timeouts more explicit in write() --- RF24.cpp | 36 +++++++++++++++++++--------------- examples/pingpair/pingpair.pde | 3 +++ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 86c8880..9dc4266 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -13,7 +13,7 @@ #undef SERIAL_DEBUG #ifdef SERIAL_DEBUG -#define IF_SERIAL_DEBUG(x) (x) +#define IF_SERIAL_DEBUG(x) ({x;}) #else #define IF_SERIAL_DEBUG(x) #endif @@ -365,31 +365,34 @@ boolean RF24::write( const void* buf, uint8_t len ) // Allons! ce(HIGH); + delayMicroseconds(15); + ce(LOW); + + // ------------ + // At this point we could return from a non-blocking write, and then call + // the rest after an interrupt + + // Instead, we are going to block here until we get TX_DS (transmission completed and ack'd) + // or MAX_RT (maximum retries, transmission failed). Also, we'll timeout in case the radio + // is flaky and we get neither. - // IN the end, the send should be blocking. It comes back in 60ms worst case, or much faster - // if I tighted up the retry logic. (Default settings will be 750us. - // Monitor the send uint8_t observe_tx; uint8_t status; - uint8_t retries = 255; + uint32_t sent_at = millis(); + const uint32_t timeout = 100; //ms to wait for timeout do { status = read_register(OBSERVE_TX,&observe_tx,1); - IF_SERIAL_DEBUG(Serial.print(status,HEX)); IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); - if ( ! retries-- ) - { - IF_SERIAL_DEBUG(printf("ABORTED: too many retries\n\r")); - break; - } } - while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) ); + while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( millis() - sent_at < timeout ) ); + // What was the result of the send? if ( status & _BV(TX_DS) ) result = true; - IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); - + IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed"); if ( status & _BV(MAX_RT) ) Serial.print(" too many retries")); + ack_payload_available = ( status & _BV(RX_DR) ); if ( ack_payload_available ) { @@ -400,7 +403,6 @@ boolean RF24::write( const void* buf, uint8_t len ) } // Yay, we are done. - ce(LOW); // Power down write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); @@ -440,7 +442,9 @@ boolean RF24::available(void) boolean RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); - IF_SERIAL_DEBUG(print_status(status)); + + // Too noisy, enable if you really want lots o data!! IF_SERIAL_DEBUG(print_status(status)); + boolean result = ( status & _BV(RX_DR) ); if (result) diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 9c449cf..1f0429f 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -96,6 +96,9 @@ void setup(void) // optionally, use a high channel to avoid WiFi chatter radio.setChannel(110); + // optionally, increase the CRC length for improved reliability + radio.setCRCLength(RF24_CRC_16); + // // Open pipes to other nodes for communication // From c62224ff8636f259dcbab79d544f3ea8b444ae0c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 29 Jun 2011 21:46:46 -0700 Subject: [PATCH 093/326] More comments, factored out powerdown from write() --- RF24.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 9dc4266..6d5e6c3 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -387,12 +387,28 @@ boolean RF24::write( const void* buf, uint8_t len ) } while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( millis() - sent_at < timeout ) ); + // The part above is what you could recreate with your own interrupt handler, + // and then call this when you got an interrupt + // ------------ + + // Read the status + status = get_status(); + + // Reset the status + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // The status tells us three things + // * The send was successful (TX_DS) + // * The send failed, too many retries (MAX_RT) + // * There is an ack packet waiting (RX_DR) + // What was the result of the send? if ( status & _BV(TX_DS) ) result = true; IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed"); if ( status & _BV(MAX_RT) ) Serial.print(" too many retries")); + // Handle the ack packet ack_payload_available = ( status & _BV(RX_DR) ); if ( ack_payload_available ) { @@ -405,12 +421,9 @@ boolean RF24::write( const void* buf, uint8_t len ) // Yay, we are done. // Power down - write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); + powerDown(); - // Reset current status - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Flush buffers + // Flush buffers (Is this a relic of past experimentation, and not needed anymore??) flush_tx(); return result; From fbae441249f11a3f15b856881c71899182b42ca3 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 6 Jul 2011 19:51:30 -0700 Subject: [PATCH 094/326] Factored out status-checking from write() into a public method --- RF24.cpp | 34 +++++++++++++++++++++------------- RF24.h | 12 ++++++++++++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 6d5e6c3..cb3d640 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -391,28 +391,20 @@ boolean RF24::write( const void* buf, uint8_t len ) // and then call this when you got an interrupt // ------------ - // Read the status - status = get_status(); - - // Reset the status - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - + // Call this when you get an interrupt // The status tells us three things // * The send was successful (TX_DS) // * The send failed, too many retries (MAX_RT) // * There is an ack packet waiting (RX_DR) + bool tx_ok, tx_fail, ack_payload_available; + whatHappened(tx_ok,tx_fail,ack_payload_available); - // What was the result of the send? - if ( status & _BV(TX_DS) ) - result = true; - - IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed"); if ( status & _BV(MAX_RT) ) Serial.print(" too many retries")); + result = tx_ok; + IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); // Handle the ack packet - ack_payload_available = ( status & _BV(RX_DR) ); if ( ack_payload_available ) { - write_register(STATUS,_BV(RX_DR) ); ack_payload_length = read_payload_length(); IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); @@ -503,6 +495,22 @@ boolean RF24::read( void* buf, uint8_t len ) /******************************************************************/ +void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) +{ + // Read the status + uint8_t status = get_status(); + + // Reset the status + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Report to the user what happened + tx_ok = status & _BV(TX_DS); + tx_fail = status & _BV(MAX_RT); + rx_ready = status & _BV(RX_DR); +} + +/******************************************************************/ + void RF24::openWritingPipe(uint64_t value) { // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01 diff --git a/RF24.h b/RF24.h index d9f844a..eafadaf 100644 --- a/RF24.h +++ b/RF24.h @@ -444,6 +444,18 @@ public: */ boolean isAckPayloadAvailable(void); + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx); + /** * Enable or disable auto-acknowlede packets * From bb37e88094c6ed4193b1de45d56d6fece84518d3 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 6 Jul 2011 19:59:06 -0700 Subject: [PATCH 095/326] Initial checking of new pingpair_irq example. Right now this is just the pingpair example but renamed. --- examples/pingpair_irq/Jamfile | 206 +++++++++++++++++++++++++ examples/pingpair_irq/pingpair_irq.pde | 180 +++++++++++++++++++++ examples/pingpair_irq/printf.h | 33 ++++ 3 files changed, 419 insertions(+) create mode 100644 examples/pingpair_irq/Jamfile create mode 100644 examples/pingpair_irq/pingpair_irq.pde create mode 100644 examples/pingpair_irq/printf.h diff --git a/examples/pingpair_irq/Jamfile b/examples/pingpair_irq/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/examples/pingpair_irq/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde new file mode 100644 index 0000000..b014e9b --- /dev/null +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -0,0 +1,180 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example of using Ack Payloads + * + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within + * the Ack packet of each transmission. Note that the payload is set BEFORE + * the sender's message arrives. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const short role_pin = 7; + +// +// Topology +// + +// Single radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_sender = 1, role_receiver } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_sender; + else + role = role_receiver; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_pl/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // We will be using the Ack Payload feature, so please enable it + radio.enableAckPayload(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens a single pipes for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. + + if ( role == role_sender ) + { + radio.openWritingPipe(pipe); + } + else + { + radio.openReadingPipe(1,pipe); + } + + // + // Start listening + // + + if ( role == role_receiver ) + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + static uint32_t message_count = 0; + + // + // Sender role. Repeatedly send the current time + // + + if (role == role_sender) + { + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + if ( radio.isAckPayloadAvailable() ) + { + radio.read(&message_count,sizeof(message_count)); + printf("Ack: [%lu] ",message_count); + } + printf("OK\n\r"); + + // Try again soon + delay(2000); + } + + // + // Receiver role. Receive each packet, dump it out, add ack payload for next time + // + + if ( role == role_receiver ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + static unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu\n",got_time); + } + + // Add an ack packet for the next time around. This is a simple + // packet counter + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); + ++message_count; + } + } +} +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_irq/printf.h b/examples/pingpair_irq/printf.h new file mode 100644 index 0000000..df6c46a --- /dev/null +++ b/examples/pingpair_irq/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From d3fff68a7cd3aaddc75ec989b50ab79a2704fe5c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 6 Jul 2011 20:49:59 -0700 Subject: [PATCH 096/326] Created a non-blocking write and an example for how to use it. --- RF24.cpp | 33 +++++++---- RF24.h | 18 +++++- examples/pingpair_irq/pingpair_irq.pde | 82 +++++++++++++++++++++----- 3 files changed, 102 insertions(+), 31 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index cb3d640..994d0ee 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -356,17 +356,8 @@ boolean RF24::write( const void* buf, uint8_t len ) { boolean result = false; - // Transmitter power-up - write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); - delay(2); - - // Send the payload - write_payload( buf, len ); - - // Allons! - ce(HIGH); - delayMicroseconds(15); - ce(LOW); + // Begin the write + startWrite(buf,len); // ------------ // At this point we could return from a non-blocking write, and then call @@ -379,7 +370,7 @@ boolean RF24::write( const void* buf, uint8_t len ) uint8_t observe_tx; uint8_t status; uint32_t sent_at = millis(); - const uint32_t timeout = 100; //ms to wait for timeout + const uint32_t timeout = 500; //ms to wait for timeout do { status = read_register(OBSERVE_TX,&observe_tx,1); @@ -420,6 +411,22 @@ boolean RF24::write( const void* buf, uint8_t len ) return result; } +/******************************************************************/ + +void RF24::startWrite( const void* buf, uint8_t len ) +{ + // Transmitter power-up + write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); + delay(2); + + // Send the payload + write_payload( buf, len ); + + // Allons! + ce(HIGH); + delayMicroseconds(15); + ce(LOW); +} /******************************************************************/ @@ -507,6 +514,8 @@ void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) tx_ok = status & _BV(TX_DS); tx_fail = status & _BV(MAX_RT); rx_ready = status & _BV(RX_DR); + + //print_status(status); } /******************************************************************/ diff --git a/RF24.h b/RF24.h index eafadaf..b2b3b1c 100644 --- a/RF24.h +++ b/RF24.h @@ -252,9 +252,6 @@ public: * getPayloadSize(). However, you can write less, and the remainder * will just be filled with zeroes. * - * @todo Write a non-blocking write to support users who want to - * check on progress separately or use an interrupt. - * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not @@ -403,6 +400,21 @@ public: */ boolean available(uint8_t* pipe_num); + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @see write() + * @see whatHappened() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + void startWrite( const void* buf, uint8_t len ); + /** * Enable custom payloads on the acknowledge packets * diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index b014e9b..abe7da0 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -7,12 +7,10 @@ */ /** - * Example of using Ack Payloads + * Example of using interrupts * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within - * the Ack packet of each transmission. Note that the payload is set BEFORE - * the sender's message arrives. + * This is an example of how to user interrupts to interact with the radio. + * It builds on the pingpair_pl example, and uses ack payloads. */ #include @@ -58,6 +56,13 @@ const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; // The role of the current running sketch role_e role; +// Message buffer to allow interrupt handler to print messages +bool message_ready; +char message[100]; + +// Interrupt handler, check the radio because we got an IRQ +void check_radio(void); + void setup(void) { // @@ -81,9 +86,15 @@ void setup(void) Serial.begin(57600); printf_begin(); - printf("\n\rRF24/examples/pingpair_pl/\n\r"); + printf("\n\rRF24/examples/pingpair_irq/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); + // + // Attach interrupt handler to interrupt #0 (using pin 2) + // + + attachInterrupt(0, check_radio, FALLING); + // // Setup and configure rf radio // @@ -93,6 +104,9 @@ void setup(void) // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); + // Pick a high channel + radio.setChannel(110); + // // Open pipes to other nodes for communication // @@ -123,9 +137,10 @@ void setup(void) radio.printDetails(); } +static uint32_t message_count = 0; + void loop(void) { - static uint32_t message_count = 0; // // Sender role. Repeatedly send the current time @@ -133,17 +148,10 @@ void loop(void) if (role == role_sender) { - // Take the time, and send it. This will block until complete + // Take the time, and send it. unsigned long time = millis(); printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - if ( radio.isAckPayloadAvailable() ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack: [%lu] ",message_count); - } - printf("OK\n\r"); + radio.startWrite( &time, sizeof(unsigned long) ); // Try again soon delay(2000); @@ -176,5 +184,47 @@ void loop(void) ++message_count; } } + + // + // Message handler. Display messages from the interrupt + // + if ( message_ready ) + { + message_ready = false; + Serial.println(message); + } } + +void check_radio(void) +{ + // What happened? + bool tx,fail,rx; + radio.whatHappened(tx,fail,rx); + + char *messageptr = message; + message_ready = true; + sprintf(message,"Unknown"); + + if ( tx ) + { + radio.powerDown(); + sprintf(messageptr,"Send:OK "); + messageptr += strlen(messageptr); + } + + if ( fail ) + { + radio.powerDown(); + sprintf(messageptr,"Send:Failed "); + messageptr += strlen(messageptr); + } + + if ( rx ) + { + radio.read(&message_count,sizeof(message_count)); + sprintf(messageptr,"Ack:%lu ",message_count); + messageptr += strlen(messageptr); + } +} + // vim:ai:cin:sts=2 sw=2 ft=cpp From 854956687f85bdb98ec2f50092bde885066eed81 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 6 Jul 2011 21:00:45 -0700 Subject: [PATCH 097/326] Simplified printing, ok to do just from the irq handler --- examples/pingpair_irq/pingpair_irq.pde | 27 ++++---------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index abe7da0..0da4f09 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -56,10 +56,6 @@ const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; // The role of the current running sketch role_e role; -// Message buffer to allow interrupt handler to print messages -bool message_ready; -char message[100]; - // Interrupt handler, check the radio because we got an IRQ void check_radio(void); @@ -150,7 +146,7 @@ void loop(void) { // Take the time, and send it. unsigned long time = millis(); - printf("Now sending %lu...",time); + printf("Now sending %lu\n\r",time); radio.startWrite( &time, sizeof(unsigned long) ); // Try again soon @@ -185,14 +181,6 @@ void loop(void) } } - // - // Message handler. Display messages from the interrupt - // - if ( message_ready ) - { - message_ready = false; - Serial.println(message); - } } void check_radio(void) @@ -201,29 +189,22 @@ void check_radio(void) bool tx,fail,rx; radio.whatHappened(tx,fail,rx); - char *messageptr = message; - message_ready = true; - sprintf(message,"Unknown"); - if ( tx ) { radio.powerDown(); - sprintf(messageptr,"Send:OK "); - messageptr += strlen(messageptr); + printf("Send:OK\n\r"); } if ( fail ) { radio.powerDown(); - sprintf(messageptr,"Send:Failed "); - messageptr += strlen(messageptr); + printf("Send:Failed\n\r"); } if ( rx ) { radio.read(&message_count,sizeof(message_count)); - sprintf(messageptr,"Ack:%lu ",message_count); - messageptr += strlen(messageptr); + printf("Ack:%lu\n\r",message_count); } } From 062d83346b27c03bcc4789c6b94f12ee3ed6be9b Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 8 Jul 2011 22:10:19 -0700 Subject: [PATCH 098/326] Now both roles use the IRQ. Thanks to Mike Denzien for testing & troubleshooting. --- examples/pingpair_irq/pingpair_irq.pde | 95 ++++++++++++++------------ 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index 0da4f09..9052719 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -85,12 +85,6 @@ void setup(void) printf("\n\rRF24/examples/pingpair_irq/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); - // - // Attach interrupt handler to interrupt #0 (using pin 2) - // - - attachInterrupt(0, check_radio, FALLING); - // // Setup and configure rf radio // @@ -100,14 +94,17 @@ void setup(void) // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); - // Pick a high channel + // Optional: Increase CRC length for improved reliability + radio.setCRCLength(RF24_CRC_16); + + // Optional: Pick a high channel radio.setChannel(110); // // Open pipes to other nodes for communication // - // This simple sketch opens a single pipes for these two nodes to communicate + // This simple sketch opens a single pipe for these two nodes to communicate // back and forth. One listens on it, the other talks to it. if ( role == role_sender ) @@ -131,13 +128,19 @@ void setup(void) // radio.printDetails(); + + // + // Attach interrupt handler to interrupt #0 (using pin 2) + // on BOTH the sender and receiver + // + + attachInterrupt(0, check_radio, FALLING); } static uint32_t message_count = 0; void loop(void) { - // // Sender role. Repeatedly send the current time // @@ -152,34 +155,10 @@ void loop(void) // Try again soon delay(2000); } - + // - // Receiver role. Receive each packet, dump it out, add ack payload for next time + // Receiver role: Does nothing! All the work is in IRQ // - - if ( role == role_receiver ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - static unsigned long got_time; - boolean done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu\n",got_time); - } - - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); - ++message_count; - } - } } @@ -188,23 +167,55 @@ void check_radio(void) // What happened? bool tx,fail,rx; radio.whatHappened(tx,fail,rx); - + + // Have we successfully transmitted? if ( tx ) { - radio.powerDown(); - printf("Send:OK\n\r"); + if ( role == role_sender ) + printf("Send:OK\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Sent\n\r"); } + // Have we failed to transmit? if ( fail ) { - radio.powerDown(); - printf("Send:Failed\n\r"); + if ( role == role_sender ) + printf("Send:Failed\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Failed\n\r"); } + // Transmitter can power down for now, because + // the transmission is done. + if ( ( tx || fail ) && ( role == role_sender ) ) + radio.powerDown(); + + // Did we receive a message? if ( rx ) { - radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu\n\r",message_count); + // If we're the sender, we've received an ack payload + if ( role == role_sender ) + { + radio.read(&message_count,sizeof(message_count)); + printf("Ack:%lu\n\r",message_count); + } + + // If we're the receiver, we've received a time message + if ( role == role_receiver ) + { + // Get this payload and dump it + static unsigned long got_time; + radio.read( &got_time, sizeof(got_time) ); + printf("Got payload %lu\n\r",got_time); + + // Add an ack packet for the next time around. This is a simple + // packet counter + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); + ++message_count; + } } } From d5a3e79c665f29bca7940b2889773f0165accc55 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 8 Jul 2011 22:15:37 -0700 Subject: [PATCH 099/326] astyle -A1 -s2 --- .gitignore | 1 + examples/led_remote/led_remote.pde | 74 ++++++++-------- examples/pingpair/pingpair.pde | 56 ++++++------ examples/pingpair_irq/pingpair_irq.pde | 34 ++++---- examples/pingpair_pl/pingpair_pl.pde | 46 +++++----- examples/pingpair_sleepy/pingpair_sleepy.pde | 89 ++++++++++---------- examples/scanner/scanner.pde | 16 ++-- examples/starping/starping.pde | 66 +++++++-------- 8 files changed, 192 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 5dae4ce..bac8ddd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.o .*.swp +*.orig docs/ output/ ojam/ diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index 53fc68b..7550a90 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -1,22 +1,22 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. */ /** - * Example LED Remote + * Example LED Remote * * This is an example of how to use the RF24 class to control a remote * bank of LED's using buttons on a remote control. * - * On the 'remote', connect any number of buttons or switches from + * On the 'remote', connect any number of buttons or switches from * an arduino pin to ground. Update 'button_pins' to reflect the * pins used. * - * On the 'led' board, connect the same number of LED's from an + * On the 'led' board, connect the same number of LED's from an * arduino pin to a resistor to ground. Update 'led_pins' to reflect * the pins used. Also connect a separate pin to ground and change * the 'role_pin'. This tells the sketch it's running on the LED board. @@ -24,7 +24,7 @@ * Every time the buttons change on the remote, the entire state of * buttons is send to the led board, which displays the state. */ - + #include #include "nRF24L01.h" #include "RF24.h" @@ -38,15 +38,15 @@ RF24 radio(8,9); -// sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver -// Leave open to be the 'remote' transmitter +// sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver +// Leave open to be the 'remote' transmitter const int role_pin = A4; // Pins on the remote for buttons const uint8_t button_pins[] = { 2,3,4,5,6,7 }; const uint8_t num_button_pins = sizeof(button_pins); -// Pins on the LED board for LED's +// Pins on the LED board for LED's const uint8_t led_pins[] = { 2,3,4,5,6,7 }; const uint8_t num_led_pins = sizeof(led_pins); @@ -84,7 +84,7 @@ uint8_t button_states[num_button_pins]; uint8_t led_states[num_led_pins]; // -// Setup +// Setup // void setup(void) @@ -92,12 +92,12 @@ void setup(void) // // Role // - + // set up the role pin pinMode(role_pin, INPUT); digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin - + // read the address pin, establish our role if ( digitalRead(role_pin) ) role = role_remote; @@ -107,7 +107,7 @@ void setup(void) // // Print preamble // - + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/led_remote/\n\r"); @@ -116,16 +116,16 @@ void setup(void) // // Setup and configure rf radio // - + radio.begin(); - + // // Open pipes to other nodes for communication // - + // This simple sketch opens a single pipes for these two nodes to communicate // back and forth. One listens on it, the other talks to it. - + if ( role == role_remote ) { radio.openWritingPipe(pipe); @@ -138,14 +138,14 @@ void setup(void) // // Start listening // - + if ( role == role_led ) radio.startListening(); // // Dump the configuration of the rf unit for debugging // - + radio.printDetails(); // @@ -178,7 +178,7 @@ void setup(void) } // -// Loop +// Loop // void loop(void) @@ -187,7 +187,7 @@ void loop(void) // Remote role. If the state of any button has changed, send the whole state of // all buttons. // - + if ( role == role_remote ) { // Get the current state of buttons, and @@ -199,8 +199,8 @@ void loop(void) uint8_t state = ! digitalRead(button_pins[i]); if ( state != button_states[i] ) { - different = true; - button_states[i] = state; + different = true; + button_states[i] = state; } } @@ -210,19 +210,19 @@ void loop(void) printf("Now sending..."); bool ok = radio.write( button_states, num_button_pins ); if (ok) - printf("ok\n\r"); + printf("ok\n\r"); else - printf("failed\n\r"); + printf("failed\n\r"); } // Try again in a short while delay(20); } - + // // LED role. Receive the state of all buttons, and reflect that in the LEDs // - + if ( role == role_led ) { // if there is data ready @@ -234,20 +234,20 @@ void loop(void) { // Fetch the payload, and see if this was the last one. done = radio.read( button_states, num_button_pins ); - + // Spew it printf("Got buttons\n\r"); - // For each button, if the button now on, then toggle the LED - int i = num_led_pins; - while(i--) - { - if ( button_states[i] ) - { - led_states[i] ^= HIGH; - digitalWrite(led_pins[i],led_states[i]); - } - } + // For each button, if the button now on, then toggle the LED + int i = num_led_pins; + while(i--) + { + if ( button_states[i] ) + { + led_states[i] ^= HIGH; + digitalWrite(led_pins[i],led_states[i]); + } + } } } } diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 1f0429f..c0d5480 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -1,6 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. @@ -14,7 +14,7 @@ * which responds by sending the value back. The ping node can then see how long the whole cycle * took. */ - + #include #include "nRF24L01.h" #include "RF24.h" @@ -29,7 +29,7 @@ RF24 radio(8,9); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter +// Leave open to be the 'ping' transmitter const int role_pin = 7; // @@ -63,12 +63,12 @@ void setup(void) // // Role // - + // set up the role pin pinMode(role_pin, INPUT); digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin - + // read the address pin, establish our role if ( digitalRead(role_pin) ) role = role_ping_out; @@ -78,7 +78,7 @@ void setup(void) // // Print preamble // - + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/pingpair/\n\r"); @@ -87,11 +87,11 @@ void setup(void) // // Setup and configure rf radio // - + radio.begin(); // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); + radio.setRetries(15,15); // optionally, use a high channel to avoid WiFi chatter radio.setChannel(110); @@ -102,12 +102,12 @@ void setup(void) // // Open pipes to other nodes for communication // - + // This simple sketch opens two pipes for these two nodes to communicate // back and forth. // Open 'our' pipe for writing // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - + if ( role == role_ping_out ) { radio.openWritingPipe(pipes[0]); @@ -122,13 +122,13 @@ void setup(void) // // Start listening // - + radio.startListening(); - + // // Dump the configuration of the rf unit for debugging // - + radio.printDetails(); } @@ -137,27 +137,27 @@ void loop(void) // // Ping out role. Repeatedly send the current time // - + if (role == role_ping_out) { // First, stop listening so we can talk. radio.stopListening(); - + // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - + radio.write( &time, sizeof(unsigned long) ); + // Now, continue listening radio.startListening(); - + // Wait here until we get a response, or timeout (250ms) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) if (millis() - started_waiting_at > 250 ) timeout = true; - + // Describe the results if ( timeout ) { @@ -168,19 +168,19 @@ void loop(void) // Grab the response, compare, and send to debugging spew unsigned long got_time; radio.read( &got_time, sizeof(unsigned long) ); - + // Spew it printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); } - + // Try again 1s later delay(1000); } - + // // Pong back role. Receive each packet, dump it out, and send it back // - + if ( role == role_pong_back ) { // if there is data ready @@ -193,18 +193,18 @@ void loop(void) { // Fetch the payload, and see if this was the last one. done = radio.read( &got_time, sizeof(unsigned long) ); - + // Spew it printf("Got payload %lu...",got_time); } - + // First, stop listening so we can talk radio.stopListening(); - + // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); + radio.write( &got_time, sizeof(unsigned long) ); printf("Sent response.\n\r"); - + // Now, resume listening so we catch the next packets. radio.startListening(); } diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index 9052719..e8c5396 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -1,6 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. @@ -12,7 +12,7 @@ * This is an example of how to user interrupts to interact with the radio. * It builds on the pingpair_pl example, and uses ack payloads. */ - + #include #include "nRF24L01.h" #include "RF24.h" @@ -27,7 +27,7 @@ RF24 radio(8,9); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter +// Leave open to be the 'ping' transmitter const short role_pin = 7; // @@ -64,12 +64,12 @@ void setup(void) // // Role // - + // set up the role pin pinMode(role_pin, INPUT); digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin - + // read the address pin, establish our role if ( digitalRead(role_pin) ) role = role_sender; @@ -79,7 +79,7 @@ void setup(void) // // Print preamble // - + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/pingpair_irq/\n\r"); @@ -88,9 +88,9 @@ void setup(void) // // Setup and configure rf radio // - + radio.begin(); - + // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); @@ -103,10 +103,10 @@ void setup(void) // // Open pipes to other nodes for communication // - + // This simple sketch opens a single pipe for these two nodes to communicate // back and forth. One listens on it, the other talks to it. - + if ( role == role_sender ) { radio.openWritingPipe(pipe); @@ -119,16 +119,16 @@ void setup(void) // // Start listening // - + if ( role == role_receiver ) radio.startListening(); // // Dump the configuration of the rf unit for debugging // - + radio.printDetails(); - + // // Attach interrupt handler to interrupt #0 (using pin 2) // on BOTH the sender and receiver @@ -144,15 +144,15 @@ void loop(void) // // Sender role. Repeatedly send the current time // - + if (role == role_sender) { // Take the time, and send it. unsigned long time = millis(); printf("Now sending %lu\n\r",time); radio.startWrite( &time, sizeof(unsigned long) ); - - // Try again soon + + // Try again soon delay(2000); } @@ -206,7 +206,7 @@ void check_radio(void) // If we're the receiver, we've received a time message if ( role == role_receiver ) { - // Get this payload and dump it + // Get this payload and dump it static unsigned long got_time; radio.read( &got_time, sizeof(got_time) ); printf("Got payload %lu\n\r",got_time); diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index b014e9b..da1dc36 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -1,6 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. @@ -9,12 +9,12 @@ /** * Example of using Ack Payloads * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within * the Ack packet of each transmission. Note that the payload is set BEFORE * the sender's message arrives. */ - + #include #include "nRF24L01.h" #include "RF24.h" @@ -29,7 +29,7 @@ RF24 radio(8,9); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter +// Leave open to be the 'ping' transmitter const short role_pin = 7; // @@ -63,12 +63,12 @@ void setup(void) // // Role // - + // set up the role pin pinMode(role_pin, INPUT); digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin - + // read the address pin, establish our role if ( digitalRead(role_pin) ) role = role_sender; @@ -78,7 +78,7 @@ void setup(void) // // Print preamble // - + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/pingpair_pl/\n\r"); @@ -87,19 +87,19 @@ void setup(void) // // Setup and configure rf radio // - + radio.begin(); - + // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); // // Open pipes to other nodes for communication // - + // This simple sketch opens a single pipes for these two nodes to communicate // back and forth. One listens on it, the other talks to it. - + if ( role == role_sender ) { radio.openWritingPipe(pipe); @@ -112,25 +112,25 @@ void setup(void) // // Start listening // - + if ( role == role_receiver ) radio.startListening(); // // Dump the configuration of the rf unit for debugging // - + radio.printDetails(); } void loop(void) { static uint32_t message_count = 0; - + // // Sender role. Repeatedly send the current time // - + if (role == role_sender) { // Take the time, and send it. This will block until complete @@ -142,17 +142,17 @@ void loop(void) { radio.read(&message_count,sizeof(message_count)); printf("Ack: [%lu] ",message_count); - } + } printf("OK\n\r"); - - // Try again soon + + // Try again soon delay(2000); } - + // // Receiver role. Receive each packet, dump it out, add ack payload for next time // - + if ( role == role_receiver ) { // if there is data ready @@ -165,11 +165,11 @@ void loop(void) { // Fetch the payload, and see if this was the last one. done = radio.read( &got_time, sizeof(unsigned long) ); - + // Spew it printf("Got payload %lu\n",got_time); } - + // Add an ack packet for the next time around. This is a simple // packet counter radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index c8edc6b..7ef0db2 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -1,6 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. @@ -15,11 +15,11 @@ * ping/pong cycle. * * As with the pingpair.pde example, write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current + * connect the role_pin to ground on one. The ping node sends the current * time to the pong node, which responds by sending the value back. The ping * node can then see how long the whole cycle took. */ - + #include #include #include @@ -36,7 +36,7 @@ RF24 radio(8,9); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter +// Leave open to be the 'ping' transmitter const int role_pin = 7; // @@ -86,12 +86,12 @@ void setup(void) // // Role // - + // set up the role pin pinMode(role_pin, INPUT); digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin - + // read the address pin, establish our role if ( digitalRead(role_pin) ) role = role_ping_out; @@ -101,7 +101,7 @@ void setup(void) // // Print preamble // - + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); @@ -118,18 +118,18 @@ void setup(void) // // Setup and configure rf radio // - + radio.begin(); // // Open pipes to other nodes for communication // - + // This simple sketch opens two pipes for these two nodes to communicate // back and forth. // Open 'our' pipe for writing // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - + if ( role == role_ping_out ) { radio.openWritingPipe(pipes[0]); @@ -144,13 +144,13 @@ void setup(void) // // Start listening // - + radio.startListening(); - + // // Dump the configuration of the rf unit for debugging // - + radio.printDetails(); } @@ -159,27 +159,27 @@ void loop(void) // // Ping out role. Repeatedly send the current time // - + if (role == role_ping_out) { // First, stop listening so we can talk. radio.stopListening(); - + // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - + radio.write( &time, sizeof(unsigned long) ); + // Now, continue listening radio.startListening(); - + // Wait here until we get a response, or timeout (250ms) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) if (millis() - started_waiting_at > 250 ) timeout = true; - + // Describe the results if ( timeout ) { @@ -190,11 +190,11 @@ void loop(void) // Grab the response, compare, and send to debugging spew unsigned long got_time; radio.read( &got_time, sizeof(unsigned long) ); - + // Spew it printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); } - + // // Shut down the system // @@ -210,16 +210,16 @@ void loop(void) // continue execution here. while( sleep_cycles_remaining ) do_sleep(); - + sleep_cycles_remaining = sleep_cycles_per_transmission; } - + // // Pong back role. Receive each packet, dump it out, and send it back // // This is untouched from the pingpair example. // - + if ( role == role_pong_back ) { // if there is data ready @@ -232,19 +232,19 @@ void loop(void) { // Fetch the payload, and see if this was the last one. done = radio.read( &got_time, sizeof(unsigned long) ); - + // Spew it. Include our time, because the ping_out millis counter is unreliable - // due to it sleeping + // due to it sleeping printf("Got payload %lu @ %lu...",got_time,millis()); } - + // First, stop listening so we can talk radio.stopListening(); - + // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); + radio.write( &got_time, sizeof(unsigned long) ); printf("Sent response.\n\r"); - + // Now, resume listening so we catch the next packets. radio.startListening(); } @@ -252,36 +252,37 @@ void loop(void) } // -// Sleep helpers +// Sleep helpers // // 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms // 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec -void setup_watchdog(uint8_t prescalar) +void setup_watchdog(uint8_t prescalar) { - prescalar = min(9,prescalar); - uint8_t wdtcsr = prescalar & 7; - if ( prescalar & 8 ) - wdtcsr |= _BV(WDP3); + prescalar = min(9,prescalar); + uint8_t wdtcsr = prescalar & 7; + if ( prescalar & 8 ) + wdtcsr |= _BV(WDP3); - MCUSR &= ~_BV(WDRF); - WDTCSR = _BV(WDCE) | _BV(WDE); - WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); + MCUSR &= ~_BV(WDRF); + WDTCSR = _BV(WDCE) | _BV(WDE); + WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); } -ISR(WDT_vect) { +ISR(WDT_vect) +{ --sleep_cycles_remaining; } void do_sleep(void) { - set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here - sleep_enable(); + set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here + sleep_enable(); - sleep_mode(); // System sleeps here + sleep_mode(); // System sleeps here - sleep_disable(); // System continues execution here when watchdog timed out + sleep_disable(); // System continues execution here when watchdog timed out } // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.pde index efefa17..f8beaf9 100644 --- a/examples/scanner/scanner.pde +++ b/examples/scanner/scanner.pde @@ -1,7 +1,7 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. @@ -47,15 +47,15 @@ void setup(void) // // Print preamble // - + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/scanner/\n\r"); - + // // Setup and configure rf radio // - + radio.begin(); radio.setAutoAck(false); @@ -63,7 +63,7 @@ void setup(void) radio.startListening(); radio.stopListening(); - // Print out header, high then low digit + // Print out header, high then low digit int i = 0; while ( i < num_channels ) { @@ -88,7 +88,7 @@ const short num_reps = 100; void loop(void) { - // Clear measurement values + // Clear measurement values memset(values,0,num_channels); // Scan all channels num_reps times @@ -108,11 +108,11 @@ void loop(void) // Did we get a carrier? if ( radio.testCarrier() ) - ++values[i]; + ++values[i]; } } - // Print out channel measurements, clamped to a single hex digit + // Print out channel measurements, clamped to a single hex digit int i = 0; while ( i < num_channels ) { diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index 1d02bcd..28470c1 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -1,18 +1,18 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. */ /** - * Example RF Radio Ping Star Group + * Example RF Radio Ping Star Group * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will + * unit will send out the value of millis() once a second. The pong unit will * respond back with a copy of the value. Each ping unit can get that response * back, and determine how long the whole cycle took. * @@ -20,7 +20,7 @@ * The pong receiver is identified by having its role_pin tied to ground. * The ping senders are further differentiated by a byte in eeprom. */ - + #include #include #include "nRF24L01.h" @@ -88,12 +88,12 @@ void setup(void) // // Role // - + // set up the role pin pinMode(role_pin, INPUT); digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin - + // read the address pin, establish our role if ( digitalRead(role_pin) ) role = role_ping_out; @@ -115,7 +115,7 @@ void setup(void) // address. if ( reading >= 2 && reading <= 6 ) node_address = reading; - + // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' else { @@ -127,7 +127,7 @@ void setup(void) // // Print preamble // - + Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/starping/\n\r"); @@ -137,7 +137,7 @@ void setup(void) // // Setup and configure rf radio // - + radio.begin(); // @@ -154,27 +154,27 @@ void setup(void) radio.openReadingPipe(4,talking_pipes[3]); radio.openReadingPipe(5,talking_pipes[4]); } - - // Each ping node has a talking pipe that it will ping into, and a listening - // pipe that it will listen for the pong. + + // Each ping node has a talking pipe that it will ping into, and a listening + // pipe that it will listen for the pong. if ( role == role_ping_out ) { // Write on our talking pipe radio.openWritingPipe(talking_pipes[node_address-2]); - // Listen on our listening pipe + // Listen on our listening pipe radio.openReadingPipe(1,listening_pipes[node_address-2]); } // // Start listening // - + radio.startListening(); - + // // Dump the configuration of the rf unit for debugging // - + radio.printDetails(); // @@ -192,27 +192,27 @@ void loop(void) // // Ping out role. Repeatedly send the current time // - + if (role == role_ping_out) { // First, stop listening so we can talk. radio.stopListening(); - + // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - + radio.write( &time, sizeof(unsigned long) ); + // Now, continue listening radio.startListening(); - + // Wait here until we get a response, or timeout (250ms) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) if (millis() - started_waiting_at > 250 ) timeout = true; - + // Describe the results if ( timeout ) { @@ -223,19 +223,19 @@ void loop(void) // Grab the response, compare, and send to debugging spew unsigned long got_time; radio.read( &got_time, sizeof(unsigned long) ); - + // Spew it printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); } - + // Try again 1s later delay(1000); } - + // // Pong back role. Receive each packet, dump it out, and send it back // - + if ( role == role_pong_back ) { // if there is data ready @@ -249,11 +249,11 @@ void loop(void) { // Fetch the payload, and see if this was the last one. done = radio.read( &got_time, sizeof(unsigned long) ); - + // Spew it printf("Got payload %lu from node %i...",got_time,pipe_num+1); } - + // First, stop listening so we can talk radio.stopListening(); @@ -262,11 +262,11 @@ void loop(void) // Retain the low 2 bytes to identify the pipe for the spew uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; - + // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); + radio.write( &got_time, sizeof(unsigned long) ); printf("Sent response to %04x.\n\r",pipe_id); - + // Now, resume listening so we catch the next packets. radio.startListening(); } From f8e6984ccc15ef0423fc04843ecb533a762d6247 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 8 Jul 2011 22:19:54 -0700 Subject: [PATCH 100/326] docupdate --- RF24.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/RF24.h b/RF24.h index b2b3b1c..6b4ab19 100644 --- a/RF24.h +++ b/RF24.h @@ -466,7 +466,7 @@ public: * @param[out] tx_fail The send failed, too many retries (MAX_RT) * @param[out] rx_ready There is a message waiting to be read (RX_DS) */ - void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx); + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); /** * Enable or disable auto-acknowlede packets @@ -548,6 +548,13 @@ public: * the sender's message arrives. */ +/** + * @example pingpair_irq.pde + * + * This is an example of how to user interrupts to interact with the radio. + * It builds on the pingpair_pl example, and uses ack payloads. + */ + /** * @example pingpair_sleepy.pde * From b000a87aa1f9582a887569591fbd8edbfacf1f8f Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 8 Jul 2011 22:25:44 -0700 Subject: [PATCH 101/326] Optimization of whatHappened, suggested by Mike Denzien --- RF24.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 994d0ee..d0cbe12 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -504,18 +504,13 @@ boolean RF24::read( void* buf, uint8_t len ) void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) { - // Read the status - uint8_t status = get_status(); - - // Reset the status - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + // Read the status & reset the status in one easy call + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Report to the user what happened tx_ok = status & _BV(TX_DS); tx_fail = status & _BV(MAX_RT); rx_ready = status & _BV(RX_DR); - - //print_status(status); } /******************************************************************/ From 32e8b3fbbbac9b744d3529fa4c7bd956c3b6c096 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 8 Jul 2011 22:29:16 -0700 Subject: [PATCH 102/326] astyle -A1 -s2 --- RF24.cpp | 120 ++++---- RF24.h | 872 +++++++++++++++++++++++++++---------------------------- 2 files changed, 499 insertions(+), 493 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index d0cbe12..14cf9e5 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -1,6 +1,6 @@ /* Copyright (C) 2011 James Coliz, Jr. - + 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. @@ -19,14 +19,14 @@ #endif // Avoid spurious warnings -#undef PROGMEM -#define PROGMEM __attribute__(( section(".progmem.data") )) -#undef PSTR +#undef PROGMEM +#define PROGMEM __attribute__(( section(".progmem.data") )) +#undef PSTR #define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) /******************************************************************/ -void RF24::csn(int mode) +void RF24::csn(int mode) { SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV8); @@ -42,7 +42,7 @@ void RF24::ce(int mode) /******************************************************************/ -uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) { uint8_t status; @@ -58,7 +58,7 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) /******************************************************************/ -uint8_t RF24::read_register(uint8_t reg) +uint8_t RF24::read_register(uint8_t reg) { csn(LOW); SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); @@ -124,7 +124,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) /******************************************************************/ -uint8_t RF24::read_payload(void* buf, uint8_t len) +uint8_t RF24::read_payload(void* buf, uint8_t len) { uint8_t status; uint8_t* current = reinterpret_cast(buf); @@ -149,7 +149,7 @@ uint8_t RF24::flush_rx(void) uint8_t status; csn(LOW); - status = SPI.transfer( FLUSH_RX ); + status = SPI.transfer( FLUSH_RX ); csn(HIGH); return status; @@ -162,7 +162,7 @@ uint8_t RF24::flush_tx(void) uint8_t status; csn(LOW); - status = SPI.transfer( FLUSH_TX ); + status = SPI.transfer( FLUSH_TX ); csn(HIGH); return status; @@ -170,7 +170,7 @@ uint8_t RF24::flush_tx(void) /******************************************************************/ -uint8_t RF24::get_status(void) +uint8_t RF24::get_status(void) { uint8_t status; @@ -186,29 +186,29 @@ uint8_t RF24::get_status(void) void RF24::print_status(uint8_t status) { printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), - status, - (status & _BV(RX_DR))?1:0, - (status & _BV(TX_DS))?1:0, - (status & _BV(MAX_RT))?1:0, - ((status >> RX_P_NO) & B111), - (status & _BV(TX_FULL))?1:0 - ); + status, + (status & _BV(RX_DR))?1:0, + (status & _BV(TX_DS))?1:0, + (status & _BV(MAX_RT))?1:0, + ((status >> RX_P_NO) & B111), + (status & _BV(TX_FULL))?1:0 + ); } /******************************************************************/ -void RF24::print_observe_tx(uint8_t value) +void RF24::print_observe_tx(uint8_t value) { printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r"), - value, - (value >> PLOS_CNT) & B1111, - (value >> ARC_CNT) & B1111 - ); + value, + (value >> PLOS_CNT) & B1111, + (value >> ARC_CNT) & B1111 + ); } /******************************************************************/ -void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) +void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; printf_P(PSTR("%S\t%c ="),name,extra_tab); @@ -219,11 +219,11 @@ void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) /******************************************************************/ -void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) +void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; printf_P(PSTR("%S\t%c ="),name,extra_tab); - + while (qty--) { uint8_t buffer[5]; @@ -232,7 +232,7 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) printf_P(PSTR(" 0x")); uint8_t* bufptr = buffer + sizeof buffer; while( --bufptr >= buffer ) - printf_P(PSTR("%02x"),*bufptr); + printf_P(PSTR("%02x"),*bufptr); } printf_P(PSTR("\n\r")); @@ -240,7 +240,7 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) /******************************************************************/ -RF24::RF24(uint8_t _cepin, uint8_t _cspin): +RF24::RF24(uint8_t _cepin, uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) { } @@ -249,7 +249,7 @@ RF24::RF24(uint8_t _cepin, uint8_t _cspin): void RF24::setChannel(uint8_t channel) { - write_register(RF_CH,min(channel,127)); + write_register(RF_CH,min(channel,127)); } /******************************************************************/ @@ -261,14 +261,14 @@ void RF24::setPayloadSize(uint8_t size) /******************************************************************/ -uint8_t RF24::getPayloadSize(void) +uint8_t RF24::getPayloadSize(void) { return payload_size; } /******************************************************************/ -void RF24::printDetails(void) +void RF24::printDetails(void) { print_status(get_status()); @@ -304,13 +304,13 @@ void RF24::begin(void) // Reset current status write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - + // Initialize CRC write_register(CONFIG, _BV(EN_CRC) ); // Flush buffers flush_rx(); - flush_tx(); + flush_tx(); // Set up default configuration. Callers can always change it later. setChannel(1); @@ -322,14 +322,14 @@ void RF24::startListening(void) { write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - + // Restore the pipe0 adddress write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); // Flush buffers flush_rx(); - // Go! + // Go! ce(HIGH); // wait for the radio to come up (130us actually only needed) @@ -370,7 +370,7 @@ boolean RF24::write( const void* buf, uint8_t len ) uint8_t observe_tx; uint8_t status; uint32_t sent_at = millis(); - const uint32_t timeout = 500; //ms to wait for timeout + const uint32_t timeout = 500; //ms to wait for timeout do { status = read_register(OBSERVE_TX,&observe_tx,1); @@ -407,7 +407,7 @@ boolean RF24::write( const void* buf, uint8_t len ) powerDown(); // Flush buffers (Is this a relic of past experimentation, and not needed anymore??) - flush_tx(); + flush_tx(); return result; } @@ -444,19 +444,19 @@ uint8_t RF24::read_payload_length(void) /******************************************************************/ -boolean RF24::available(void) +boolean RF24::available(void) { return available(NULL); } /******************************************************************/ -boolean RF24::available(uint8_t* pipe_num) +boolean RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); // Too noisy, enable if you really want lots o data!! IF_SERIAL_DEBUG(print_status(status)); - + boolean result = ( status & _BV(RX_DR) ); if (result) @@ -484,7 +484,7 @@ boolean RF24::available(uint8_t* pipe_num) /******************************************************************/ -boolean RF24::read( void* buf, uint8_t len ) +boolean RF24::read( void* buf, uint8_t len ) { // was this the last of the data available? boolean result = false; @@ -504,7 +504,7 @@ boolean RF24::read( void* buf, uint8_t len ) void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) { - // Read the status & reset the status in one easy call + // Read the status & reset the status in one easy call uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Report to the user what happened @@ -518,10 +518,10 @@ void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) void RF24::openWritingPipe(uint64_t value) { // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01 - // expects it LSB first too, so we're good. - + // expects it LSB first too, so we're good. + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); - write_register(TX_ADDR, reinterpret_cast(&value), 5); + write_register(TX_ADDR, reinterpret_cast(&value), 5); write_register(RX_PW_P0,min(payload_size,32)); } @@ -529,12 +529,18 @@ void RF24::openWritingPipe(uint64_t value) void RF24::openReadingPipe(uint8_t child, uint64_t value) { - const uint8_t child_pipe[] = { - RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 }; - const uint8_t child_payload_size[] = { - RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 }; - const uint8_t child_pipe_enable[] = { - ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 }; + const uint8_t child_pipe[] = + { + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 + }; + const uint8_t child_payload_size[] = + { + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 + }; + const uint8_t child_pipe_enable[] = + { + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 + }; // If this is pipe 0, cache the address. This is needed because // openWritingPipe() will overwrite the pipe 0 address, so @@ -546,11 +552,11 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) { // For pipes 2-5, only write the LSB if ( child < 2 ) - write_register(child_pipe[child], reinterpret_cast(&value), 5); - else - write_register(child_pipe[child], reinterpret_cast(&value), 1); - - write_register(child_payload_size[child],payload_size); + write_register(child_pipe[child], reinterpret_cast(&value), 5); + else + write_register(child_pipe[child], reinterpret_cast(&value), 1); + + write_register(child_payload_size[child],payload_size); // Note it would be more efficient to set all of the bits for all open // pipes at once. However, I thought it would make the calling code @@ -560,7 +566,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) } /******************************************************************/ - + void RF24::toggle_features(void) { csn(LOW); diff --git a/RF24.h b/RF24.h index 6b4ab19..4e7fb79 100644 --- a/RF24.h +++ b/RF24.h @@ -21,488 +21,488 @@ typedef enum { RF24_CRC_8 = 0, RF24_CRC_16 } rf24_crclength_e; class RF24 { private: - uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ - uint8_t csn_pin; /**< SPI Chip select */ - uint8_t payload_size; /**< Fixed size of payloads */ - boolean ack_payload_available; /**< Whether there is an ack payload waiting */ - uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ - uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + uint8_t payload_size; /**< Fixed size of payloads */ + boolean ack_payload_available; /**< Whether there is an ack payload waiting */ + uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ protected: - /** - * @name Low-level internal interface. - * - * Protected methods that address the chip directly. Regular users cannot - * ever call these. They are documented for completeness and for developers who - * may want to extend this class. - */ - /**@{*/ + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. + */ + /**@{*/ - /** - * Set chip select pin - * - * @param mode HIGH to take this unit off the SPI bus, LOW to put it on - */ - void csn(int mode) ; + /** + * Set chip select pin + * + * @param mode HIGH to take this unit off the SPI bus, LOW to put it on + */ + void csn(int mode); - /** - * Set chip enable - * - * @param mode HIGH to actively begin transmission or LOW to put in standby. Please see data sheet - * for a much more detailed description of this pin. - */ - void ce(int mode); + /** + * Set chip enable + * + * @param mode HIGH to actively begin transmission or LOW to put in standby. Please see data sheet + * for a much more detailed description of this pin. + */ + void ce(int mode); - /** - * Read a chunk of data in from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to put the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); - /** - * Read single byte from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @return Current value of register @p reg - */ - uint8_t read_register(uint8_t reg); + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); - /** - * Write a chunk of data to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to get the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); - /** - * Write a single byte to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param value The new value to write - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, uint8_t value); + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); - /** - * Write the transmit payload - * - * The size of data written is the fixed payload size, see getPayloadSize() - * - * @param buf Where to get the data - * @param len Number of bytes to be sent - * @return Current value of status register - */ - uint8_t write_payload(const void* buf, uint8_t len); + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @param len Number of bytes to be sent + * @return Current value of status register + */ + uint8_t write_payload(const void* buf, uint8_t len); - /** - * Read the receive payload - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @param buf Where to put the data - * @param len Maximum number of bytes to read - * @return Current value of status register - */ - uint8_t read_payload(void* buf, uint8_t len) ; + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register + */ + uint8_t read_payload(void* buf, uint8_t len); - /** - * Read the payload length - * - * For dynamic payloads, this pulls the size of the payload off - * the chip - * - * @return Payload length of last-received dynamic payload - */ - uint8_t read_payload_length(void); + /** + * Read the payload length + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @return Payload length of last-received dynamic payload + */ + uint8_t read_payload_length(void); - /** - * Empty the receive buffer - * - * @return Current value of status register - */ - uint8_t flush_rx(void); + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); - /** - * Empty the transmit buffer - * - * @return Current value of status register - */ - uint8_t flush_tx(void); + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); - /** - * Retrieve the current status of the chip - * - * @return Current value of status register - */ - uint8_t get_status(void) ; + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void); - /** - * Decode and print the given status to stdout - * - * @param status Status value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_status(uint8_t status) ; + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status); - /** - * Decode and print the given 'observe_tx' value to stdout - * - * @param value The observe_tx value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_observe_tx(uint8_t value) ; + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value); - /** - * Print the name and value of an 8-bit register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_byte_register(prog_char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Print the name and value of a 40-bit address register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_address_register(prog_char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Turn on or off the special features of the chip - * - * The chip has certain 'features' which are only available when the 'features' - * are enabled. See the datasheet for details. - */ - void toggle_features(void); - /**@}*/ + /** + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_byte_register(prog_char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(prog_char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); + /**@}*/ public: - /** - * @name Primary public interface - * - * These are the main methods you need to operate the chip - */ - /**@{*/ + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ - /** - * Constructor - * - * Creates a new instance of this driver. Before using, you create an instance - * and send in the unique pins that this chip is connected to. - * - * @param _cepin The pin attached to Chip Enable on the RF module - * @param _cspin The pin attached to Chip Select - */ - RF24(uint8_t _cepin, uint8_t _cspin); + /** + * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(uint8_t _cepin, uint8_t _cspin); - /** - * Begin operation of the chip - * - * Call this in setup(), before calling any other methods. - */ - void begin(void); + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + */ + void begin(void); - /** - * Start listening on the pipes opened for reading. - * - * Be sure to call openReadingPipe() first. Do not call write() while - * in this mode, without first calling stopListening(). Call - * isAvailable() to check for incoming traffic, and read() to get it. - */ - void startListening(void); + /** + * Start listening on the pipes opened for reading. + * + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. + */ + void startListening(void); - /** - * Stop listening for incoming messages - * - * Do this before calling write(). - */ - void stopListening(void); + /** + * Stop listening for incoming messages + * + * Do this before calling write(). + */ + void stopListening(void); - /** - * Write to the open writing pipe - * - * Be sure to call openWritingPipe() first to set the destination - * of where to write to. - * - * This blocks until the message is successfully acknowledged by - * the receiver or the timeout/retransmit maxima are reached. In - * the current configuration, the max delay here is 60ms. - * - * The maximum size of data written is the fixed payload size, see - * getPayloadSize(). However, you can write less, and the remainder - * will just be filled with zeroes. - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - boolean write( const void* buf, uint8_t len ); + /** + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + boolean write( const void* buf, uint8_t len ); - /** - * Test whether there are bytes available to be read - * - * @return True if there is a payload available, false if none is - */ - boolean available(void) ; + /** + * Test whether there are bytes available to be read + * + * @return True if there is a payload available, false if none is + */ + boolean available(void); - /** - * Read the payload - * - * Return the last payload received - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @note I specifically chose 'void*' as a data type to make it easier - * for beginners to use. No casting needed. - * - * @param buf Pointer to a buffer where the data should be written - * @param len Maximum number of bytes to read into the buffer - * @return True if the payload was delivered successfully false if not - */ - boolean read( void* buf, uint8_t len ) ; + /** + * Read the payload + * + * Return the last payload received + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer + * @return True if the payload was delivered successfully false if not + */ + boolean read( void* buf, uint8_t len ); - /** - * Open a pipe for writing - * - * Only one pipe can be open at once, but you can change the pipe - * you'll listen to. Do not call this while actively listening. - * Remember to stopListening() first. - * - * Addresses are 40-bit hex values, e.g.: - * - * @code - * openWritingPipe(0xF0F0F0F0F0); - * @endcode - * - * @param address The 40-bit address of the pipe to open. This can be - * any value whatsoever, as long as you are the only one writing to it - * and only one other radio is listening to it. Coordinate these pipe - * addresses amongst nodes on the network. - */ - void openWritingPipe(uint64_t address); + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param address The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); - /** - * Open a pipe for reading - * - * Up to 6 pipes can be open for reading at once. Open all the - * reading pipes, and then call startListening(). - * - * @see openWritingPipe - * - * @warning Pipes 1-5 should share the first 32 bits. - * Only the least significant byte should be unique, e.g. - * @code - * openReadingPipe(1,0xF0F0F0F0AA); - * openReadingPipe(2,0xF0F0F0F066); - * @endcode - * - * @warning Pipe 0 is also used by the writing pipe. So if you open - * pipe 0 for reading, and then startListening(), it will overwrite the - * writing pipe. Ergo, do an openWritingPipe() again before write(). - * - * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits - * - * @param number Which pipe# to open, 0-5. - * @param address The 40-bit address of the pipe to open. - */ - void openReadingPipe(uint8_t number, uint64_t address); + /** + * Open a pipe for reading + * + * Up to 6 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning Pipes 1-5 should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * @code + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); + * @endcode + * + * @warning Pipe 0 is also used by the writing pipe. So if you open + * pipe 0 for reading, and then startListening(), it will overwrite the + * writing pipe. Ergo, do an openWritingPipe() again before write(). + * + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits + * + * @param number Which pipe# to open, 0-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); - /**@}*/ - /** - * @name Optional public interface - * - * Methods you may want to use but are not needed for regular operation - */ - /**@{*/ + /**@}*/ + /** + * @name Optional public interface + * + * Methods you may want to use but are not needed for regular operation + */ + /**@{*/ - /** - * Set the number and delay of retries upon failed submit - * - * @param delay How long to wait between each retry, in multiples of 250us, - * max is 15. 0 means 250us, 15 means 4000us. - * @param count How many retries before giving up, max 15 - */ - void setRetries(uint8_t delay, uint8_t count); + /** + * Set the number and delay of retries upon failed submit + * + * @param delay How long to wait between each retry, in multiples of 250us, + * max is 15. 0 means 250us, 15 means 4000us. + * @param count How many retries before giving up, max 15 + */ + void setRetries(uint8_t delay, uint8_t count); - /** - * Set RF communication channel - * - * @param channel Which RF channel to communicate on, 0-127 - */ - void setChannel(uint8_t channel); + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); - /** - * Set Payload Size - * - * This implementation uses a pre-stablished fixed payload size for all - * transmissions. If this method is never called, the driver will always - * transmit the maximum payload size (32 bytes), no matter how much - * was sent to write(). - * - * @todo Implement variable-sized payloads feature - * - * @param size The number of bytes in the payload - */ - void setPayloadSize(uint8_t size); + /** + * Set Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); - /** - * Get Payload Size - * - * @see setPayloadSize() - * - * @return The number of bytes in the payload - */ - uint8_t getPayloadSize(void) ; + /** + * Get Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void); - /** - * Print a giant block of debugging information to stdout - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void printDetails(void) ; + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); - /** - * Enter low-power mode - * - * To return to normal power mode, either write() some data or - * startListening(). - */ - void powerDown(void); + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening(). + */ + void powerDown(void); - /** - * Test whether there are bytes available to be read - * - * Use this version to discover on which pipe the message - * arrived. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - boolean available(uint8_t* pipe_num); + /** + * Test whether there are bytes available to be read + * + * Use this version to discover on which pipe the message + * arrived. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + boolean available(uint8_t* pipe_num); - /** - * Non-blocking write to the open writing pipe - * - * Just like write(), but it returns immediately. To find out what happened - * to the send, catch the IRQ and then call whatHappened(). - * - * @see write() - * @see whatHappened() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - void startWrite( const void* buf, uint8_t len ); + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @see write() + * @see whatHappened() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + void startWrite( const void* buf, uint8_t len ); - /** - * Enable custom payloads on the acknowledge packets - * - * Ack payloads are a handy way to return data back to senders without - * manually changing the radio modes on both units. - * - * @see examples/pingpair_pl/pingpair_pl.pde - */ - void enableAckPayload(void); + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde + */ + void enableAckPayload(void); - /** - * Write an ack payload for the specified pipe - * - * The next time a message is received on @p pipe, the data in @p buf will - * be sent back in the acknowledgement. - * - * @warning According to the data sheet, only three of these can be pending - * at any time. I have not tested this. - * - * @param pipe Which pipe# (typically 1-5) will get this response. - * @param buf Pointer to data that is sent - * @param len Length of the data to send, up to 32 bytes max. Not affected - * by the static payload set by setPayloadSize(). - */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time. I have not tested this. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); - /** - * Determine if an ack payload was received in the most recent call to - * write(). - * - * Call read() to retrieve the ack payload. - * - * @warning Calling this function clears the internal flag which indicates - * a payload is available. If it returns true, you must read the packet - * out as the very next interaction with the radio, or the results are - * undefined. - * - * @return True if an ack payload is available. - */ - boolean isAckPayloadAvailable(void); + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @warning Calling this function clears the internal flag which indicates + * a payload is available. If it returns true, you must read the packet + * out as the very next interaction with the radio, or the results are + * undefined. + * + * @return True if an ack payload is available. + */ + boolean isAckPayloadAvailable(void); - /** - * Call this when you get an interrupt to find out why - * - * Tells you what caused the interrupt, and clears the state of - * interrupts. - * - * @param[out] tx_ok The send was successful (TX_DS) - * @param[out] tx_fail The send failed, too many retries (MAX_RT) - * @param[out] rx_ready There is a message waiting to be read (RX_DS) - */ - void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); - /** - * Enable or disable auto-acknowlede packets - * - * This is enabled by default, so it's only needed if you want to turn - * it off for some reason. - * - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck(bool enable); + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); - /** - * Test whether there was a carrier on the line for the - * previous listening period. - * - * Useful to check for interference on the current channel. - * - * @return true if was carrier, false if not - */ - boolean testCarrier(void); + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + boolean testCarrier(void); - /** - * Set the transmission data rate - * - * @param speed RF24_1MBPS for 1Mbps or RF24_2MBPS for 2Mbps - */ - void setDataRate(rf24_datarate_e speed); + /** + * Set the transmission data rate + * + * @param speed RF24_1MBPS for 1Mbps or RF24_2MBPS for 2Mbps + */ + void setDataRate(rf24_datarate_e speed); - /** - * Set the CRC length - * - * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - void setCRCLength(rf24_crclength_e length); + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); - /**@}*/ + /**@}*/ }; /** From 98fbd44111f11063b4c4cfbcf642dbaac00159d0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 07:12:58 -0700 Subject: [PATCH 103/326] Adding new example pingpair_dyn for dynamic payloads --- examples/pingpair_dyn/Jamfile | 206 ++++++++++++++++++++++++ examples/pingpair_dyn/pingpair_dyn.pde | 210 +++++++++++++++++++++++++ examples/pingpair_dyn/printf.h | 33 ++++ 3 files changed, 449 insertions(+) create mode 100644 examples/pingpair_dyn/Jamfile create mode 100644 examples/pingpair_dyn/pingpair_dyn.pde create mode 100644 examples/pingpair_dyn/printf.h diff --git a/examples/pingpair_dyn/Jamfile b/examples/pingpair_dyn/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/examples/pingpair_dyn/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde new file mode 100644 index 0000000..7a70469 --- /dev/null +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -0,0 +1,210 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example using Dynamic Payloads + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_dyn/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // optionally, use a high channel to avoid WiFi chatter + radio.setChannel(110); + + // optionally, increase the CRC length for improved reliability + radio.setCRCLength(RF24_CRC_16); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_dyn/printf.h b/examples/pingpair_dyn/printf.h new file mode 100644 index 0000000..df6c46a --- /dev/null +++ b/examples/pingpair_dyn/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From 63eb62ebc3a69f4f549532c493ce48cdfa0a8928 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 08:00:58 -0700 Subject: [PATCH 104/326] Implement dynamic payloads, with an example --- RF24.cpp | 42 +++++++++++++++----- RF24.h | 34 ++++++++++------ examples/pingpair_dyn/pingpair_dyn.pde | 54 +++++++++++++++++++------- 3 files changed, 96 insertions(+), 34 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 14cf9e5..24e5fc8 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -111,11 +111,11 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); uint8_t data_len = min(len,payload_size); - uint8_t blank_len = payload_size - data_len; + //uint8_t blank_len = payload_size - data_len; while ( data_len-- ) SPI.transfer(*current++); - while ( blank_len-- ) - SPI.transfer(0); + //while ( blank_len-- ) + // SPI.transfer(0); csn(HIGH); @@ -132,11 +132,11 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); uint8_t data_len = min(len,payload_size); - uint8_t blank_len = payload_size - data_len; + //uint8_t blank_len = payload_size - data_len; while ( data_len-- ) *current++ = SPI.transfer(0xff); - while ( blank_len-- ) - SPI.transfer(0xff); + //while ( blank_len-- ) + // SPI.transfer(0xff); csn(HIGH); return status; @@ -396,7 +396,7 @@ boolean RF24::write( const void* buf, uint8_t len ) // Handle the ack packet if ( ack_payload_available ) { - ack_payload_length = read_payload_length(); + ack_payload_length = getDynamicPayloadSize(); IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); } @@ -430,7 +430,7 @@ void RF24::startWrite( const void* buf, uint8_t len ) /******************************************************************/ -uint8_t RF24::read_payload_length(void) +uint8_t RF24::getDynamicPayloadSize(void) { uint8_t result = 0; @@ -577,6 +577,30 @@ void RF24::toggle_features(void) /******************************************************************/ +void RF24::enableDynamicPayloads(void) +{ + // Enable dynamic payload throughout the system + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); + + // If it didn't work, the features are not enabled + if ( ! read_register(FEATURE) ) + { + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); + } + + IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); + + // Enable dynamic payload on all pipes + // + // Not sure the use case of only having dynamic payload on certain + // pipes, so the library does not support it. + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/******************************************************************/ + void RF24::enableAckPayload(void) { // @@ -596,7 +620,7 @@ void RF24::enableAckPayload(void) IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); // - // Enable dynamic payload on pipe 0 + // Enable dynamic payload on pipes 0 & 1 // write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); diff --git a/RF24.h b/RF24.h index 4e7fb79..58a4a92 100644 --- a/RF24.h +++ b/RF24.h @@ -112,16 +112,6 @@ protected: */ uint8_t read_payload(void* buf, uint8_t len); - /** - * Read the payload length - * - * For dynamic payloads, this pulls the size of the payload off - * the chip - * - * @return Payload length of last-received dynamic payload - */ - uint8_t read_payload_length(void); - /** * Empty the receive buffer * @@ -352,7 +342,7 @@ public: void setChannel(uint8_t channel); /** - * Set Payload Size + * Set Static Payload Size * * This implementation uses a pre-stablished fixed payload size for all * transmissions. If this method is never called, the driver will always @@ -366,7 +356,7 @@ public: void setPayloadSize(uint8_t size); /** - * Get Payload Size + * Get Static Payload Size * * @see setPayloadSize() * @@ -374,6 +364,16 @@ public: */ uint8_t getPayloadSize(void); + /** + * Get Dynamic Payload Size + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @return Payload length of last-received dynamic payload + */ + uint8_t getDynamicPayloadSize(void); + /** * Print a giant block of debugging information to stdout * @@ -425,6 +425,16 @@ public: */ void enableAckPayload(void); + /** + * Enable dynamically-sized payloads + * + * This way you don't always have to send large packets just to send them + * once in a while. This enables dynamic payloads on ALL pipes. + * + * @see examples/pingpair_pl/pingpair_dyn.pde + */ + void enableDynamicPayloads(void); + /** * Write an ack payload for the specified pipe * diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde index 7a70469..3ee7c86 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -55,6 +55,17 @@ const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The role of the current running sketch role_e role; +// +// Payload +// + +const int min_payload_size = 4; +const int max_payload_size = 32; +const int payload_size_increments_by = 2; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + void setup(void) { // @@ -87,6 +98,9 @@ void setup(void) radio.begin(); + // enable dynamic payloads + radio.enableDynamicPayloads(); + // optionally, increase the delay between retries & # of retries radio.setRetries(15,15); @@ -137,22 +151,24 @@ void loop(void) if (role == role_ping_out) { + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + // First, stop listening so we can talk. radio.stopListening(); // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); + printf("Now sending length %i...",next_payload_size); + radio.write( send_payload, next_payload_size ); // Now, continue listening radio.startListening(); - // Wait here until we get a response, or timeout (250ms) + // Wait here until we get a response, or timeout unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) + if (millis() - started_waiting_at > 500 ) timeout = true; // Describe the results @@ -163,12 +179,20 @@ void loop(void) else { // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); + uint8_t len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + printf("Got response size=%i value=%s\n\r",len,receive_payload); } + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; // Try again 1s later delay(1000); @@ -184,22 +208,26 @@ void loop(void) if ( radio.available() ) { // Dump the payloads until we've gotten everything - unsigned long got_time; + uint8_t len; boolean done = false; while (!done) { // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); + len = radio.getDynamicPayloadSize(); + done = radio.read( receive_payload, len ); - // Spew it - printf("Got payload %lu...",got_time); + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got payload size=%i value=%s\n\r",len,receive_payload); } // First, stop listening so we can talk radio.stopListening(); // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); + radio.write( receive_payload, len ); printf("Sent response.\n\r"); // Now, resume listening so we catch the next packets. From 9566c82367a735b1db7b603880dc657352a1793a Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 08:12:12 -0700 Subject: [PATCH 105/326] Clean up read() to use new-style read_register, which reduces it to two lines of code --- RF24.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 24e5fc8..1f2858b 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -486,18 +486,11 @@ boolean RF24::available(uint8_t* pipe_num) boolean RF24::read( void* buf, uint8_t len ) { - // was this the last of the data available? - boolean result = false; - // Fetch the payload read_payload( buf, len ); - uint8_t fifo_status; - read_register(FIFO_STATUS,&fifo_status,1); - if ( fifo_status & _BV(RX_EMPTY) ) - result = true; - - return result; + // was this the last of the data available? + return read_register(FIFO_STATUS) & _BV(RX_EMPTY); } /******************************************************************/ From 774f53d6c5ff7cc635b2a5740b8044c3d9132b68 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 08:18:37 -0700 Subject: [PATCH 106/326] Reduce example payload size. Does this help reliability? --- examples/pingpair/pingpair.pde | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index c0d5480..9590b40 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -99,6 +99,10 @@ void setup(void) // optionally, increase the CRC length for improved reliability radio.setCRCLength(RF24_CRC_16); + // optionally, reduce the payload size. seems to + // improve reliability + radio.setPayloadSize(8); + // // Open pipes to other nodes for communication // From ccf32891f02f07564e7a4114cd5468572cd76103 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 08:18:41 -0700 Subject: [PATCH 107/326] comments --- RF24.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 1f2858b..958877b 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -111,9 +111,14 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); uint8_t data_len = min(len,payload_size); - //uint8_t blank_len = payload_size - data_len; while ( data_len-- ) SPI.transfer(*current++); + + // This does not seem to be needed. Keeping it here in case + // removing it does cause problems for static payloads + // + // Send blanks out to the chip to finish off the payload + //uint8_t blank_len = payload_size - data_len; //while ( blank_len-- ) // SPI.transfer(0); @@ -132,11 +137,18 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); uint8_t data_len = min(len,payload_size); - //uint8_t blank_len = payload_size - data_len; while ( data_len-- ) *current++ = SPI.transfer(0xff); + + // This does not seem to be needed. Keeping it here in case + // removing it does cause problems for static payloads + // + // Read the remaining payload off the chip, even though we will + // throw it away. + //uint8_t blank_len = payload_size - data_len; //while ( blank_len-- ) // SPI.transfer(0xff); + csn(HIGH); return status; From 42bf3e8cd2acb68ca346779b4479495563929568 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 08:22:30 -0700 Subject: [PATCH 108/326] Purge 'boolean' type, use 'bool' instead --- .gitignore | 1 + RF24.cpp | 18 +++++++++--------- RF24.h | 14 +++++++------- examples/led_remote/led_remote.pde | 2 +- examples/pingpair/pingpair.pde | 2 +- examples/pingpair_dyn/pingpair_dyn.pde | 2 +- examples/pingpair_pl/pingpair_pl.pde | 2 +- examples/pingpair_sleepy/pingpair_sleepy.pde | 2 +- examples/starping/starping.pde | 2 +- 9 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index bac8ddd..4b1d772 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.bak *.o .*.swp *.orig diff --git a/RF24.cpp b/RF24.cpp index 958877b..53cf1ee 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -364,9 +364,9 @@ void RF24::powerDown(void) /******************************************************************/ -boolean RF24::write( const void* buf, uint8_t len ) +bool RF24::write( const void* buf, uint8_t len ) { - boolean result = false; + bool result = false; // Begin the write startWrite(buf,len); @@ -456,20 +456,20 @@ uint8_t RF24::getDynamicPayloadSize(void) /******************************************************************/ -boolean RF24::available(void) +bool RF24::available(void) { return available(NULL); } /******************************************************************/ -boolean RF24::available(uint8_t* pipe_num) +bool RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); // Too noisy, enable if you really want lots o data!! IF_SERIAL_DEBUG(print_status(status)); - boolean result = ( status & _BV(RX_DR) ); + bool result = ( status & _BV(RX_DR) ); if (result) { @@ -496,7 +496,7 @@ boolean RF24::available(uint8_t* pipe_num) /******************************************************************/ -boolean RF24::read( void* buf, uint8_t len ) +bool RF24::read( void* buf, uint8_t len ) { // Fetch the payload read_payload( buf, len ); @@ -648,9 +648,9 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) /******************************************************************/ -boolean RF24::isAckPayloadAvailable(void) +bool RF24::isAckPayloadAvailable(void) { - boolean result = ack_payload_available; + bool result = ack_payload_available; ack_payload_available = false; return result; } @@ -667,7 +667,7 @@ void RF24::setAutoAck(bool enable) /******************************************************************/ -boolean RF24::testCarrier(void) +bool RF24::testCarrier(void) { return ( read_register(CD) & 1 ); } diff --git a/RF24.h b/RF24.h index 58a4a92..1f28ef8 100644 --- a/RF24.h +++ b/RF24.h @@ -24,7 +24,7 @@ private: uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ uint8_t csn_pin; /**< SPI Chip select */ uint8_t payload_size; /**< Fixed size of payloads */ - boolean ack_payload_available; /**< Whether there is an ack payload waiting */ + bool ack_payload_available; /**< Whether there is an ack payload waiting */ uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ @@ -246,14 +246,14 @@ public: * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not */ - boolean write( const void* buf, uint8_t len ); + bool write( const void* buf, uint8_t len ); /** * Test whether there are bytes available to be read * * @return True if there is a payload available, false if none is */ - boolean available(void); + bool available(void); /** * Read the payload @@ -269,7 +269,7 @@ public: * @param len Maximum number of bytes to read into the buffer * @return True if the payload was delivered successfully false if not */ - boolean read( void* buf, uint8_t len ); + bool read( void* buf, uint8_t len ); /** * Open a pipe for writing @@ -398,7 +398,7 @@ public: * @param[out] pipe_num Which pipe has the payload available * @return True if there is a payload available, false if none is */ - boolean available(uint8_t* pipe_num); + bool available(uint8_t* pipe_num); /** * Non-blocking write to the open writing pipe @@ -464,7 +464,7 @@ public: * * @return True if an ack payload is available. */ - boolean isAckPayloadAvailable(void); + bool isAckPayloadAvailable(void); /** * Call this when you get an interrupt to find out why @@ -496,7 +496,7 @@ public: * * @return true if was carrier, false if not */ - boolean testCarrier(void); + bool testCarrier(void); /** * Set the transmission data rate diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index 7550a90..45f971d 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -229,7 +229,7 @@ void loop(void) if ( radio.available() ) { // Dump the payloads until we've gotten everything - boolean done = false; + bool done = false; while (!done) { // Fetch the payload, and see if this was the last one. diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 9590b40..bd4b439 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -192,7 +192,7 @@ void loop(void) { // Dump the payloads until we've gotten everything unsigned long got_time; - boolean done = false; + bool done = false; while (!done) { // Fetch the payload, and see if this was the last one. diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde index 3ee7c86..7655505 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -209,7 +209,7 @@ void loop(void) { // Dump the payloads until we've gotten everything uint8_t len; - boolean done = false; + bool done = false; while (!done) { // Fetch the payload, and see if this was the last one. diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index da1dc36..8980293 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -160,7 +160,7 @@ void loop(void) { // Dump the payloads until we've gotten everything static unsigned long got_time; - boolean done = false; + bool done = false; while (!done) { // Fetch the payload, and see if this was the last one. diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index 7ef0db2..dea0d3c 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -227,7 +227,7 @@ void loop(void) { // Dump the payloads until we've gotten everything unsigned long got_time; - boolean done = false; + bool done = false; while (!done) { // Fetch the payload, and see if this was the last one. diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index 28470c1..ac5197a 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -244,7 +244,7 @@ void loop(void) { // Dump the payloads until we've gotten everything unsigned long got_time; - boolean done = false; + bool done = false; while (!done) { // Fetch the payload, and see if this was the last one. From 20e91ab82bdb3ba6a37ac600f6d9e1e92cecafe0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 08:26:49 -0700 Subject: [PATCH 109/326] comments --- RF24.cpp | 89 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 53cf1ee..11089e2 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -24,7 +24,7 @@ #undef PSTR #define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) -/******************************************************************/ +/****************************************************************************/ void RF24::csn(int mode) { @@ -33,14 +33,14 @@ void RF24::csn(int mode) digitalWrite(csn_pin,mode); } -/******************************************************************/ +/****************************************************************************/ void RF24::ce(int mode) { digitalWrite(ce_pin,mode); } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) { @@ -56,7 +56,7 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) return status; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::read_register(uint8_t reg) { @@ -68,7 +68,7 @@ uint8_t RF24::read_register(uint8_t reg) return result; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) { @@ -84,7 +84,7 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) return status; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::write_register(uint8_t reg, uint8_t value) { @@ -100,7 +100,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) return status; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::write_payload(const void* buf, uint8_t len) { @@ -127,7 +127,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) return status; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::read_payload(void* buf, uint8_t len) { @@ -154,7 +154,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) return status; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::flush_rx(void) { @@ -167,7 +167,7 @@ uint8_t RF24::flush_rx(void) return status; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::flush_tx(void) { @@ -180,7 +180,7 @@ uint8_t RF24::flush_tx(void) return status; } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::get_status(void) { @@ -193,7 +193,7 @@ uint8_t RF24::get_status(void) return status; } -/******************************************************************/ +/****************************************************************************/ void RF24::print_status(uint8_t status) { @@ -207,7 +207,7 @@ void RF24::print_status(uint8_t status) ); } -/******************************************************************/ +/****************************************************************************/ void RF24::print_observe_tx(uint8_t value) { @@ -218,7 +218,7 @@ void RF24::print_observe_tx(uint8_t value) ); } -/******************************************************************/ +/****************************************************************************/ void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) { @@ -229,7 +229,7 @@ void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) printf_P(PSTR("\n\r")); } -/******************************************************************/ +/****************************************************************************/ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) { @@ -250,35 +250,35 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) printf_P(PSTR("\n\r")); } -/******************************************************************/ +/****************************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) { } -/******************************************************************/ +/****************************************************************************/ void RF24::setChannel(uint8_t channel) { write_register(RF_CH,min(channel,127)); } -/******************************************************************/ +/****************************************************************************/ void RF24::setPayloadSize(uint8_t size) { payload_size = min(size,32); } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::getPayloadSize(void) { return payload_size; } -/******************************************************************/ +/****************************************************************************/ void RF24::printDetails(void) { @@ -296,7 +296,7 @@ void RF24::printDetails(void) print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); } -/******************************************************************/ +/****************************************************************************/ void RF24::begin(void) { @@ -328,7 +328,7 @@ void RF24::begin(void) setChannel(1); } -/******************************************************************/ +/****************************************************************************/ void RF24::startListening(void) { @@ -348,21 +348,21 @@ void RF24::startListening(void) delayMicroseconds(130); } -/******************************************************************/ +/****************************************************************************/ void RF24::stopListening(void) { ce(LOW); } -/******************************************************************/ +/****************************************************************************/ void RF24::powerDown(void) { write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); } -/******************************************************************/ +/****************************************************************************/ bool RF24::write( const void* buf, uint8_t len ) { @@ -423,7 +423,7 @@ bool RF24::write( const void* buf, uint8_t len ) return result; } -/******************************************************************/ +/****************************************************************************/ void RF24::startWrite( const void* buf, uint8_t len ) { @@ -440,7 +440,7 @@ void RF24::startWrite( const void* buf, uint8_t len ) ce(LOW); } -/******************************************************************/ +/****************************************************************************/ uint8_t RF24::getDynamicPayloadSize(void) { @@ -454,20 +454,21 @@ uint8_t RF24::getDynamicPayloadSize(void) return result; } -/******************************************************************/ +/****************************************************************************/ bool RF24::available(void) { return available(NULL); } -/******************************************************************/ +/****************************************************************************/ bool RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); - // Too noisy, enable if you really want lots o data!! IF_SERIAL_DEBUG(print_status(status)); + // Too noisy, enable if you really want lots o data!! + //IF_SERIAL_DEBUG(print_status(status)); bool result = ( status & _BV(RX_DR) ); @@ -494,7 +495,7 @@ bool RF24::available(uint8_t* pipe_num) return result; } -/******************************************************************/ +/****************************************************************************/ bool RF24::read( void* buf, uint8_t len ) { @@ -505,7 +506,7 @@ bool RF24::read( void* buf, uint8_t len ) return read_register(FIFO_STATUS) & _BV(RX_EMPTY); } -/******************************************************************/ +/****************************************************************************/ void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) { @@ -518,7 +519,7 @@ void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) rx_ready = status & _BV(RX_DR); } -/******************************************************************/ +/****************************************************************************/ void RF24::openWritingPipe(uint64_t value) { @@ -530,7 +531,7 @@ void RF24::openWritingPipe(uint64_t value) write_register(RX_PW_P0,min(payload_size,32)); } -/******************************************************************/ +/****************************************************************************/ void RF24::openReadingPipe(uint8_t child, uint64_t value) { @@ -570,7 +571,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) } } -/******************************************************************/ +/****************************************************************************/ void RF24::toggle_features(void) { @@ -580,7 +581,7 @@ void RF24::toggle_features(void) csn(HIGH); } -/******************************************************************/ +/****************************************************************************/ void RF24::enableDynamicPayloads(void) { @@ -604,7 +605,7 @@ void RF24::enableDynamicPayloads(void) write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); } -/******************************************************************/ +/****************************************************************************/ void RF24::enableAckPayload(void) { @@ -631,7 +632,7 @@ void RF24::enableAckPayload(void) write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); } -/******************************************************************/ +/****************************************************************************/ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) { @@ -646,7 +647,7 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) csn(HIGH); } -/******************************************************************/ +/****************************************************************************/ bool RF24::isAckPayloadAvailable(void) { @@ -655,7 +656,7 @@ bool RF24::isAckPayloadAvailable(void) return result; } -/******************************************************************/ +/****************************************************************************/ void RF24::setAutoAck(bool enable) { @@ -665,14 +666,14 @@ void RF24::setAutoAck(bool enable) write_register(EN_AA, 0); } -/******************************************************************/ +/****************************************************************************/ bool RF24::testCarrier(void) { return ( read_register(CD) & 1 ); } -/******************************************************************/ +/****************************************************************************/ void RF24::setDataRate(rf24_datarate_e speed) { @@ -683,7 +684,7 @@ void RF24::setDataRate(rf24_datarate_e speed) } -/******************************************************************/ +/****************************************************************************/ void RF24::setCRCLength(rf24_crclength_e length) { @@ -693,7 +694,7 @@ void RF24::setCRCLength(rf24_crclength_e length) write_register(CONFIG,config); } -/******************************************************************/ +/****************************************************************************/ void RF24::setRetries(uint8_t delay, uint8_t count) { From a783ed38da19d781a3863215d47a2e59a0b2754e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 10 Jul 2011 20:55:25 -0700 Subject: [PATCH 110/326] Fixed missing includes, thanks to Spoiler for reporting the problem. --- RF24.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RF24.h b/RF24.h index 1f28ef8..2c218b9 100644 --- a/RF24.h +++ b/RF24.h @@ -9,7 +9,8 @@ #ifndef __RF24_H__ #define __RF24_H__ -#include +#include +#include typedef enum { RF24_1MBPS = 0, RF24_2MBPS } rf24_datarate_e; typedef enum { RF24_CRC_8 = 0, RF24_CRC_16 } rf24_crclength_e; From 0946538f3a4df9000005ed470a1a53983bae8a4a Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 13:00:51 -0700 Subject: [PATCH 111/326] Created skeleton of new test --- tests/README | 2 + tests/pingpair_test/Jamfile | 206 ++++++++++++++++++++++++ tests/pingpair_test/pingpair_test.pde | 222 ++++++++++++++++++++++++++ tests/pingpair_test/printf.h | 33 ++++ 4 files changed, 463 insertions(+) create mode 100644 tests/README create mode 100644 tests/pingpair_test/Jamfile create mode 100644 tests/pingpair_test/pingpair_test.pde create mode 100644 tests/pingpair_test/printf.h diff --git a/tests/README b/tests/README new file mode 100644 index 0000000..02f01c8 --- /dev/null +++ b/tests/README @@ -0,0 +1,2 @@ +The sketches in this directory are intended to be checkin tests. +No code should be pushed to github without these tests passing. diff --git a/tests/pingpair_test/Jamfile b/tests/pingpair_test/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/tests/pingpair_test/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde new file mode 100644 index 0000000..e8c5396 --- /dev/null +++ b/tests/pingpair_test/pingpair_test.pde @@ -0,0 +1,222 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example of using interrupts + * + * This is an example of how to user interrupts to interact with the radio. + * It builds on the pingpair_pl example, and uses ack payloads. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const short role_pin = 7; + +// +// Topology +// + +// Single radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_sender = 1, role_receiver } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; + +// The role of the current running sketch +role_e role; + +// Interrupt handler, check the radio because we got an IRQ +void check_radio(void); + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_sender; + else + role = role_receiver; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_irq/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // We will be using the Ack Payload feature, so please enable it + radio.enableAckPayload(); + + // Optional: Increase CRC length for improved reliability + radio.setCRCLength(RF24_CRC_16); + + // Optional: Pick a high channel + radio.setChannel(110); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens a single pipe for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. + + if ( role == role_sender ) + { + radio.openWritingPipe(pipe); + } + else + { + radio.openReadingPipe(1,pipe); + } + + // + // Start listening + // + + if ( role == role_receiver ) + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Attach interrupt handler to interrupt #0 (using pin 2) + // on BOTH the sender and receiver + // + + attachInterrupt(0, check_radio, FALLING); +} + +static uint32_t message_count = 0; + +void loop(void) +{ + // + // Sender role. Repeatedly send the current time + // + + if (role == role_sender) + { + // Take the time, and send it. + unsigned long time = millis(); + printf("Now sending %lu\n\r",time); + radio.startWrite( &time, sizeof(unsigned long) ); + + // Try again soon + delay(2000); + } + + // + // Receiver role: Does nothing! All the work is in IRQ + // + +} + +void check_radio(void) +{ + // What happened? + bool tx,fail,rx; + radio.whatHappened(tx,fail,rx); + + // Have we successfully transmitted? + if ( tx ) + { + if ( role == role_sender ) + printf("Send:OK\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Sent\n\r"); + } + + // Have we failed to transmit? + if ( fail ) + { + if ( role == role_sender ) + printf("Send:Failed\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Failed\n\r"); + } + + // Transmitter can power down for now, because + // the transmission is done. + if ( ( tx || fail ) && ( role == role_sender ) ) + radio.powerDown(); + + // Did we receive a message? + if ( rx ) + { + // If we're the sender, we've received an ack payload + if ( role == role_sender ) + { + radio.read(&message_count,sizeof(message_count)); + printf("Ack:%lu\n\r",message_count); + } + + // If we're the receiver, we've received a time message + if ( role == role_receiver ) + { + // Get this payload and dump it + static unsigned long got_time; + radio.read( &got_time, sizeof(got_time) ); + printf("Got payload %lu\n\r",got_time); + + // Add an ack packet for the next time around. This is a simple + // packet counter + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); + ++message_count; + } + } +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/tests/pingpair_test/printf.h b/tests/pingpair_test/printf.h new file mode 100644 index 0000000..df6c46a --- /dev/null +++ b/tests/pingpair_test/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From 77411b4ec57839b55b0dfb25c0296e0a8c4b3f9f Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 14:12:05 -0700 Subject: [PATCH 112/326] Added working test --- tests/pingpair_test/pingpair_test.pde | 149 ++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 19 deletions(-) diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index e8c5396..5e1d90e 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -7,10 +7,15 @@ */ /** - * Example of using interrupts + * Full test on single RF pair * - * This is an example of how to user interrupts to interact with the radio. - * It builds on the pingpair_pl example, and uses ack payloads. + * This sketches uses as many RF24 methods as possible in a single test. + * + * To operate: + * Upload this sketch on two nodes, each with IRQ -> pin 2 + * One node needs pin 7 -> GND, the other NC. That's the receiving node + * Monitor the sending node's serial output + * Look for "+OK PASS" or "+OK FAIL" */ #include @@ -59,6 +64,48 @@ role_e role; // Interrupt handler, check the radio because we got an IRQ void check_radio(void); +// +// Payload +// + +const int min_payload_size = 4; +const int max_payload_size = 32; +const int payload_size_increments_by = 2; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + +// +// Test state +// + +bool done; //*< Are we done with the test? */ +bool passed; //*< Have we passed the test? */ +const int num_needed = 10; //*< How many success/failures until we're done? */ +int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ +int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ +const int interval = 100; //*< ms to wait between sends */ + +void one_ok(void) +{ + // Have we received enough yet? + if ( ! --receives_remaining ) + { + done = true; + passed = true; + } +} + +void one_failed(void) +{ + // Have we failed enough yet? + if ( ! --failures_remaining ) + { + done = true; + passed = false; + } +} + void setup(void) { // @@ -82,9 +129,16 @@ void setup(void) Serial.begin(57600); printf_begin(); - printf("\n\rRF24/examples/pingpair_irq/\n\r"); + printf("\n\rRF24/tests/pingpair_test/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); + // + // TODO: Read configuration from serial + // + // It would be a much better test if this program could accept configuration + // from the serial port. Then it would be possible to run the same test under + // lots of different circumstances. + // // Setup and configure rf radio // @@ -93,12 +147,18 @@ void setup(void) // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); + + // enable dynamic payloads + radio.enableDynamicPayloads(); // Optional: Increase CRC length for improved reliability radio.setCRCLength(RF24_CRC_16); + // Optional: Decrease data rate for improved reliability + radio.setDataRate(RF24_1MBPS); + // Optional: Pick a high channel - radio.setChannel(110); + radio.setChannel(90); // // Open pipes to other nodes for communication @@ -138,6 +198,7 @@ void setup(void) } static uint32_t message_count = 0; +static uint32_t last_message_count = 0; void loop(void) { @@ -147,19 +208,45 @@ void loop(void) if (role == role_sender) { - // Take the time, and send it. - unsigned long time = millis(); - printf("Now sending %lu\n\r",time); - radio.startWrite( &time, sizeof(unsigned long) ); + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + // First, stop listening so we can talk. + radio.stopListening(); + + // Send it. This will block until complete + printf("\n\rNow sending length %i...",next_payload_size); + radio.startWrite( send_payload, next_payload_size ); + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; + // Try again soon - delay(2000); + delay(interval); } // // Receiver role: Does nothing! All the work is in IRQ // + // + // Stop the test if we're done and report results + // + if ( done ) + { + detachInterrupt(0); + printf("\n\r+OK "); + if ( passed ) + printf("PASS\n\r"); + else + printf("FAIL\n\r"); + + // Wait here + while(1) {} + } + } void check_radio(void) @@ -172,7 +259,7 @@ void check_radio(void) if ( tx ) { if ( role == role_sender ) - printf("Send:OK\n\r"); + printf("Send:OK "); if ( role == role_receiver ) printf("Ack Payload:Sent\n\r"); @@ -182,7 +269,12 @@ void check_radio(void) if ( fail ) { if ( role == role_sender ) - printf("Send:Failed\n\r"); + { + printf("Send:Failed "); + + // log status of this line + one_failed(); + } if ( role == role_receiver ) printf("Ack Payload:Failed\n\r"); @@ -200,20 +292,39 @@ void check_radio(void) if ( role == role_sender ) { radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu\n\r",message_count); + printf("Ack:%lu ",message_count); + + // is this ack what we were expecting? to account + // for failures, we simply want to make sure we get a + // DIFFERENT ack every time. + if ( message_count != last_message_count ) + { + printf("OK "); + one_ok(); + } + else + { + printf("FAILED "); + one_failed(); + } } // If we're the receiver, we've received a time message if ( role == role_receiver ) { // Get this payload and dump it - static unsigned long got_time; - radio.read( &got_time, sizeof(got_time) ); - printf("Got payload %lu\n\r",got_time); + size_t len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); + // Spew it + printf("Got payload size=%i value=%s\n\r",len,receive_payload); + + // Add an ack packet for the next time around. + // Here we will report back how many bytes we got this time. + radio.writeAckPayload( 1, &len, sizeof(len) ); ++message_count; } } From 22b4c558cd9c5c922ca84d7781e058d53d47bc9c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 15:02:03 -0700 Subject: [PATCH 113/326] Added a test script to control the test, using python. --- tests/README | 8 ++++++++ tests/pingpair_test/pingpair_test.pde | 4 ++-- tests/pingpair_test/runtest.py | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100755 tests/pingpair_test/runtest.py diff --git a/tests/README b/tests/README index 02f01c8..5730385 100644 --- a/tests/README +++ b/tests/README @@ -1,2 +1,10 @@ The sketches in this directory are intended to be checkin tests. No code should be pushed to github without these tests passing. + +The way I run each test is from within the sketch dir, doing this: +jam p4 p6 && ./runtest.py /dev/tty.usbserial-A40081RP + +runtest.py will exit with code 0 on pass code 1 on fail + +Note that this requires python and py-serial + diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 5e1d90e..f3e9ffa 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -239,9 +239,9 @@ void loop(void) detachInterrupt(0); printf("\n\r+OK "); if ( passed ) - printf("PASS\n\r"); + printf("PASS\n\r\n\r"); else - printf("FAIL\n\r"); + printf("FAIL\n\r\n\r"); // Wait here while(1) {} diff --git a/tests/pingpair_test/runtest.py b/tests/pingpair_test/runtest.py new file mode 100755 index 0000000..8941db0 --- /dev/null +++ b/tests/pingpair_test/runtest.py @@ -0,0 +1,20 @@ +#!/opt/local/bin/python + +import sys,serial + +def read_until(token): + while 1: + line = ser.readline(None,"\r") + sys.stdout.write(line) + + if (line.startswith("+OK")): + break + return line + + +ser = serial.Serial(sys.argv[1], 57600, timeout=5) +line = read_until("+OK") +if (line.find("PASS") != -1): + sys.exit(0) +else: + sys.exit(1) From c6248157046fca8ee8a358d71eba3eb6c8a98664 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 15:28:34 -0700 Subject: [PATCH 114/326] bugfix --- tests/pingpair_test/runtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pingpair_test/runtest.py b/tests/pingpair_test/runtest.py index 8941db0..5611d2c 100755 --- a/tests/pingpair_test/runtest.py +++ b/tests/pingpair_test/runtest.py @@ -7,7 +7,7 @@ def read_until(token): line = ser.readline(None,"\r") sys.stdout.write(line) - if (line.startswith("+OK")): + if (line.startswith(token)): break return line From af37c345e87e7bbf67196bc667ee1156a0ae3af1 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 15:41:37 -0700 Subject: [PATCH 115/326] Created an all-up script runner --- tests/README | 7 ++----- tests/pingpair_test/pingpair_test.pde | 23 +++++++++++++++++------ tests/pingpair_test/runtests.sh | 8 ++++++++ 3 files changed, 27 insertions(+), 11 deletions(-) create mode 100755 tests/pingpair_test/runtests.sh diff --git a/tests/README b/tests/README index 5730385..43ceaf5 100644 --- a/tests/README +++ b/tests/README @@ -1,10 +1,7 @@ The sketches in this directory are intended to be checkin tests. No code should be pushed to github without these tests passing. -The way I run each test is from within the sketch dir, doing this: -jam p4 p6 && ./runtest.py /dev/tty.usbserial-A40081RP - -runtest.py will exit with code 0 on pass code 1 on fail +See "runtests.sh" script inside each sketch dir. This script is fully compatible with +git bisest. Note that this requires python and py-serial - diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index f3e9ffa..5840c3c 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -138,6 +138,14 @@ void setup(void) // It would be a much better test if this program could accept configuration // from the serial port. Then it would be possible to run the same test under // lots of different circumstances. + // + // The idea is that we will print "+READY" at this point. The python script + // will wait for it, and then send down a configuration script that we + // execute here and then run with. + // + // The test controller will need to configure the receiver first, then go run + // the test on the sender. + // // // Setup and configure rf radio @@ -195,6 +203,14 @@ void setup(void) // attachInterrupt(0, check_radio, FALLING); + + // + // Receiver node automatically "passes" the test + // + if ( role == role_receiver ) + { + done = passed = true; + } } static uint32_t message_count = 0; @@ -206,7 +222,7 @@ void loop(void) // Sender role. Repeatedly send the current time // - if (role == role_sender) + if (role == role_sender && !done) { // The payload will always be the same, what will change is how much of it we send. static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; @@ -236,17 +252,12 @@ void loop(void) // if ( done ) { - detachInterrupt(0); printf("\n\r+OK "); if ( passed ) printf("PASS\n\r\n\r"); else printf("FAIL\n\r\n\r"); - - // Wait here - while(1) {} } - } void check_radio(void) diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh new file mode 100755 index 0000000..1d0181d --- /dev/null +++ b/tests/pingpair_test/runtests.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Connect p6 to receiver, p4 to sender + +jam p4 p6 || exit 1 +./runtest.py /dev/tty.usbserial-A600eHIs || exit 1 +./runtest.py /dev/tty.usbserial-A40081RP || exit 1 +exit 0 From 7640852edf6e3cf139608fe70505cffa93a8c650 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 16:01:45 -0700 Subject: [PATCH 116/326] prepare tests to run with configuration parameters --- tests/pingpair_test/pingpair_test.pde | 21 +++++++++++---------- tests/pingpair_test/runtest.py | 7 ++++++- tests/pingpair_test/runtests.sh | 3 ++- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 5840c3c..39ae648 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -81,6 +81,7 @@ char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating bool done; //*< Are we done with the test? */ bool passed; //*< Have we passed the test? */ +bool notified; //*< Have we notified the user we're done? */ const int num_needed = 10; //*< How many success/failures until we're done? */ int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ @@ -147,6 +148,11 @@ void setup(void) // the test on the sender. // + printf("+READY press any key to start\n\r\n\r"); + + while (! Serial.available() ) {} + Serial.read(); + // // Setup and configure rf radio // @@ -203,16 +209,9 @@ void setup(void) // attachInterrupt(0, check_radio, FALLING); - - // - // Receiver node automatically "passes" the test - // - if ( role == role_receiver ) - { - done = passed = true; - } } + static uint32_t message_count = 0; static uint32_t last_message_count = 0; @@ -246,12 +245,14 @@ void loop(void) // // Receiver role: Does nothing! All the work is in IRQ // - + // // Stop the test if we're done and report results // - if ( done ) + if ( done && ! notified ) { + notified = true; + printf("\n\r+OK "); if ( passed ) printf("PASS\n\r\n\r"); diff --git a/tests/pingpair_test/runtest.py b/tests/pingpair_test/runtest.py index 5611d2c..3b9e0c3 100755 --- a/tests/pingpair_test/runtest.py +++ b/tests/pingpair_test/runtest.py @@ -12,8 +12,13 @@ def read_until(token): return line -ser = serial.Serial(sys.argv[1], 57600, timeout=5) +ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) + +read_until("+READY") +ser.write(" ") + line = read_until("+OK") +ser.close() if (line.find("PASS") != -1): sys.exit(0) else: diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 1d0181d..61ed042 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -3,6 +3,7 @@ # Connect p6 to receiver, p4 to sender jam p4 p6 || exit 1 -./runtest.py /dev/tty.usbserial-A600eHIs || exit 1 +./runtest.py /dev/tty.usbserial-A600eHIs & ./runtest.py /dev/tty.usbserial-A40081RP || exit 1 +kill `jobs -p` exit 0 From 1517f1249de1713d0ec26c37476cbf2bf4291b58 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 19:13:32 -0700 Subject: [PATCH 117/326] Fixed bug in setcrclength and setdatarate which was mishandling the bits --- RF24.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 11089e2..c16e034 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -677,7 +677,7 @@ bool RF24::testCarrier(void) void RF24::setDataRate(rf24_datarate_e speed) { - uint8_t setup = read_register(RF_SETUP) & _BV(RF_DR); + uint8_t setup = read_register(RF_SETUP) & ~_BV(RF_DR); if (speed == RF24_2MBPS) setup |= _BV(RF_DR); write_register(RF_SETUP,setup); @@ -688,7 +688,7 @@ void RF24::setDataRate(rf24_datarate_e speed) void RF24::setCRCLength(rf24_crclength_e length) { - uint8_t config = read_register(CONFIG) & _BV(CRCO); + uint8_t config = read_register(CONFIG) & ~_BV(CRCO); if (length == RF24_CRC_16) config |= _BV(CRCO); write_register(CONFIG,config); From c13db8f42db76a78ba79fa244271d6beba1c425c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 19:13:57 -0700 Subject: [PATCH 118/326] Added a second test configuration --- RF24.cpp | 1 + tests/pingpair_test/pingpair_test.pde | 45 ++++++++++++++++++++------- tests/pingpair_test/runtest.py | 2 +- tests/pingpair_test/runtests.sh | 7 +++-- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index c16e034..91bec36 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -293,6 +293,7 @@ void RF24::printDetails(void) print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); print_byte_register(PSTR("RF_CH"),RF_CH); print_byte_register(PSTR("RF_SETUP"),RF_SETUP); + print_byte_register(PSTR("CONFIG"),CONFIG); print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); } diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 39ae648..72048e3 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -87,6 +87,8 @@ int receives_remaining = num_needed; //*< How many ack packets until we declare int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ const int interval = 100; //*< ms to wait between sends */ +char configuration = ' '; //*< Configuration key, one char sent in by the test framework to tell us how to configure, ' ' is default */ + void one_ok(void) { // Have we received enough yet? @@ -134,7 +136,7 @@ void setup(void) printf("ROLE: %s\n\r",role_friendly_name[role]); // - // TODO: Read configuration from serial + // Read configuration from serial // // It would be a much better test if this program could accept configuration // from the serial port. Then it would be possible to run the same test under @@ -151,7 +153,8 @@ void setup(void) printf("+READY press any key to start\n\r\n\r"); while (! Serial.available() ) {} - Serial.read(); + configuration = Serial.read(); + printf("Configuration\t = %c\n\r",configuration); // // Setup and configure rf radio @@ -161,19 +164,37 @@ void setup(void) // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); - + + switch(configuration) + { + case ' ': + case '1': + // Optional: Increase CRC length for improved reliability + radio.setCRCLength(RF24_CRC_16); + + // Optional: Decrease data rate for improved reliability + radio.setDataRate(RF24_1MBPS); + + // Optional: Pick a high channel + radio.setChannel(90); + + break; + case '2': + // Optional: Increase CRC length for improved reliability + radio.setCRCLength(RF24_CRC_8); + radio.setDataRate(RF24_2MBPS); + radio.setChannel(10); + break; + default: + printf("ERROR Unknown configuration %c\n\r",configuration); + done = true; + passed = false; + break; + } + // enable dynamic payloads radio.enableDynamicPayloads(); - // Optional: Increase CRC length for improved reliability - radio.setCRCLength(RF24_CRC_16); - - // Optional: Decrease data rate for improved reliability - radio.setDataRate(RF24_1MBPS); - - // Optional: Pick a high channel - radio.setChannel(90); - // // Open pipes to other nodes for communication // diff --git a/tests/pingpair_test/runtest.py b/tests/pingpair_test/runtest.py index 3b9e0c3..45fb65c 100755 --- a/tests/pingpair_test/runtest.py +++ b/tests/pingpair_test/runtest.py @@ -15,7 +15,7 @@ def read_until(token): ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) read_until("+READY") -ser.write(" ") +ser.write(sys.argv[2]) line = read_until("+OK") ser.close() diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 61ed042..028faa8 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -3,7 +3,10 @@ # Connect p6 to receiver, p4 to sender jam p4 p6 || exit 1 -./runtest.py /dev/tty.usbserial-A600eHIs & -./runtest.py /dev/tty.usbserial-A40081RP || exit 1 +./runtest.py /dev/tty.usbserial-A600eHIs 1 & +./runtest.py /dev/tty.usbserial-A40081RP 1 || exit 1 +kill `jobs -p` +./runtest.py /dev/tty.usbserial-A600eHIs 2 & +./runtest.py /dev/tty.usbserial-A40081RP 2 || exit 1 kill `jobs -p` exit 0 From c408c5ef90928c679e4533183dbe6f19008d39e1 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 21:22:39 -0700 Subject: [PATCH 119/326] Simplify specification of test configs slightly --- tests/pingpair_test/pingpair_test.pde | 49 ++++++++++++++------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 72048e3..8105be7 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -87,7 +87,7 @@ int receives_remaining = num_needed; //*< How many ack packets until we declare int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ const int interval = 100; //*< ms to wait between sends */ -char configuration = ' '; //*< Configuration key, one char sent in by the test framework to tell us how to configure, ' ' is default */ +char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ void one_ok(void) { @@ -109,6 +109,10 @@ void one_failed(void) } } +// +// Setup +// + void setup(void) { // @@ -165,32 +169,26 @@ void setup(void) // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); - switch(configuration) + if (configuration=='2') { - case ' ': - case '1': - // Optional: Increase CRC length for improved reliability - radio.setCRCLength(RF24_CRC_16); + // Config 2 is special radio config + radio.setCRCLength(RF24_CRC_8); + radio.setDataRate(RF24_2MBPS); + radio.setChannel(10); + } + else + { + //Otherwise, default radio config + + // Optional: Increase CRC length for improved reliability + radio.setCRCLength(RF24_CRC_16); - // Optional: Decrease data rate for improved reliability - radio.setDataRate(RF24_1MBPS); + // Optional: Decrease data rate for improved reliability + radio.setDataRate(RF24_1MBPS); - // Optional: Pick a high channel - radio.setChannel(90); - - break; - case '2': - // Optional: Increase CRC length for improved reliability - radio.setCRCLength(RF24_CRC_8); - radio.setDataRate(RF24_2MBPS); - radio.setChannel(10); - break; - default: - printf("ERROR Unknown configuration %c\n\r",configuration); - done = true; - passed = false; - break; - } + // Optional: Pick a high channel + radio.setChannel(90); + } // enable dynamic payloads radio.enableDynamicPayloads(); @@ -232,6 +230,9 @@ void setup(void) attachInterrupt(0, check_radio, FALLING); } +// +// Loop +// static uint32_t message_count = 0; static uint32_t last_message_count = 0; From d436895f7c97a4c862299431bbad29d5f939c6f2 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 13 Jul 2011 21:42:16 -0700 Subject: [PATCH 120/326] Added 3rd test case, static payload --- tests/pingpair_test/pingpair_test.pde | 30 ++++++++++++++++++++------- tests/pingpair_test/runtests.sh | 3 +++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 8105be7..192811d 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -70,7 +70,7 @@ void check_radio(void); const int min_payload_size = 4; const int max_payload_size = 32; -const int payload_size_increments_by = 2; +int payload_size_increments_by = 2; int next_payload_size = min_payload_size; char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char @@ -169,9 +169,9 @@ void setup(void) // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); + // Config 2 is special radio config if (configuration=='2') { - // Config 2 is special radio config radio.setCRCLength(RF24_CRC_8); radio.setDataRate(RF24_2MBPS); radio.setChannel(10); @@ -189,9 +189,19 @@ void setup(void) // Optional: Pick a high channel radio.setChannel(90); } - - // enable dynamic payloads - radio.enableDynamicPayloads(); + + // Config 3 is static payloads only + if (configuration == '3') + { + next_payload_size = max_payload_size; + payload_size_increments_by = 0; + radio.setPayloadSize(16); + } + else + { + // enable dynamic payloads + radio.enableDynamicPayloads(); + } // // Open pipes to other nodes for communication @@ -331,7 +341,7 @@ void check_radio(void) // is this ack what we were expecting? to account // for failures, we simply want to make sure we get a // DIFFERENT ack every time. - if ( message_count != last_message_count ) + if ( ( message_count != last_message_count ) || ( configuration=='3' && message_count == 16 ) ) { printf("OK "); one_ok(); @@ -347,13 +357,19 @@ void check_radio(void) if ( role == role_receiver ) { // Get this payload and dump it - size_t len = radio.getDynamicPayloadSize(); + size_t len = max_payload_size; + memset(receive_payload,0,max_payload_size); + + if ( configuration != '3' ) + len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); // Put a zero at the end for easy printing receive_payload[len] = 0; // Spew it + len = strlen(receive_payload); // How much did we REALLY get? printf("Got payload size=%i value=%s\n\r",len,receive_payload); // Add an ack packet for the next time around. diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 028faa8..3c1e942 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -3,6 +3,9 @@ # Connect p6 to receiver, p4 to sender jam p4 p6 || exit 1 +./runtest.py /dev/tty.usbserial-A600eHIs 3 & +./runtest.py /dev/tty.usbserial-A40081RP 3 || exit 1 +kill `jobs -p` ./runtest.py /dev/tty.usbserial-A600eHIs 1 & ./runtest.py /dev/tty.usbserial-A40081RP 1 || exit 1 kill `jobs -p` From b491480dde5669590996f8b9db510d03ea57f3d6 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Sat, 16 Jul 2011 10:36:36 -0500 Subject: [PATCH 121/326] Reverted constructor changes. Changed SPI bus speed. Fixed setAutoAck for specific pipelines per suggestion. --- RF24.cpp | 22 +++++++++++----------- RF24.h | 16 ++++------------ 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 9c897ff..bd6cf34 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -35,9 +35,9 @@ void RF24::csn(const int mode) const /******************************************************************/ -void RF24::ce(const int mode) const +void RF24::ce(const int level) const { - digitalWrite(ce_pin,mode); + digitalWrite(ce_pin,level); } /******************************************************************/ @@ -208,14 +208,11 @@ void RF24::print_observe_tx(uint8_t value) const /******************************************************************/ -RF24::RF24(const uint8_t _cepin, const uint8_t _cspin, - const rf24_datarate_e speed, const uint8_t channel): +RF24::RF24(const uint8_t _cepin, const uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), payload_size(32), ack_payload_available(false) { begin() ; - setDataRate( speed ) ; - setChannel( channel ) ; } /******************************************************************/ @@ -305,13 +302,12 @@ void RF24::begin(void) pinMode(ce_pin,OUTPUT); pinMode(csn_pin,OUTPUT); + SPI.begin(); ce(LOW); csn(HIGH); - - SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV8); + SPI.setClockDivider(SPI_CLOCK_DIV2); // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet @@ -331,7 +327,7 @@ void RF24::begin(void) // Determine if this is a p or non-p RF24 module and then // reset our data rate back to default value. This works // because a non-P variant won't allow the data rate to - // be set to 250KBS. + // be set to 250Kbps. if( setDataRate( RF24_250KBPS ) ) { p_variant = true ; } @@ -636,7 +632,11 @@ void RF24::setAutoAck(const bool enable) const void RF24::setAutoAck( const uint8_t pipe, const bool enable ) const { uint8_t en_aa = read_register( EN_AA ) ; - en_aa &= ~((enable?0:1)< Date: Sat, 16 Jul 2011 11:36:10 -0500 Subject: [PATCH 122/326] Removed method argument const qualifiers. --- RF24.cpp | 32 ++++++++++++++++---------------- RF24.h | 30 +++++++++++++++--------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index bd6cf34..b7e3cbb 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -26,7 +26,7 @@ /******************************************************************/ -void RF24::csn(const int mode) const +void RF24::csn(int mode) const { SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV2); @@ -35,14 +35,14 @@ void RF24::csn(const int mode) const /******************************************************************/ -void RF24::ce(const int level) const +void RF24::ce(int level) const { digitalWrite(ce_pin,level); } /******************************************************************/ -uint8_t RF24::read_register(const uint8_t reg, uint8_t* buf, uint8_t len) const +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) const { uint8_t status; @@ -58,7 +58,7 @@ uint8_t RF24::read_register(const uint8_t reg, uint8_t* buf, uint8_t len) const /******************************************************************/ -uint8_t RF24::read_register(const uint8_t reg) const +uint8_t RF24::read_register(uint8_t reg) const { csn(LOW); SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); @@ -70,7 +70,7 @@ uint8_t RF24::read_register(const uint8_t reg) const /******************************************************************/ -uint8_t RF24::write_register(const uint8_t reg, const uint8_t* buf, uint8_t len) const +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) const { uint8_t status; @@ -86,7 +86,7 @@ uint8_t RF24::write_register(const uint8_t reg, const uint8_t* buf, uint8_t len) /******************************************************************/ -uint8_t RF24::write_register(const uint8_t reg, const uint8_t value) const +uint8_t RF24::write_register(uint8_t reg, uint8_t value) const { uint8_t status; @@ -208,7 +208,7 @@ void RF24::print_observe_tx(uint8_t value) const /******************************************************************/ -RF24::RF24(const uint8_t _cepin, const uint8_t _cspin): +RF24::RF24(uint8_t _cepin, uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), payload_size(32), ack_payload_available(false) { @@ -217,7 +217,7 @@ RF24::RF24(const uint8_t _cepin, const uint8_t _cspin): /******************************************************************/ -void RF24::setChannel(const uint8_t channel) +void RF24::setChannel(uint8_t channel) { if( wide_band ) { write_register(RF_CH,min(channel,127)); @@ -228,7 +228,7 @@ void RF24::setChannel(const uint8_t channel) /******************************************************************/ -void RF24::setPayloadSize(const uint8_t size) +void RF24::setPayloadSize(uint8_t size) { payload_size = min(size,32); } @@ -516,7 +516,7 @@ void RF24::openWritingPipe(uint64_t value) /******************************************************************/ -void RF24::openReadingPipe(const uint8_t child, const uint64_t address) +void RF24::openReadingPipe(uint8_t child, uint64_t address) { const uint8_t child_pipe[] = { RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 }; @@ -589,7 +589,7 @@ void RF24::enableAckPayload(void) const /******************************************************************/ -void RF24::writeAckPayload(const uint8_t pipe, const void* buf, uint8_t len) const +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) const { const uint8_t* current = (const uint8_t*)buf; @@ -619,7 +619,7 @@ boolean RF24::isPVariant(void) const { /******************************************************************/ -void RF24::setAutoAck(const bool enable) const +void RF24::setAutoAck(bool enable) const { if ( enable ) write_register(EN_AA, B111111); @@ -629,7 +629,7 @@ void RF24::setAutoAck(const bool enable) const /******************************************************************/ -void RF24::setAutoAck( const uint8_t pipe, const bool enable ) const +void RF24::setAutoAck( uint8_t pipe, bool enable ) const { uint8_t en_aa = read_register( EN_AA ) ; if( enable ) { @@ -656,7 +656,7 @@ boolean RF24::testRPD(void) const /******************************************************************/ -void RF24::setPALevel(const rf24_pa_dbm_e level) const +void RF24::setPALevel(rf24_pa_dbm_e level) const { uint8_t setup = read_register(RF_SETUP) ; setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; @@ -718,7 +718,7 @@ rf24_pa_dbm_e RF24::getPALevel(void) const /******************************************************************/ -boolean RF24::setDataRate(const rf24_datarate_e speed) +boolean RF24::setDataRate(rf24_datarate_e speed) { uint8_t setup = read_register(RF_SETUP) ; @@ -786,7 +786,7 @@ rf24_datarate_e RF24::getDataRate( void ) const { /******************************************************************/ -void RF24::setCRCLength(const rf24_crclength_e length) const +void RF24::setCRCLength(rf24_crclength_e length) const { uint8_t config = read_register(CONFIG) & _BV(CRCO); if (length == RF24_CRC_16) diff --git a/RF24.h b/RF24.h index 4c7d942..8542807 100644 --- a/RF24.h +++ b/RF24.h @@ -50,7 +50,7 @@ protected: * * @param mode HIGH to take this unit off the SPI bus, LOW to put it on */ - void csn(const int mode) const ; + void csn(int mode) const ; /** * Set chip enable @@ -58,7 +58,7 @@ protected: * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet * for a much more detailed description of this pin. */ - void ce(const int level) const ; + void ce(int level) const ; /** * Read a chunk of data in from a register @@ -68,7 +68,7 @@ protected: * @param len How many bytes of data to transfer * @return Current value of status register */ - uint8_t read_register(const uint8_t reg, uint8_t* buf, uint8_t len) const ; + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) const ; /** * Read single byte from a register @@ -76,7 +76,7 @@ protected: * @param reg Which register. Use constants from nRF24L01.h * @return Current value of register @p reg */ - uint8_t read_register(const uint8_t reg) const ; + uint8_t read_register(uint8_t reg) const ; /** * Write a chunk of data to a register @@ -86,7 +86,7 @@ protected: * @param len How many bytes of data to transfer * @return Current value of status register */ - uint8_t write_register(const uint8_t reg, const uint8_t* buf, uint8_t len) const ; + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len) const ; /** * Write a single byte to a register @@ -95,7 +95,7 @@ protected: * @param value The new value to write * @return Current value of status register */ - uint8_t write_register(const uint8_t reg, const uint8_t value) const ; + uint8_t write_register(uint8_t reg, uint8_t value) const ; /** * Write the transmit payload @@ -195,7 +195,7 @@ public: * @param _cspin The pin attached to Chip Select * */ - RF24(const uint8_t _cepin, const uint8_t _cspin) ; + RF24(uint8_t _cepin, uint8_t _cspin) ; /** * Begin operation of the chip @@ -304,7 +304,7 @@ public: * @param number Which pipe# to open, 0-5. * @param address The 40-bit address of the pipe to open. */ - void openReadingPipe(const uint8_t number, const uint64_t address); + void openReadingPipe(uint8_t number, uint64_t address); /**@}*/ /** @@ -335,7 +335,7 @@ public: * * @param size The number of bytes in the payload */ - void setPayloadSize(const uint8_t size); + void setPayloadSize(uint8_t size); /** * Get Payload Size @@ -396,7 +396,7 @@ public: * @param len Length of the data to send, up to 32 bytes max. Not affected * by the static payload set by setPayloadSize(). */ - void writeAckPayload(const uint8_t pipe, const void* buf, uint8_t len) const ; + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) const ; /** * Determine if an ack payload was received in the most recent call to @@ -427,7 +427,7 @@ public: * * @param enable Whether to enable (true) or disable (false) auto-acks */ - void setAutoAck(const bool enable) const ; + void setAutoAck(bool enable) const ; /** * Enable or disable auto-acknowlede packets on a per pipeline basis. @@ -438,7 +438,7 @@ public: * @param which pipeline to modify * @param enable Whether to enable (true) or disable (false) auto-acks */ - void setAutoAck( const uint8_t pipe, const bool enable ) const ; + void setAutoAck( uint8_t pipe, bool enable ) const ; /** * Test whether there was a carrier on the line for the @@ -471,7 +471,7 @@ public: * * @param Desired PA level. */ - void setPALevel( const rf24_pa_dbm_e level ) const ; + void setPALevel( rf24_pa_dbm_e level ) const ; /** * Fetches the current PA level. @@ -488,7 +488,7 @@ public: * * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps */ - boolean setDataRate(const rf24_datarate_e speed); + boolean setDataRate(rf24_datarate_e speed); /** * Set the transmission data rate @@ -504,7 +504,7 @@ public: * * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit */ - void setCRCLength(const rf24_crclength_e length) const ; + void setCRCLength(rf24_crclength_e length) const ; /**@}*/ }; From 158164746b59d32eb10a8017e1ad03b1f858df86 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Mon, 18 Jul 2011 15:37:30 -0500 Subject: [PATCH 123/326] Adds explicit CRC width and PA level. Changes hardware initialization order to minimize noisy/false message reception. Removed begin call from constructor as begin now has delay which creates timer race initialization. The delay exists to allow for power on/reset radio settling which can otherwise prevent CONFIG bits from holding. Added a method to allow CRC hardware validation to be disabled. This is a requirement for various promiscuous listening RF applications. Setting CRC width now forces CRC hardware validation enable. --- RF24.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++------------ RF24.h | 6 ++++ 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index bd6cf34..e95fa3c 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -28,8 +28,9 @@ void RF24::csn(const int mode) const { + SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); + SPI.setClockDivider(SPI_CLOCK_DIV2); digitalWrite(csn_pin,mode); } @@ -212,7 +213,6 @@ RF24::RF24(const uint8_t _cepin, const uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), payload_size(32), ack_payload_available(false) { - begin() ; } /******************************************************************/ @@ -268,6 +268,14 @@ void RF24::printDetails(void) const printf_P(PSTR("RX_ADDR_P3 = 0x%02x"),*buffer); printf_P(PSTR("\n\r")); + status = read_register(RX_ADDR_P4,buffer,1); + printf_P(PSTR("RX_ADDR_P4 = 0x%02x"),*buffer); + printf_P(PSTR("\n\r")); + + status = read_register(RX_ADDR_P5,buffer,1); + printf_P(PSTR("RX_ADDR_P5 = 0x%02x"),*buffer); + printf_P(PSTR("\n\r")); + status = read_register(TX_ADDR,buffer,5); printf_P(PSTR("TX_ADDR = 0x")); bufptr = buffer + 5; @@ -293,36 +301,52 @@ void RF24::printDetails(void) const read_register(RF_SETUP,buffer,1); printf_P(PSTR("RF_SETUP = 0x%02x (data rate: %d)\n\r"),*buffer,getDataRate()); printf_P(PSTR("Hardware; isPVariant: %d\n\r"),isPVariant()); + + read_register(CONFIG,buffer,1); + printf_P(PSTR("CONFIG = 0x%02x (CRC enable: %d; CRC16: %d)\n\r"), + *buffer,(*buffer)&_BV(EN_CRC)?1:0, + (*buffer)&_BV(CRCO)?1:0); } /******************************************************************/ void RF24::begin(void) { + // Initialize pins pinMode(ce_pin,OUTPUT); pinMode(csn_pin,OUTPUT); + // Initialize SPI bus + // Minimum ideal SPI bus speed is 2x data rate + // If we assume 2Mbs data rate and 16Mhz clock, a + // divider of 4 is the minimum we want. + // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz + // We'll use a divider of 2 which will work up to + // MCU speeds of 20Mhz. + // CLK:BUS 8Mhz:4Mhz, 16Mhz:8Mhz, or 20Mhz:10Mhz (max) SPI.begin(); - ce(LOW); - csn(HIGH); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); + SPI.setClockDivider(SPI_CLOCK_DIV2); + + ce(LOW); + csn(HIGH); + + // Must allow the radio time to settle else configuration bits will not necessarily stick. + // This is actually only required following power up but some settling time also appears to + // be required after resets too. For full coverage, we'll always assume the worst. + // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped. + // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure. + // WARNING: Delay is based on P-variant whereby non-P *may* require different timing. + delay( 5 ) ; // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet // sizes must never be used. See documentation for a more complete explanation. write_register(SETUP_RETR,(B0100 << ARD) | (B1111 << ARC)); - // Reset current status - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Initialize CRC - write_register(CONFIG, _BV(EN_CRC) ); - - // Flush buffers - flush_rx(); - flush_tx(); + // Restore our default PA level + setPALevel( RF24_PA_MAX ) ; // Determine if this is a p or non-p RF24 module and then // reset our data rate back to default value. This works @@ -332,6 +356,17 @@ void RF24::begin(void) p_variant = true ; } setDataRate( RF24_2MBPS ) ; + + // Initialize CRC and request 2-byte (16bit) CRC + setCRCLength( RF24_CRC_16 ) ; + + // Reset current status + // Notice reset and flush is the last thing we do + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Flush buffers + flush_rx(); + flush_tx(); } /******************************************************************/ @@ -788,11 +823,26 @@ rf24_datarate_e RF24::getDataRate( void ) const { void RF24::setCRCLength(const rf24_crclength_e length) const { - uint8_t config = read_register(CONFIG) & _BV(CRCO); - if (length == RF24_CRC_16) - config |= _BV(CRCO); - write_register(CONFIG,config); + uint8_t config = read_register(CONFIG) & ~_BV(CRCO) ; + + // Always make sure CRC hardware validation is actually on + config |= _BV(EN_CRC) ; + + // Now config 8 or 16 bit CRCs - only 16bit need be turned on + // 8b is the default. + if( length == RF24_CRC_16 ) { + config |= _BV( CRCO ) ; + } + + write_register( CONFIG, config ) ; } +/******************************************************************/ + +void RF24::disableCRC( void ) const +{ + uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; + write_register( CONFIG, disable ) ; +} // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 4c7d942..e50b80c 100644 --- a/RF24.h +++ b/RF24.h @@ -506,6 +506,12 @@ public: */ void setCRCLength(const rf24_crclength_e length) const ; + /** + * Disable CRC validation + * + */ + void disableCRC( void ) const ; + /**@}*/ }; From 0c2515df19d180c82303bcda074f06a239f6ff2c Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Tue, 19 Jul 2011 11:05:18 -0500 Subject: [PATCH 124/326] As a just in case I misunderstood some of the constant-ness changes, I completely took out all of the constant correctness enahcements. Also added a powerUp() method. --- RF24.cpp | 67 ++++++++++++++++++++++++++++------------------------ RF24.h | 71 +++++++++++++++++++++++++++++++------------------------- 2 files changed, 76 insertions(+), 62 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index d2073b5..7ad8056 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -26,7 +26,7 @@ /******************************************************************/ -void RF24::csn(int mode) const +void RF24::csn(int mode) { SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); @@ -36,14 +36,14 @@ void RF24::csn(int mode) const /******************************************************************/ -void RF24::ce(int level) const +void RF24::ce(int level) { digitalWrite(ce_pin,level); } /******************************************************************/ -uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) const +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) { uint8_t status; @@ -59,7 +59,7 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) const /******************************************************************/ -uint8_t RF24::read_register(uint8_t reg) const +uint8_t RF24::read_register(uint8_t reg) { csn(LOW); SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); @@ -71,7 +71,7 @@ uint8_t RF24::read_register(uint8_t reg) const /******************************************************************/ -uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) const +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) { uint8_t status; @@ -87,7 +87,7 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) const /******************************************************************/ -uint8_t RF24::write_register(uint8_t reg, uint8_t value) const +uint8_t RF24::write_register(uint8_t reg, uint8_t value) { uint8_t status; @@ -145,7 +145,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) /******************************************************************/ -uint8_t RF24::flush_rx(void) const +uint8_t RF24::flush_rx(void) { uint8_t status; @@ -158,7 +158,7 @@ uint8_t RF24::flush_rx(void) const /******************************************************************/ -uint8_t RF24::flush_tx(void) const +uint8_t RF24::flush_tx(void) { uint8_t status; @@ -171,7 +171,7 @@ uint8_t RF24::flush_tx(void) const /******************************************************************/ -uint8_t RF24::get_status(void) const +uint8_t RF24::get_status(void) { uint8_t status; @@ -184,7 +184,7 @@ uint8_t RF24::get_status(void) const /******************************************************************/ -void RF24::print_status(uint8_t status) const +void RF24::print_status(uint8_t status) { printf_P(PSTR("STATUS=%02x: RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), status, @@ -198,7 +198,7 @@ void RF24::print_status(uint8_t status) const /******************************************************************/ -void RF24::print_observe_tx(uint8_t value) const +void RF24::print_observe_tx(uint8_t value) { printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r"), value, @@ -242,7 +242,7 @@ uint8_t RF24::getPayloadSize(void) /******************************************************************/ -void RF24::printDetails(void) const +void RF24::printDetails(void) { uint8_t buffer[5]; uint8_t status = read_register(RX_ADDR_P0,buffer,5); @@ -371,7 +371,7 @@ void RF24::begin(void) /******************************************************************/ -void RF24::startListening(void) const +void RF24::startListening(void) { write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); @@ -391,20 +391,27 @@ void RF24::startListening(void) const /******************************************************************/ -void RF24::stopListening(void) const +void RF24::stopListening(void) { ce(LOW); } /******************************************************************/ -void RF24::powerDown(void) const +void RF24::powerDown(void) { write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); } /******************************************************************/ +void RF24::powerUp(void) +{ + write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); +} + +/******************************************************************/ + boolean RF24::write( const void* buf, uint8_t len ) { boolean result = false; @@ -483,14 +490,14 @@ uint8_t RF24::read_payload_length(void) /******************************************************************/ -boolean RF24::available(void) const +boolean RF24::available(void) { return available(NULL); } /******************************************************************/ -boolean RF24::available(uint8_t* pipe_num) const +boolean RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); IF_SERIAL_DEBUG(print_status(status)); @@ -587,7 +594,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) /******************************************************************/ -void RF24::toggle_features(void) const +void RF24::toggle_features(void) { csn(LOW); SPI.transfer( ACTIVATE ); @@ -597,7 +604,7 @@ void RF24::toggle_features(void) const /******************************************************************/ -void RF24::enableAckPayload(void) const +void RF24::enableAckPayload(void) { // // enable ack payload and dynamic payload features @@ -624,7 +631,7 @@ void RF24::enableAckPayload(void) const /******************************************************************/ -void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) const +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) { const uint8_t* current = (const uint8_t*)buf; @@ -648,13 +655,13 @@ boolean RF24::isAckPayloadAvailable(void) /******************************************************************/ -boolean RF24::isPVariant(void) const { +boolean RF24::isPVariant(void) { return p_variant ; } /******************************************************************/ -void RF24::setAutoAck(bool enable) const +void RF24::setAutoAck(bool enable) { if ( enable ) write_register(EN_AA, B111111); @@ -664,7 +671,7 @@ void RF24::setAutoAck(bool enable) const /******************************************************************/ -void RF24::setAutoAck( uint8_t pipe, bool enable ) const +void RF24::setAutoAck( uint8_t pipe, bool enable ) { uint8_t en_aa = read_register( EN_AA ) ; if( enable ) { @@ -677,21 +684,21 @@ void RF24::setAutoAck( uint8_t pipe, bool enable ) const /******************************************************************/ -boolean RF24::testCarrier(void) const +boolean RF24::testCarrier(void) { return ( read_register(CD) & 1 ); } /******************************************************************/ -boolean RF24::testRPD(void) const +boolean RF24::testRPD(void) { return ( read_register(RPD) & 1 ) ; } /******************************************************************/ -void RF24::setPALevel(rf24_pa_dbm_e level) const +void RF24::setPALevel(rf24_pa_dbm_e level) { uint8_t setup = read_register(RF_SETUP) ; setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; @@ -724,7 +731,7 @@ void RF24::setPALevel(rf24_pa_dbm_e level) const /******************************************************************/ -rf24_pa_dbm_e RF24::getPALevel(void) const +rf24_pa_dbm_e RF24::getPALevel(void) { rf24_pa_dbm_e result = RF24_PA_ERROR ; uint8_t power = read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; @@ -794,7 +801,7 @@ boolean RF24::setDataRate(rf24_datarate_e speed) /******************************************************************/ -rf24_datarate_e RF24::getDataRate( void ) const { +rf24_datarate_e RF24::getDataRate( void ) { rf24_datarate_e result ; uint8_t setup = read_register(RF_SETUP) ; @@ -821,7 +828,7 @@ rf24_datarate_e RF24::getDataRate( void ) const { /******************************************************************/ -void RF24::setCRCLength(rf24_crclength_e length) const +void RF24::setCRCLength(rf24_crclength_e length) { uint8_t config = read_register(CONFIG) & ~_BV(CRCO) ; @@ -839,7 +846,7 @@ void RF24::setCRCLength(rf24_crclength_e length) const /******************************************************************/ -void RF24::disableCRC( void ) const +void RF24::disableCRC( void ) { uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; write_register( CONFIG, disable ) ; diff --git a/RF24.h b/RF24.h index e4cf87c..81f10ba 100644 --- a/RF24.h +++ b/RF24.h @@ -50,7 +50,7 @@ protected: * * @param mode HIGH to take this unit off the SPI bus, LOW to put it on */ - void csn(int mode) const ; + void csn(int mode) ; /** * Set chip enable @@ -58,7 +58,7 @@ protected: * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet * for a much more detailed description of this pin. */ - void ce(int level) const ; + void ce(int level) ; /** * Read a chunk of data in from a register @@ -68,7 +68,7 @@ protected: * @param len How many bytes of data to transfer * @return Current value of status register */ - uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) const ; + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; /** * Read single byte from a register @@ -76,7 +76,7 @@ protected: * @param reg Which register. Use constants from nRF24L01.h * @return Current value of register @p reg */ - uint8_t read_register(uint8_t reg) const ; + uint8_t read_register(uint8_t reg) ; /** * Write a chunk of data to a register @@ -86,7 +86,7 @@ protected: * @param len How many bytes of data to transfer * @return Current value of status register */ - uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len) const ; + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len) ; /** * Write a single byte to a register @@ -95,7 +95,7 @@ protected: * @param value The new value to write * @return Current value of status register */ - uint8_t write_register(uint8_t reg, uint8_t value) const ; + uint8_t write_register(uint8_t reg, uint8_t value) ; /** * Write the transmit payload @@ -134,21 +134,21 @@ protected: * * @return Current value of status register */ - uint8_t flush_rx(void) const ; + uint8_t flush_rx(void) ; /** * Empty the transmit buffer * * @return Current value of status register */ - uint8_t flush_tx(void) const ; + uint8_t flush_tx(void) ; /** * Retrieve the current status of the chip * * @return Current value of status register */ - uint8_t get_status(void) const ; + uint8_t get_status(void) ; /** * Decode and print the given status to stdout @@ -157,7 +157,7 @@ protected: * * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h */ - void print_status(uint8_t status) const ; + void print_status(uint8_t status) ; /** * Decode and print the given 'observe_tx' value to stdout @@ -166,7 +166,7 @@ protected: * * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h */ - void print_observe_tx(uint8_t value) const ; + void print_observe_tx(uint8_t value) ; /** * Turn on or off the special features of the chip @@ -174,7 +174,7 @@ protected: * The chip has certain 'features' which are only available when the 'features' * are enabled. See the datasheet for details. */ - void toggle_features(void) const ; + void toggle_features(void) ; /**@}*/ public: @@ -211,14 +211,14 @@ public: * in this mode, without first calling stopListening(). Call * isAvailable() to check for incoming traffic, and read() to get it. */ - void startListening(void) const ; + void startListening(void) ; /** * Stop listening for incoming messages * * Do this before calling write(). */ - void stopListening(void) const ; + void stopListening(void) ; /** * Write to the open writing pipe @@ -245,7 +245,7 @@ public: * * @return True if there is a payload available, false if none is */ - boolean available(void) const ; + boolean available(void) ; /** * Read the payload @@ -351,15 +351,22 @@ public: * * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h */ - void printDetails(void) const ; + void printDetails(void) ; /** * Enter low-power mode * - * To return to normal power mode, either write() some data or - * startListening(). + * To return to normal power mode, either write() some data, + * startListening(), or powerUp(). */ - void powerDown(void) const ; + void powerDown(void) ; + + /** + * Leave low-power mode - making radio more responsive + * + * To return to low power mode, call powerDown(). + */ + void powerUp(void) ; /** * Test whether there are bytes available to be read @@ -370,7 +377,7 @@ public: * @param[out] pipe_num Which pipe has the payload available * @return True if there is a payload available, false if none is */ - boolean available(uint8_t* pipe_num) const ; + boolean available(uint8_t* pipe_num) ; /** * Enable custom payloads on the acknowledge packets @@ -380,7 +387,7 @@ public: * * @see examples/pingpair_pl/pingpair_pl.pde */ - void enableAckPayload(void) const; + void enableAckPayload(void) ; /** * Write an ack payload for the specified pipe @@ -396,7 +403,7 @@ public: * @param len Length of the data to send, up to 32 bytes max. Not affected * by the static payload set by setPayloadSize(). */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) const ; + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) ; /** * Determine if an ack payload was received in the most recent call to @@ -417,7 +424,7 @@ public: * @return Returns true if the hardware is RF24L01P (or compatible) and false * if its not. */ - boolean isPVariant(void) const ; + boolean isPVariant(void) ; /** * Enable or disable auto-acknowlede packets @@ -427,7 +434,7 @@ public: * * @param enable Whether to enable (true) or disable (false) auto-acks */ - void setAutoAck(bool enable) const ; + void setAutoAck(bool enable) ; /** * Enable or disable auto-acknowlede packets on a per pipeline basis. @@ -438,7 +445,7 @@ public: * @param which pipeline to modify * @param enable Whether to enable (true) or disable (false) auto-acks */ - void setAutoAck( uint8_t pipe, bool enable ) const ; + void setAutoAck( uint8_t pipe, bool enable ) ; /** * Test whether there was a carrier on the line for the @@ -448,7 +455,7 @@ public: * * @return true if was carrier, false if not */ - boolean testCarrier(void) const ; + boolean testCarrier(void) ; /** * Test whether a signal (carrier or otherwise) greater than @@ -460,7 +467,7 @@ public: * * @return true if signal => -64dBm, false if not */ - boolean testRPD(void) const ; + boolean testRPD(void) ; /** * Set Power Amplifier (PA) level to one of four levels. @@ -471,7 +478,7 @@ public: * * @param Desired PA level. */ - void setPALevel( rf24_pa_dbm_e level ) const ; + void setPALevel( rf24_pa_dbm_e level ) ; /** * Fetches the current PA level. @@ -481,7 +488,7 @@ public: * by the enum mnemonics are negative dBm. See setPALevel for * return value descriptions. */ - rf24_pa_dbm_e getPALevel( void ) const ; + rf24_pa_dbm_e getPALevel( void ) ; /** * Set the transmission data rate @@ -497,20 +504,20 @@ public: * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the * rf24_datarate_e enum. */ - rf24_datarate_e getDataRate( void ) const ; + rf24_datarate_e getDataRate( void ) ; /** * Set the CRC length * * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit */ - void setCRCLength(rf24_crclength_e length) const ; + void setCRCLength(rf24_crclength_e length) ; /** * Disable CRC validation * */ - void disableCRC( void ) const ; + void disableCRC( void ) ; /**@}*/ }; From 14c34ca2e4990c8df6e6c605150f1c6c1e323cd6 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 31 Jul 2011 09:47:41 -0700 Subject: [PATCH 125/326] astyle -A1 -s2 --- RF24.cpp | 125 +++++++++++++++++++++++++++++-------------------------- RF24.h | 4 +- 2 files changed, 69 insertions(+), 60 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 3dc400c..f491f18 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -30,7 +30,7 @@ void RF24::csn(int mode) { SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); + SPI.setClockDivider(SPI_CLOCK_DIV2); digitalWrite(csn_pin,mode); } @@ -118,7 +118,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) // This does not seem to be needed. Keeping it here in case // removing it does cause problems for static payloads // - // Send blanks out to the chip to finish off the payload + // Send blanks out to the chip to finish off the payload //uint8_t blank_len = payload_size - data_len; //while ( blank_len-- ) // SPI.transfer(0); @@ -140,7 +140,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) uint8_t data_len = min(len,payload_size); while ( data_len-- ) *current++ = SPI.transfer(0xff); - + // This does not seem to be needed. Keeping it here in case // removing it does cause problems for static payloads // @@ -149,7 +149,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) //uint8_t blank_len = payload_size - data_len; //while ( blank_len-- ) // SPI.transfer(0xff); - + csn(HIGH); return status; @@ -253,9 +253,9 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) /****************************************************************************/ -RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), - payload_size(32), ack_payload_available(false) +RF24::RF24(uint8_t _cepin, uint8_t _cspin): + ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), + payload_size(32), ack_payload_available(false) { } @@ -298,16 +298,16 @@ void RF24::printDetails(void) print_byte_register(PSTR("CONFIG"),CONFIG); print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); - // These need to be merged in with the register printing scheme + // These need to be merged in with the register printing scheme #if 0 read_register(RF_SETUP,buffer,1); - printf_P(PSTR("RF_SETUP = 0x%02x (data rate: %d)\n\r"),*buffer,getDataRate()); + printf_P(PSTR("RF_SETUP = 0x%02x (data rate: %d)\n\r"),*buffer,getDataRate()); printf_P(PSTR("Hardware; isPVariant: %d\n\r"),isPVariant()); read_register(CONFIG,buffer,1); printf_P(PSTR("CONFIG = 0x%02x (CRC enable: %d; CRC16: %d)\n\r"), - *buffer,(*buffer)&_BV(EN_CRC)?1:0, - (*buffer)&_BV(CRCO)?1:0); + *buffer,(*buffer)&_BV(EN_CRC)?1:0, + (*buffer)&_BV(CRCO)?1:0); #endif } @@ -330,7 +330,7 @@ void RF24::begin(void) SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); + SPI.setClockDivider(SPI_CLOCK_DIV2); ce(LOW); csn(HIGH); @@ -355,8 +355,9 @@ void RF24::begin(void) // reset our data rate back to default value. This works // because a non-P variant won't allow the data rate to // be set to 250Kbps. - if( setDataRate( RF24_250KBPS ) ) { - p_variant = true ; + if( setDataRate( RF24_250KBPS ) ) + { + p_variant = true ; } setDataRate( RF24_2MBPS ) ; @@ -369,7 +370,7 @@ void RF24::begin(void) // Set up default configuration. Callers can always change it later. setChannel(1); - + // Flush buffers flush_rx(); flush_tx(); @@ -524,7 +525,7 @@ bool RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); - // Too noisy, enable if you really want lots o data!! + // Too noisy, enable if you really want lots o data!! //IF_SERIAL_DEBUG(print_status(status)); bool result = ( status & _BV(RX_DR) ); @@ -581,8 +582,8 @@ void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) void RF24::openWritingPipe(uint64_t value) { // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) - // expects it LSB first too, so we're good. - + // expects it LSB first too, so we're good. + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); write_register(TX_ADDR, reinterpret_cast(&value), 5); write_register(RX_PW_P0,min(payload_size,32)); @@ -715,8 +716,9 @@ bool RF24::isAckPayloadAvailable(void) /****************************************************************************/ -boolean RF24::isPVariant(void) { - return p_variant ; +boolean RF24::isPVariant(void) +{ + return p_variant ; } /******************************************************************/ @@ -736,10 +738,13 @@ void RF24::setAutoAck( uint8_t pipe, bool enable ) if ( pipe <= 6 ) { uint8_t en_aa = read_register( EN_AA ) ; - if( enable ) { - en_aa |= _BV(pipe) ; - } else { - en_aa &= ~_BV(pipe) ; + if( enable ) + { + en_aa |= _BV(pipe) ; + } + else + { + en_aa &= ~_BV(pipe) ; } write_register( EN_AA, en_aa ) ; } @@ -769,24 +774,24 @@ void RF24::setPALevel(rf24_pa_dbm_e level) switch( level ) { case RF24_PA_MAX: - setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - break ; + setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; + break ; case RF24_PA_HIGH: - setup |= _BV(RF_PWR_HIGH) ; - break ; + setup |= _BV(RF_PWR_HIGH) ; + break ; case RF24_PA_LOW: - setup |= _BV(RF_PWR_LOW) ; - break ; + setup |= _BV(RF_PWR_LOW) ; + break ; case RF24_PA_MIN: - break ; + break ; case RF24_PA_ERROR: - // On error, go to maximum PA - setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - break ; + // On error, go to maximum PA + setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; + break ; } write_register( RF_SETUP, setup ) ; @@ -802,20 +807,20 @@ rf24_pa_dbm_e RF24::getPALevel(void) switch( power ) { case (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)): - result = RF24_PA_MAX ; - break ; + result = RF24_PA_MAX ; + break ; case _BV(RF_PWR_HIGH): - result = RF24_PA_HIGH ; - break ; + result = RF24_PA_HIGH ; + break ; case _BV(RF_PWR_LOW): - result = RF24_PA_LOW ; - break ; + result = RF24_PA_LOW ; + break ; default: - result = RF24_PA_MIN ; - break ; + result = RF24_PA_MIN ; + break ; } return result ; @@ -845,8 +850,8 @@ boolean RF24::setDataRate(rf24_datarate_e speed) { wide_band = true ; setup |= _BV(RF_DR_HIGH); - } - else + } + else { // 1Mbs wide_band = false ; @@ -856,8 +861,9 @@ boolean RF24::setDataRate(rf24_datarate_e speed) // Verify our result setup = read_register(RF_SETUP) ; - if( setup == setup ) { - return true ; + if( setup == setup ) + { + return true ; } wide_band = false ; @@ -866,26 +872,28 @@ boolean RF24::setDataRate(rf24_datarate_e speed) /******************************************************************/ -rf24_datarate_e RF24::getDataRate( void ) { +rf24_datarate_e RF24::getDataRate( void ) +{ rf24_datarate_e result ; uint8_t setup = read_register(RF_SETUP) ; // Order matters in our case below - switch( setup & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ) { + switch( setup & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ) + { case _BV(RF_DR_LOW): - // '10' = 250KBPS - result = RF24_250KBPS ; - break ; + // '10' = 250KBPS + result = RF24_250KBPS ; + break ; case _BV(RF_DR_HIGH): - // '01' = 2MBPS - result = RF24_2MBPS ; - break ; + // '01' = 2MBPS + result = RF24_2MBPS ; + break ; default: - // '00' = 1MBPS - result = RF24_1MBPS ; - break ; + // '00' = 1MBPS + result = RF24_1MBPS ; + break ; } return result ; @@ -902,7 +910,8 @@ void RF24::setCRCLength(rf24_crclength_e length) // Now config 8 or 16 bit CRCs - only 16bit need be turned on // 8b is the default. - if( length == RF24_CRC_16 ) { + if( length == RF24_CRC_16 ) + { config |= _BV( CRCO ) ; } diff --git a/RF24.h b/RF24.h index 4b9ca76..d93bb8b 100644 --- a/RF24.h +++ b/RF24.h @@ -374,7 +374,7 @@ public: uint8_t getPayloadSize(void); /** - * Get Dynamic Payload Size + * Get Dynamic Payload Size * * For dynamic payloads, this pulls the size of the payload off * the chip @@ -557,7 +557,7 @@ public: void setPALevel( rf24_pa_dbm_e level ) ; /** - * Fetches the current PA level. + * Fetches the current PA level. * * @return Returns a value from the rf24_pa_dbm_e enum describing * the current PA setting. Please remember, all values represented From 956b63a4dce35afb73bdd4847f20512fbbb38ea5 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 31 Jul 2011 10:00:09 -0700 Subject: [PATCH 126/326] sed s/boolean/bool/g --- RF24.cpp | 11 ++++++----- RF24.h | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index f491f18..5efc4ea 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -419,7 +419,7 @@ void RF24::powerUp(void) /******************************************************************/ -boolean RF24::write( const void* buf, uint8_t len ) +bool RF24::write( const void* buf, uint8_t len ) { bool result = false; @@ -716,7 +716,7 @@ bool RF24::isAckPayloadAvailable(void) /****************************************************************************/ -boolean RF24::isPVariant(void) +bool RF24::isPVariant(void) { return p_variant ; } @@ -752,14 +752,14 @@ void RF24::setAutoAck( uint8_t pipe, bool enable ) /******************************************************************/ -boolean RF24::testCarrier(void) +bool RF24::testCarrier(void) { return ( read_register(CD) & 1 ); } /****************************************************************************/ -boolean RF24::testRPD(void) +bool RF24::testRPD(void) { return ( read_register(RPD) & 1 ) ; } @@ -828,8 +828,9 @@ rf24_pa_dbm_e RF24::getPALevel(void) /******************************************************************/ -boolean RF24::setDataRate(rf24_datarate_e speed) +bool RF24::setDataRate(rf24_datarate_e speed) { + bool result = false; uint8_t setup = read_register(RF_SETUP) ; // HIGH and LOW '00' is 1Mbs - our default diff --git a/RF24.h b/RF24.h index d93bb8b..7f69bb4 100644 --- a/RF24.h +++ b/RF24.h @@ -488,7 +488,7 @@ public: * @return true if the hardware is nRF24L01+ (or compatible) and false * if its not. */ - boolean isPVariant(void) ; + bool isPVariant(void) ; /** * Call this when you get an interrupt to find out why @@ -543,7 +543,7 @@ public: * * @return true if signal => -64dBm, false if not */ - boolean testRPD(void) ; + bool testRPD(void) ; /** * Set Power Amplifier (PA) level to one of four levels. From cd3b0b3ccbb55c09d9b4b7beb8ca001b86cf6f41 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 31 Jul 2011 10:00:21 -0700 Subject: [PATCH 127/326] Merge cleanups --- RF24.cpp | 16 +++++++++------- RF24.h | 14 +++++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 5efc4ea..dc3e2a5 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -616,9 +616,9 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) { // For pipes 2-5, only write the LSB if ( child < 2 ) - write_register(child_pipe[child], reinterpret_cast(&value), 5); + write_register(child_pipe[child], reinterpret_cast(&address), 5); else - write_register(child_pipe[child], reinterpret_cast(&value), 1); + write_register(child_pipe[child], reinterpret_cast(&address), 1); write_register(child_payload_size[child],payload_size); @@ -861,14 +861,16 @@ bool RF24::setDataRate(rf24_datarate_e speed) write_register(RF_SETUP,setup); // Verify our result - setup = read_register(RF_SETUP) ; - if( setup == setup ) + if ( read_register(RF_SETUP) == setup ) { - return true ; + result = true; + } + else + { + wide_band = false; } - wide_band = false ; - return false ; + return result; } /******************************************************************/ diff --git a/RF24.h b/RF24.h index 7f69bb4..bfd5b13 100644 --- a/RF24.h +++ b/RF24.h @@ -569,9 +569,21 @@ public: /** * Set the transmission data rate * + * @warning setting RF24_250KBPS will fail for non-P units + * * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps + * @return true if the change was successful */ - void setDataRate(rf24_datarate_e speed); + bool setDataRate(rf24_datarate_e speed); + + /** + * Fetches the transmission data rate + * + * @return Returns the hardware's currently configured datarate. The value + * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the + * rf24_datarate_e enum. + */ + rf24_datarate_e getDataRate( void ) ; /** * Set the CRC length From 77a798b1a91d213b0fb65734afc5a89cda24ce70 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 31 Jul 2011 10:17:07 -0700 Subject: [PATCH 128/326] Changed test to use new 250kbps mode --- tests/pingpair_test/pingpair_test.pde | 2 +- tests/pingpair_test/runtests.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 192811d..341242d 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -173,7 +173,7 @@ void setup(void) if (configuration=='2') { radio.setCRCLength(RF24_CRC_8); - radio.setDataRate(RF24_2MBPS); + radio.setDataRate(RF24_250KBPS); radio.setChannel(10); } else diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 3c1e942..c68a0a2 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -1,6 +1,7 @@ #!/bin/sh # Connect p6 to receiver, p4 to sender +# WARNING: Test config 2 only works with PLUS units. jam p4 p6 || exit 1 ./runtest.py /dev/tty.usbserial-A600eHIs 3 & From 18ebe7a0aac86bf90aa08e0fd8d48d6929651383 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 1 Aug 2011 09:45:55 -0700 Subject: [PATCH 129/326] Treat warning as errors in tests --- tests/pingpair_test/Jamfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pingpair_test/Jamfile b/tests/pingpair_test/Jamfile index 901f8da..98ec12f 100644 --- a/tests/pingpair_test/Jamfile +++ b/tests/pingpair_test/Jamfile @@ -40,7 +40,7 @@ AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; CTUNING = -ffunction-sections -fdata-sections ; CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CFLAGS = -Os -Werror -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; From fcc3dfbedb2e7047cd1f7f5931bea553c2ef1980 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 1 Aug 2011 09:46:13 -0700 Subject: [PATCH 130/326] Fix up some doc bugs --- RF24.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RF24.h b/RF24.h index bfd5b13..7e25b9b 100644 --- a/RF24.h +++ b/RF24.h @@ -518,7 +518,7 @@ public: * AA is enabled by default, so it's only needed if you want to turn * it off/on for some reason on a per pipeline basis. * - * @param which pipeline to modify + * @param pipe Which pipeline to modify * @param enable Whether to enable (true) or disable (false) auto-acks */ void setAutoAck( uint8_t pipe, bool enable ) ; @@ -552,7 +552,7 @@ public: * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. * - * @param Desired PA level. + * @param level Desired PA level. */ void setPALevel( rf24_pa_dbm_e level ) ; From c9d64fd0888d7f7610c8bded105a787086fda62e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 1 Aug 2011 09:52:35 -0700 Subject: [PATCH 131/326] More doc tweaks --- RF24.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RF24.h b/RF24.h index 7e25b9b..b0993cc 100644 --- a/RF24.h +++ b/RF24.h @@ -394,7 +394,7 @@ public: * Enter low-power mode * * To return to normal power mode, either write() some data or - * startListening(). + * startListening, or powerUp(). */ void powerDown(void); @@ -569,7 +569,7 @@ public: /** * Set the transmission data rate * - * @warning setting RF24_250KBPS will fail for non-P units + * @warning setting RF24_250KBPS will fail for non-plus units * * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps * @return true if the change was successful From 7b401d7281d783b4a156ad4b9c4a41a18aec9fd0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 1 Aug 2011 21:05:54 -0700 Subject: [PATCH 132/326] Improve pingpair timing. Should lead to less timeouts. --- examples/pingpair/pingpair.pde | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index bd4b439..d9f2b7e 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -159,7 +159,7 @@ void loop(void) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) + if (millis() - started_waiting_at > 200 ) timeout = true; // Describe the results @@ -200,6 +200,10 @@ void loop(void) // Spew it printf("Got payload %lu...",got_time); + + // Delay just a little bit to let the other unit + // make the transition to receiver + delay(20); } // First, stop listening so we can talk From df328408f79962adaed44194fef99e4c7426f41c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 15:24:19 -0700 Subject: [PATCH 133/326] Fix bugs. Ack payload available was broken since interrupts went in. stREtries was missing since the gcopeland merge. --- RF24.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index dc3e2a5..750f82b 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -457,7 +457,7 @@ bool RF24::write( const void* buf, uint8_t len ) // * The send was successful (TX_DS) // * The send failed, too many retries (MAX_RT) // * There is an ack packet waiting (RX_DR) - bool tx_ok, tx_fail, ack_payload_available; + bool tx_ok, tx_fail; whatHappened(tx_ok,tx_fail,ack_payload_available); result = tx_ok; @@ -928,5 +928,12 @@ void RF24::disableCRC( void ) uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; write_register( CONFIG, disable ) ; } + +/****************************************************************************/ +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR,(delay&0xf)< Date: Tue, 2 Aug 2011 15:24:46 -0700 Subject: [PATCH 134/326] Experimental changes to help in debugging --- RF24.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index 750f82b..762e402 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -459,6 +459,7 @@ bool RF24::write( const void* buf, uint8_t len ) // * There is an ack packet waiting (RX_DR) bool tx_ok, tx_fail; whatHappened(tx_ok,tx_fail,ack_payload_available); + printf("%u%u%u\n\r",tx_ok,tx_fail,ack_payload_available); result = tx_ok; IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); @@ -495,6 +496,7 @@ void RF24::startWrite( const void* buf, uint8_t len ) // Allons! ce(HIGH); delayMicroseconds(15); + delay(2); ce(LOW); } @@ -569,6 +571,7 @@ bool RF24::read( void* buf, uint8_t len ) void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) { // Read the status & reset the status in one easy call + // Or is that such a good idea? uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Report to the user what happened From af00a6155596b7728884a2759d11a02b9e54901e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 15:51:51 -0700 Subject: [PATCH 135/326] Fix bug in which static payloads were broken --- RF24.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 24e5fc8..20d5602 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -111,11 +111,11 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); uint8_t data_len = min(len,payload_size); - //uint8_t blank_len = payload_size - data_len; + uint8_t blank_len = payload_size - data_len; while ( data_len-- ) SPI.transfer(*current++); - //while ( blank_len-- ) - // SPI.transfer(0); + while ( blank_len-- ) + SPI.transfer(0); csn(HIGH); @@ -132,11 +132,11 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); uint8_t data_len = min(len,payload_size); - //uint8_t blank_len = payload_size - data_len; + uint8_t blank_len = payload_size - data_len; while ( data_len-- ) *current++ = SPI.transfer(0xff); - //while ( blank_len-- ) - // SPI.transfer(0xff); + while ( blank_len-- ) + SPI.transfer(0xff); csn(HIGH); return status; From a31cafcc887f1809fb63ac23ea3881e1bcda3e17 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 16:30:43 -0700 Subject: [PATCH 136/326] Static and dynamic payloads now fully fixed and interoperable --- RF24.cpp | 22 +++++++++++++++++----- RF24.h | 3 ++- tests/pingpair_test/pingpair_test.pde | 12 +++++++----- tests/pingpair_test/runtests.sh | 10 +++++----- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 8b5933b..d92c970 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -108,10 +108,13 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) const uint8_t* current = reinterpret_cast(buf); + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + //printf("[Writing %u bytes %u blanks]",data_len,blank_len); + csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); - uint8_t data_len = min(len,payload_size); - uint8_t blank_len = payload_size - data_len; while ( data_len-- ) SPI.transfer(*current++); while ( blank_len-- ) @@ -129,10 +132,13 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) uint8_t status; uint8_t* current = reinterpret_cast(buf); + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + //printf("[Reading %u bytes %u blanks]",data_len,blank_len); + csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); - uint8_t data_len = min(len,payload_size); - uint8_t blank_len = payload_size - data_len; while ( data_len-- ) *current++ = SPI.transfer(0xff); while ( blank_len-- ) @@ -241,7 +247,8 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) /****************************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false) + ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_payload_available(false), + dynamic_payloads_enabled(false) { } @@ -300,6 +307,9 @@ void RF24::begin(void) SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV8); + // Disable dynamic payloads, to match dynamic_payloads_enabled setting + write_register(DYNPD,0); + // Set generous timeouts, to make testing a little easier write_register(SETUP_RETR,(B1111 << ARD) | (B1111 << ARC)); @@ -592,6 +602,8 @@ void RF24::enableDynamicPayloads(void) // Not sure the use case of only having dynamic payload on certain // pipes, so the library does not support it. write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + + dynamic_payloads_enabled = true; } /****************************************************************************/ diff --git a/RF24.h b/RF24.h index 2c218b9..d6619c2 100644 --- a/RF24.h +++ b/RF24.h @@ -26,7 +26,8 @@ private: uint8_t csn_pin; /**< SPI Chip select */ uint8_t payload_size; /**< Fixed size of payloads */ bool ack_payload_available; /**< Whether there is an ack payload waiting */ - uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. Note: not used. */ + bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ + uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ protected: diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 192811d..0e36b25 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -193,9 +193,9 @@ void setup(void) // Config 3 is static payloads only if (configuration == '3') { - next_payload_size = max_payload_size; + next_payload_size = 16; payload_size_increments_by = 0; - radio.setPayloadSize(16); + radio.setPayloadSize(next_payload_size); } else { @@ -351,6 +351,7 @@ void check_radio(void) printf("FAILED "); one_failed(); } + last_message_count = message_count; } // If we're the receiver, we've received a time message @@ -360,7 +361,9 @@ void check_radio(void) size_t len = max_payload_size; memset(receive_payload,0,max_payload_size); - if ( configuration != '3' ) + if ( configuration == '3' ) + len = next_payload_size; + else len = radio.getDynamicPayloadSize(); radio.read( receive_payload, len ); @@ -369,8 +372,7 @@ void check_radio(void) receive_payload[len] = 0; // Spew it - len = strlen(receive_payload); // How much did we REALLY get? - printf("Got payload size=%i value=%s\n\r",len,receive_payload); + printf("Got payload size=%i value=%s strlen=%u\n\r",len,receive_payload,strlen(receive_payload)); // Add an ack packet for the next time around. // Here we will report back how many bytes we got this time. diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 3c1e942..f25d1d7 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -3,13 +3,13 @@ # Connect p6 to receiver, p4 to sender jam p4 p6 || exit 1 -./runtest.py /dev/tty.usbserial-A600eHIs 3 & -./runtest.py /dev/tty.usbserial-A40081RP 3 || exit 1 -kill `jobs -p` ./runtest.py /dev/tty.usbserial-A600eHIs 1 & -./runtest.py /dev/tty.usbserial-A40081RP 1 || exit 1 +./runtest.py /dev/tty.usbserial-A40081RP 1 || ( kill `jobs -p` && exit 1 ) kill `jobs -p` ./runtest.py /dev/tty.usbserial-A600eHIs 2 & -./runtest.py /dev/tty.usbserial-A40081RP 2 || exit 1 +./runtest.py /dev/tty.usbserial-A40081RP 2 || ( kill `jobs -p` && exit 1 ) +kill `jobs -p` +./runtest.py /dev/tty.usbserial-A600eHIs 3 & +./runtest.py /dev/tty.usbserial-A40081RP 3 || ( kill `jobs -p` && exit 1 ) kill `jobs -p` exit 0 From 89180f284404e0d598675b8c0ccc5edd301f89b7 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 16:32:21 -0700 Subject: [PATCH 137/326] Added another test of a simpler calling pattern. No IRQ, no dynamic payload or ack packet. --- tests/pingpair_blocking/Jamfile | 206 +++++++++++++ tests/pingpair_blocking/pingpair_blocking.pde | 270 ++++++++++++++++++ tests/pingpair_blocking/printf.h | 33 +++ tests/pingpair_blocking/runtest.py | 25 ++ tests/pingpair_blocking/runtests.sh | 9 + 5 files changed, 543 insertions(+) create mode 100644 tests/pingpair_blocking/Jamfile create mode 100644 tests/pingpair_blocking/pingpair_blocking.pde create mode 100644 tests/pingpair_blocking/printf.h create mode 100755 tests/pingpair_blocking/runtest.py create mode 100755 tests/pingpair_blocking/runtests.sh diff --git a/tests/pingpair_blocking/Jamfile b/tests/pingpair_blocking/Jamfile new file mode 100644 index 0000000..98ec12f --- /dev/null +++ b/tests/pingpair_blocking/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Werror -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/tests/pingpair_blocking/pingpair_blocking.pde b/tests/pingpair_blocking/pingpair_blocking.pde new file mode 100644 index 0000000..817310d --- /dev/null +++ b/tests/pingpair_blocking/pingpair_blocking.pde @@ -0,0 +1,270 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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 +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Test version of RF24, exposes some protected interface +// + +class RF24Test: public RF24 +{ +public: RF24Test(int a, int b): RF24(a,b) {} +}; + + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24Test radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +// +// Test state +// + +bool done; //*< Are we done with the test? */ +bool passed; //*< Have we passed the test? */ +bool notified; //*< Have we notified the user we're done? */ +const int num_needed = 10; //*< How many success/failures until we're done? */ +int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ +int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ +const int interval = 100; //*< ms to wait between sends */ + +char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ + +void one_ok(void) +{ + // Have we received enough yet? + if ( ! --receives_remaining ) + { + done = true; + passed = true; + } +} + +void one_failed(void) +{ + // Have we failed enough yet? + if ( ! --failures_remaining ) + { + done = true; + passed = false; + } +} + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/tests/pingpair_blocking/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // get test config + // + + printf("+READY press any key to start\n\r\n\r"); + + while (! Serial.available() ) {} + configuration = Serial.read(); + printf("Configuration\t = %c\n\r",configuration); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + one_failed(); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + one_ok(); + } + + // Try again later + delay(250); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + + // Delay just a little bit to let the other unit + // make the transition to receiver + delay(20); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + + } + } + + // + // Stop the test if we're done and report results + // + if ( done && ! notified ) + { + notified = true; + + printf("\n\r+OK "); + if ( passed ) + printf("PASS\n\r\n\r"); + else + printf("FAIL\n\r\n\r"); + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/tests/pingpair_blocking/printf.h b/tests/pingpair_blocking/printf.h new file mode 100644 index 0000000..df6c46a --- /dev/null +++ b/tests/pingpair_blocking/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/tests/pingpair_blocking/runtest.py b/tests/pingpair_blocking/runtest.py new file mode 100755 index 0000000..45fb65c --- /dev/null +++ b/tests/pingpair_blocking/runtest.py @@ -0,0 +1,25 @@ +#!/opt/local/bin/python + +import sys,serial + +def read_until(token): + while 1: + line = ser.readline(None,"\r") + sys.stdout.write(line) + + if (line.startswith(token)): + break + return line + + +ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) + +read_until("+READY") +ser.write(sys.argv[2]) + +line = read_until("+OK") +ser.close() +if (line.find("PASS") != -1): + sys.exit(0) +else: + sys.exit(1) diff --git a/tests/pingpair_blocking/runtests.sh b/tests/pingpair_blocking/runtests.sh new file mode 100755 index 0000000..53b16f9 --- /dev/null +++ b/tests/pingpair_blocking/runtests.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Connect p6 to receiver, p4 to sender + +jam p4 p6 || exit 1 +./runtest.py /dev/tty.usbserial-A600eHIs 1 & +./runtest.py /dev/tty.usbserial-A40081RP 1 || ( kill `jobs -p` && exit 1 ) +kill `jobs -p` +exit 0 From 39ca6b95dd127340869445d3e746c8f01c7c623b Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 16:35:04 -0700 Subject: [PATCH 138/326] Ignore .swp files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4b1d772..1ac5497 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.o .*.swp *.orig +.swp docs/ output/ ojam/ From a261710761b885915c5b8c27af453bf3fef5bb57 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 20:59:57 -0700 Subject: [PATCH 139/326] Comment cleanups --- RF24.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 5568607..2686a1d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -455,7 +455,8 @@ bool RF24::write( const void* buf, uint8_t len ) // * There is an ack packet waiting (RX_DR) bool tx_ok, tx_fail; whatHappened(tx_ok,tx_fail,ack_payload_available); - printf("%u%u%u\n\r",tx_ok,tx_fail,ack_payload_available); + + //printf("%u%u%u\n\r",tx_ok,tx_fail,ack_payload_available); result = tx_ok; IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); From d967f7708f0cb2a560f17cb96a60e53608ea565b Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 21:00:31 -0700 Subject: [PATCH 140/326] Test refinement. IRQ test times out after not receiving anything, warning that you probably don't have interrupts hooked up. --- tests/pingpair_test/pingpair_test.pde | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 60ecb6e..c16db2b 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -272,6 +272,13 @@ void loop(void) // Try again soon delay(interval); + + // Timeout if we have not received anything back ever + if ( ! last_message_count && millis() > interval * 10 ) + { + printf("No responses received. Are interrupts connected??\n\r"); + done = true; + } } // @@ -291,6 +298,9 @@ void loop(void) else printf("FAIL\n\r\n\r"); } + + // + // } void check_radio(void) From b715bf0fc89899f539aee8c4a982009fe27cfbe8 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 21:15:14 -0700 Subject: [PATCH 141/326] Comment cleanups --- RF24.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 2686a1d..729a309 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -256,6 +256,9 @@ RF24::RF24(uint8_t _cepin, uint8_t _cspin): void RF24::setChannel(uint8_t channel) { + // TODO: This method could take advantage of the 'wide_band' calculation + // done in setChannel() to require certain channel spacing. + write_register(RF_CH,min(channel,127)); } @@ -723,7 +726,7 @@ bool RF24::isPVariant(void) return p_variant ; } -/******************************************************************/ +/****************************************************************************/ void RF24::setAutoAck(bool enable) { @@ -752,7 +755,7 @@ void RF24::setAutoAck( uint8_t pipe, bool enable ) } } -/******************************************************************/ +/****************************************************************************/ bool RF24::testCarrier(void) { @@ -766,7 +769,7 @@ bool RF24::testRPD(void) return ( read_register(RPD) & 1 ) ; } -/******************************************************************/ +/****************************************************************************/ void RF24::setPALevel(rf24_pa_dbm_e level) { @@ -799,7 +802,7 @@ void RF24::setPALevel(rf24_pa_dbm_e level) write_register( RF_SETUP, setup ) ; } -/******************************************************************/ +/****************************************************************************/ rf24_pa_dbm_e RF24::getPALevel(void) { @@ -828,7 +831,7 @@ rf24_pa_dbm_e RF24::getPALevel(void) return result ; } -/******************************************************************/ +/****************************************************************************/ bool RF24::setDataRate(rf24_datarate_e speed) { @@ -875,7 +878,7 @@ bool RF24::setDataRate(rf24_datarate_e speed) return result; } -/******************************************************************/ +/****************************************************************************/ rf24_datarate_e RF24::getDataRate( void ) { @@ -923,7 +926,7 @@ void RF24::setCRCLength(rf24_crclength_e length) write_register( CONFIG, config ) ; } -/******************************************************************/ +/****************************************************************************/ void RF24::disableCRC( void ) { From 483361029e812f22d8fd66e431d4720fe04a5928 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 2 Aug 2011 21:35:45 -0700 Subject: [PATCH 142/326] Reorganize header file to better group the now very large list of methods --- RF24.h | 243 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 126 insertions(+), 117 deletions(-) diff --git a/RF24.h b/RF24.h index 1d5e531..9e32939 100644 --- a/RF24.h +++ b/RF24.h @@ -329,12 +329,13 @@ public: /**@}*/ /** - * @name Optional public interface + * @name Optional Configurators * - * Methods you may want to use but are not needed for regular operation + * Methods you can use to get or set the configuration of the chip. + * None are required. Calling begin() sets up a reasonable set of + * defaults. */ /**@{*/ - /** * Set the number and delay of retries upon failed submit * @@ -383,55 +384,7 @@ public: * @return Payload length of last-received dynamic payload */ uint8_t getDynamicPayloadSize(void); - - /** - * Print a giant block of debugging information to stdout - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void printDetails(void); - - /** - * Enter low-power mode - * - * To return to normal power mode, either write() some data or - * startListening, or powerUp(). - */ - void powerDown(void); - - /** - * Leave low-power mode - making radio more responsive - * - * To return to low power mode, call powerDown(). - */ - void powerUp(void) ; - - /** - * Test whether there are bytes available to be read - * - * Use this version to discover on which pipe the message - * arrived. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - bool available(uint8_t* pipe_num); - - /** - * Non-blocking write to the open writing pipe - * - * Just like write(), but it returns immediately. To find out what happened - * to the send, catch the IRQ and then call whatHappened(). - * - * @see write() - * @see whatHappened() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - void startWrite( const void* buf, uint8_t len ); - + /** * Enable custom payloads on the acknowledge packets * @@ -452,37 +405,6 @@ public: */ void enableDynamicPayloads(void); - /** - * Write an ack payload for the specified pipe - * - * The next time a message is received on @p pipe, the data in @p buf will - * be sent back in the acknowledgement. - * - * @warning According to the data sheet, only three of these can be pending - * at any time. I have not tested this. - * - * @param pipe Which pipe# (typically 1-5) will get this response. - * @param buf Pointer to data that is sent - * @param len Length of the data to send, up to 32 bytes max. Not affected - * by the static payload set by setPayloadSize(). - */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); - - /** - * Determine if an ack payload was received in the most recent call to - * write(). - * - * Call read() to retrieve the ack payload. - * - * @warning Calling this function clears the internal flag which indicates - * a payload is available. If it returns true, you must read the packet - * out as the very next interaction with the radio, or the results are - * undefined. - * - * @return True if an ack payload is available. - */ - bool isAckPayloadAvailable(void); - /** * Determine whether the hardware is an nRF24L01+ or not. * @@ -491,18 +413,6 @@ public: */ bool isPVariant(void) ; - /** - * Call this when you get an interrupt to find out why - * - * Tells you what caused the interrupt, and clears the state of - * interrupts. - * - * @param[out] tx_ok The send was successful (TX_DS) - * @param[out] tx_fail The send failed, too many retries (MAX_RT) - * @param[out] rx_ready There is a message waiting to be read (RX_DS) - */ - void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); - /** * Enable or disable auto-acknowlede packets * @@ -524,28 +434,6 @@ public: */ void setAutoAck( uint8_t pipe, bool enable ) ; - /** - * Test whether there was a carrier on the line for the - * previous listening period. - * - * Useful to check for interference on the current channel. - * - * @return true if was carrier, false if not - */ - bool testCarrier(void); - - /** - * Test whether a signal (carrier or otherwise) greater than - * or equal to -64dBm is present on the channel. Valid only - * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). - * - * Useful to check for interference on the current channel and - * channel hopping strategies. - * - * @return true if signal => -64dBm, false if not - */ - bool testRPD(void) ; - /** * Set Power Amplifier (PA) level to one of four levels. * Relative mnemonics have been used to allow for future PA level @@ -599,6 +487,127 @@ public: */ void disableCRC( void ) ; + /**@}*/ + /** + * @name Advanced Operation + * + * Methods you can use to drive the chip in more advanced ways + */ + /**@{*/ + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); + + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening, or powerUp(). + */ + void powerDown(void); + + /** + * Leave low-power mode - making radio more responsive + * + * To return to low power mode, call powerDown(). + */ + void powerUp(void) ; + + /** + * Test whether there are bytes available to be read + * + * Use this version to discover on which pipe the message + * arrived. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + bool available(uint8_t* pipe_num); + + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @see write() + * @see whatHappened() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + void startWrite( const void* buf, uint8_t len ); + + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time. I have not tested this. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @warning Calling this function clears the internal flag which indicates + * a payload is available. If it returns true, you must read the packet + * out as the very next interaction with the radio, or the results are + * undefined. + * + * @return True if an ack payload is available. + */ + bool isAckPayloadAvailable(void); + + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + bool testCarrier(void); + + /** + * Test whether a signal (carrier or otherwise) greater than + * or equal to -64dBm is present on the channel. Valid only + * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * + * Useful to check for interference on the current channel and + * channel hopping strategies. + * + * @return true if signal => -64dBm, false if not + */ + bool testRPD(void) ; + /**@}*/ }; From 8f17e63a22ac01f03a3923b139799baec8256443 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 4 Aug 2011 20:14:27 -0700 Subject: [PATCH 143/326] Add getDataRate, add DISABLED to data rate enum --- RF24.cpp | 40 ++++++++++++++++++++++++++++++++-------- RF24.h | 9 ++++++++- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 729a309..401715c 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -911,16 +911,22 @@ rf24_datarate_e RF24::getDataRate( void ) void RF24::setCRCLength(rf24_crclength_e length) { - uint8_t config = read_register(CONFIG) & ~_BV(CRCO) ; + uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - // Always make sure CRC hardware validation is actually on - config |= _BV(EN_CRC) ; - - // Now config 8 or 16 bit CRCs - only 16bit need be turned on - // 8b is the default. - if( length == RF24_CRC_16 ) + switch (length) { - config |= _BV( CRCO ) ; + case RF24_CRC_DISABLED: + break; + + case RF24_CRC_8: + config |= _BV(EN_CRC); + break; + + case RF24_CRC_16: + default: + config |= _BV(EN_CRC); + config |= _BV( CRCO ); + break; } write_register( CONFIG, config ) ; @@ -928,6 +934,24 @@ void RF24::setCRCLength(rf24_crclength_e length) /****************************************************************************/ +rf24_crclength_e RF24::getCRCLength(void) +{ + rf24_crclength_e result = RF24_CRC_DISABLED; + uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; + + if ( config & _BV(EN_CRC ) ) + { + if ( config & _BV(CRCO) ) + result = RF24_CRC_16; + else + result = RF24_CRC_8; + } + + return result; +} + +/****************************************************************************/ + void RF24::disableCRC( void ) { uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; diff --git a/RF24.h b/RF24.h index 9e32939..2c7dce8 100644 --- a/RF24.h +++ b/RF24.h @@ -14,7 +14,7 @@ typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; -typedef enum { RF24_CRC_8 = 0, RF24_CRC_16 } rf24_crclength_e; +typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; /** * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver @@ -481,6 +481,13 @@ public: */ void setCRCLength(rf24_crclength_e length); + /** + * Get the CRC length + * + * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + rf24_crclength_e getCRCLength(void); + /** * Disable CRC validation * From 4c21ab6ad099c193f1d47822c4e13fc9c77be9d0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 4 Aug 2011 20:16:07 -0700 Subject: [PATCH 144/326] Added back in gcopeland's additional debugging info, with readable values. --- RF24.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 401715c..8fb3dc2 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -294,17 +294,13 @@ void RF24::printDetails(void) print_byte_register(PSTR("CONFIG"),CONFIG); print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); - // These need to be merged in with the register printing scheme -#if 0 - read_register(RF_SETUP,buffer,1); - printf_P(PSTR("RF_SETUP = 0x%02x (data rate: %d)\n\r"),*buffer,getDataRate()); - printf_P(PSTR("Hardware; isPVariant: %d\n\r"),isPVariant()); - - read_register(CONFIG,buffer,1); - printf_P(PSTR("CONFIG = 0x%02x (CRC enable: %d; CRC16: %d)\n\r"), - *buffer,(*buffer)&_BV(EN_CRC)?1:0, - (*buffer)&_BV(CRCO)?1:0); -#endif + const char * rf24_datarate_e_str[] = { "1MBPS", "2MBPS", "250KBPS" }; + const char * rf24_model_e_str[] = { "nRF24L01", "nRF24L01+" } ; + const char * rf24_crclength_e_str[] = { "Disabled", "8 bits", "16 bits" } ; + + printf_P(PSTR("Data Rate\t = %s\n\r"),rf24_datarate_e_str[getDataRate()]); + printf_P(PSTR("Model\t\t = %s\n\r"),rf24_model_e_str[isPVariant()]); + printf_P(PSTR("CRC Length\t = %s\n\r"),rf24_crclength_e_str[getCRCLength()]); } /****************************************************************************/ From 7aeaf0d37329db116e8e363098f61b0b27bbf132 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 4 Aug 2011 20:45:58 -0700 Subject: [PATCH 145/326] Added FAQ page to docs --- Doxyfile | 2 +- FAQ | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ RF24.h | 4 ++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 FAQ diff --git a/Doxyfile b/Doxyfile index 5cfb66f..2bd1ea0 100644 --- a/Doxyfile +++ b/Doxyfile @@ -591,7 +591,7 @@ INPUT_ENCODING = UTF-8 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 -FILE_PATTERNS = *.h +FILE_PATTERNS = *.h FAQ # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. diff --git a/FAQ b/FAQ new file mode 100644 index 0000000..d134bb8 --- /dev/null +++ b/FAQ @@ -0,0 +1,56 @@ +/** + * @page FAQ Frequently Asked Questions + * + * @ref starting + * + * @ref hardware + * + * @ref range + * + * @ref tests + * + * @section starting Where do I start? + * + * Start with the pingpair example. Follow the instructions in the comments + * of that sketch. + * + * @section hardware Where can I buy some hardware? + * + * I've been using modules from mdfly.com. + * + * Mostly these units as the workhorse of the system: + * + * @li 2.4Ghz Wireless nRF24L01 Transceiver Module $6.95 + * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=82 + * + * And then also experimenting with these for greater range + * + * @li nRF24L01 2.4GHz Transceiver Module w/ Power Amplifier $12.95 + * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=394 + * @li 2.4GHz Transceiver Module w/ Power Amplifier $19.95 + * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=583 + * + * However, I also noticed that iTeadStudio has nRF24L01+ units for $4! + * + * @section range What is the range of these units? + * + * Here are some results from measurements I have taken. I recommend that everyone + * take their own measurements in their particular circumstances. + * + * @li non-plus unit, 2MBps (worst case), 41+ ft line of sight indoors, immediate dropoff with any deviation from LOS. (41 ft is as far as I can go in my house without turning a corner) + * @li Plus unit, 250kbps (best case), 46 ft around two corners indoors, 49 ft around one corner. More importantly, at 250k, packet loss is almost negligible through almost all of that range. + * @li Both units at 1MBps, plus unit gets about 10% range improvement over non-plus in almost all situations. + * + * @section tests Why are the examples in the 'tests' directory failing? + * + * The sketches in the 'tests' directory are not for general use. + * Please use the examples in the 'examples' directory instead. + * + * The 'tests' directory is only for people making changes to the library + * to ensure that their changes do not break anything. + * + * + * + * + * + */ diff --git a/RF24.h b/RF24.h index 2c7dce8..e575158 100644 --- a/RF24.h +++ b/RF24.h @@ -707,6 +707,10 @@ public: * * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or * the SPI hardware will go into 'slave' mode. + * + * @section More More Information + * + * @subpage FAQ */ #endif // __RF24_H__ From 5344c5fe8cefaafa1a267267e7edefec631603d9 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 4 Aug 2011 20:46:30 -0700 Subject: [PATCH 146/326] CHanged default data rate to 1MBps and default channel to 100 for better performance --- RF24.cpp | 7 +++++-- examples/pingpair/pingpair.pde | 6 ------ examples/pingpair_dyn/pingpair_dyn.pde | 6 ------ examples/pingpair_irq/pingpair_irq.pde | 6 ------ 4 files changed, 5 insertions(+), 20 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 8fb3dc2..6ad87e4 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -351,7 +351,10 @@ void RF24::begin(void) { p_variant = true ; } - setDataRate( RF24_2MBPS ) ; + + // Then set the data rate to the slowest (and most reliable) speed supported by all + // hardware. + setDataRate( RF24_1MBPS ) ; // Initialize CRC and request 2-byte (16bit) CRC setCRCLength( RF24_CRC_16 ) ; @@ -364,7 +367,7 @@ void RF24::begin(void) write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Set up default configuration. Callers can always change it later. - setChannel(1); + setChannel(100); // Flush buffers flush_rx(); diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index d9f2b7e..05254c4 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -93,12 +93,6 @@ void setup(void) // optionally, increase the delay between retries & # of retries radio.setRetries(15,15); - // optionally, use a high channel to avoid WiFi chatter - radio.setChannel(110); - - // optionally, increase the CRC length for improved reliability - radio.setCRCLength(RF24_CRC_16); - // optionally, reduce the payload size. seems to // improve reliability radio.setPayloadSize(8); diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde index 7655505..5d3be9e 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -104,12 +104,6 @@ void setup(void) // optionally, increase the delay between retries & # of retries radio.setRetries(15,15); - // optionally, use a high channel to avoid WiFi chatter - radio.setChannel(110); - - // optionally, increase the CRC length for improved reliability - radio.setCRCLength(RF24_CRC_16); - // // Open pipes to other nodes for communication // diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index e8c5396..bf565cb 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -94,12 +94,6 @@ void setup(void) // We will be using the Ack Payload feature, so please enable it radio.enableAckPayload(); - // Optional: Increase CRC length for improved reliability - radio.setCRCLength(RF24_CRC_16); - - // Optional: Pick a high channel - radio.setChannel(110); - // // Open pipes to other nodes for communication // From e1527afc31bb46fff3929df579aec5e81eb9d078 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 27 Aug 2011 21:58:06 -0700 Subject: [PATCH 147/326] Small tweaks and new test for native target --- .gitignore | 1 + RF24.cpp | 2 + tests/native/Jamfile | 300 ++++++++++++++++++++++++++++++++++ tests/native/pingpair_irq.pde | 223 +++++++++++++++++++++++++ tests/native/printf.h | 33 ++++ 5 files changed, 559 insertions(+) create mode 100644 tests/native/Jamfile create mode 100644 tests/native/pingpair_irq.pde create mode 100644 tests/native/printf.h diff --git a/.gitignore b/.gitignore index 1ac5497..12bc711 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ docs/ output/ ojam/ +out_native/ version.h Session.vim diff --git a/RF24.cpp b/RF24.cpp index 6ad87e4..5e4fc6a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -19,10 +19,12 @@ #endif // Avoid spurious warnings +#ifndef NATIVE #undef PROGMEM #define PROGMEM __attribute__(( section(".progmem.data") )) #undef PSTR #define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) +#endif /****************************************************************************/ diff --git a/tests/native/Jamfile b/tests/native/Jamfile new file mode 100644 index 0000000..10d0336 --- /dev/null +++ b/tests/native/Jamfile @@ -0,0 +1,300 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +SKETCH_DIR = $(HOME)/Source/Arduino ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(SKETCH_DIR)/libraries ; +AVR_AS = $(AVR_TOOLS_PATH)/avr-as ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H HAL=1 ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +ASFLAGS = -mmcu=$(MCU) ; +CFLAGS = -Os -Wall -Wextra $(ASFLAGS) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; +HDRS += [ GLOB $(HDRS) : utility ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +# GitVersion version.h ; + +rule AvrAsm +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrAsm +{ + $(AVR_AS) $(ASFLAGS) -o $(<) $(>) +} + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule AvrAsmFromC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrAsmFromC++ +{ + $(AVR_CXX) -S -fverbose-asm -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .S : AvrAsm $(<) : $(>) ; + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + +# +# Native +# + +OUT_DIR_NATIVE = out_native ; +OUT_NATIVE = $(OUT_DIR_NATIVE)/$(PROJECT_NAME) ; +NATIVE_CORE = $(SKETCH_DIR)/hardware/native ; +HDRS = $(NATIVE_CORE) $(HDRS) ; +NATIVE_CORE_MODULES = [ GLOB $(NATIVE_CORE) : *.c *.cpp ] ; +NATIVE_MODULES = ; +DEFINES += NATIVE ; + +rule NativePde +{ + local _CPP = $(OUT_DIR_NATIVE)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>) + { + case *.pde : NativePde $(<) : $(>) ; + } +} + +rule Objects +{ + for _I in $(<) + { + local _O = $(OUT_DIR_NATIVE)/$(_I:B).o ; + Object $(_O) : $(_I) ; + } +} + +rule Main +{ + MainFromObjects $(<) : $(OUT_DIR_NATIVE)/$(>:B).o ; + Objects $(>) ; +} + +actions C++ +{ + c++ -c -o $(<) $(CCHDRS) $(CCDEFS) $(>) +} + +actions Link +{ + c++ -o $(<) $(>) +} + + + +MkDir $(OUT_DIR_NATIVE) ; +Depends $(OUT_NATIVE) : $(OUT_DIR_NATIVE) ; +Main $(OUT_NATIVE) : $(NATIVE_CORE_MODULES) $(NATIVE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; + +Depends native : $(OUT_NATIVE) ; + diff --git a/tests/native/pingpair_irq.pde b/tests/native/pingpair_irq.pde new file mode 100644 index 0000000..99c2cdf --- /dev/null +++ b/tests/native/pingpair_irq.pde @@ -0,0 +1,223 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Interrupt-driven test for native target + * + * This example is the friendliest for the native target because it doesn't do + * any polling. Made a slight change to call done() at the end of setup. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const short role_pin = 7; + +// +// Topology +// + +// Single radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_sender = 1, role_receiver } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; + +// The role of the current running sketch +role_e role; + +// Interrupt handler, check the radio because we got an IRQ +void check_radio(void); + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_sender; + else + role = role_receiver; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_irq/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // We will be using the Ack Payload feature, so please enable it + radio.enableAckPayload(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens a single pipe for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. + + if ( role == role_sender ) + { + radio.openWritingPipe(pipe); + } + else + { + radio.openReadingPipe(1,pipe); + } + + // + // Start listening + // + + if ( role == role_receiver ) + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Attach interrupt handler to interrupt #0 (using pin 2) + // on BOTH the sender and receiver + // + + attachInterrupt(0, check_radio, FALLING); + + // + // On the native target, this is as far as we get + // +#if NATIVE + done(); +#endif +} + +static uint32_t message_count = 0; + +void loop(void) +{ + // + // Sender role. Repeatedly send the current time + // + + if (role == role_sender) + { + // Take the time, and send it. + unsigned long time = millis(); + printf("Now sending %lu\n\r",time); + radio.startWrite( &time, sizeof(unsigned long) ); + + // Try again soon + delay(2000); + } + + // + // Receiver role: Does nothing! All the work is in IRQ + // + +} + +void check_radio(void) +{ + // What happened? + bool tx,fail,rx; + radio.whatHappened(tx,fail,rx); + + // Have we successfully transmitted? + if ( tx ) + { + if ( role == role_sender ) + printf("Send:OK\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Sent\n\r"); + } + + // Have we failed to transmit? + if ( fail ) + { + if ( role == role_sender ) + printf("Send:Failed\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Failed\n\r"); + } + + // Transmitter can power down for now, because + // the transmission is done. + if ( ( tx || fail ) && ( role == role_sender ) ) + radio.powerDown(); + + // Did we receive a message? + if ( rx ) + { + // If we're the sender, we've received an ack payload + if ( role == role_sender ) + { + radio.read(&message_count,sizeof(message_count)); + printf("Ack:%lu\n\r",(unsigned long)message_count); + } + + // If we're the receiver, we've received a time message + if ( role == role_receiver ) + { + // Get this payload and dump it + static unsigned long got_time; + radio.read( &got_time, sizeof(got_time) ); + printf("Got payload %lu\n\r",got_time); + + // Add an ack packet for the next time around. This is a simple + // packet counter + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); + ++message_count; + } + } +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/tests/native/printf.h b/tests/native/printf.h new file mode 100644 index 0000000..df6c46a --- /dev/null +++ b/tests/native/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From cc7e0a2f19264852847edd42c4231584da8ec104 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 12 Sep 2011 19:11:37 -0700 Subject: [PATCH 148/326] Fixed a bug when pipe 0 was NOT being used for writing. --- RF24.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 6ad87e4..5ef182d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -247,8 +247,9 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) /****************************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), payload_size(32), - ack_payload_available(false), dynamic_payloads_enabled(false) + ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), + payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), + pipe0_reading_address(0) { } @@ -381,8 +382,9 @@ void RF24::startListening(void) write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - // Restore the pipe0 adddress - write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address) + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); // Flush buffers flush_rx(); From dceef7b45ec2ae29025089fea2fa9dcdf411aea0 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 12 Sep 2011 19:13:07 -0700 Subject: [PATCH 149/326] Added more information about whether the write was successful. --- examples/pingpair/pingpair.pde | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 05254c4..2bf2267 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -144,7 +144,12 @@ void loop(void) // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (ok) + printf("ok..."); + else + printf("failed.\n\r"); // Now, continue listening radio.startListening(); From 7ad1b409fe180b7c37aeecefb08bc4f93c503e5d Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Mon, 12 Sep 2011 19:21:49 -0700 Subject: [PATCH 150/326] More debugging information. --- RF24.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index 5ef182d..35eab2b 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -298,10 +298,12 @@ void RF24::printDetails(void) const char * rf24_datarate_e_str[] = { "1MBPS", "2MBPS", "250KBPS" }; const char * rf24_model_e_str[] = { "nRF24L01", "nRF24L01+" } ; const char * rf24_crclength_e_str[] = { "Disabled", "8 bits", "16 bits" } ; + const char * rf24_pa_dbm_e_str[] = { "PA_MIN", "PA_LOW", "LA_MED", "PA_HIGH"} ; printf_P(PSTR("Data Rate\t = %s\n\r"),rf24_datarate_e_str[getDataRate()]); printf_P(PSTR("Model\t\t = %s\n\r"),rf24_model_e_str[isPVariant()]); printf_P(PSTR("CRC Length\t = %s\n\r"),rf24_crclength_e_str[getCRCLength()]); + printf_P(PSTR("PA Power\t = %s\n\r"),rf24_pa_dbm_e_str[getPALevel()]); } /****************************************************************************/ From a4cabc66a2b15c45182718c9302385d7378b4447 Mon Sep 17 00:00:00 2001 From: Greg Copeland Date: Wed, 17 Aug 2011 12:02:51 -0500 Subject: [PATCH 151/326] Yet more merge cleanup. Changes default channel to be US/Internationally legal while ensuring no spectrum bleed. --- RF24.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 35eab2b..1e87ef6 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -370,7 +370,9 @@ void RF24::begin(void) write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Set up default configuration. Callers can always change it later. - setChannel(100); + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); // Flush buffers flush_rx(); From a28e2361fc1bab48117cff68d07ab9ee9951948f Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 12 Sep 2011 19:27:27 -0700 Subject: [PATCH 152/326] SPI bus speed divider to 4, suggested by Greg Copeland. Also removed duplicated spi setup out of begin(), as it's all in csn() --- RF24.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 1e87ef6..eae7a45 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -28,9 +28,13 @@ void RF24::csn(int mode) { + // Minimum ideal SPI bus speed is 2x data rate + // If we assume 2Mbs data rate and 16Mhz clock, a + // divider of 4 is the minimum we want. + // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); + SPI.setClockDivider(SPI_CLOCK_DIV4); digitalWrite(csn_pin,mode); } @@ -315,17 +319,7 @@ void RF24::begin(void) pinMode(csn_pin,OUTPUT); // Initialize SPI bus - // Minimum ideal SPI bus speed is 2x data rate - // If we assume 2Mbs data rate and 16Mhz clock, a - // divider of 4 is the minimum we want. - // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz - // We'll use a divider of 2 which will work up to - // MCU speeds of 20Mhz. - // CLK:BUS 8Mhz:4Mhz, 16Mhz:8Mhz, or 20Mhz:10Mhz (max) SPI.begin(); - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); ce(LOW); csn(HIGH); From 19dd5c77265bd61d6c84df8bd9c4278f7c449c5e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Fri, 7 Oct 2011 19:07:08 -0700 Subject: [PATCH 153/326] Avert warnings on native platform --- RF24.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 5e4fc6a..8ed606a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -261,14 +261,16 @@ void RF24::setChannel(uint8_t channel) // TODO: This method could take advantage of the 'wide_band' calculation // done in setChannel() to require certain channel spacing. - write_register(RF_CH,min(channel,127)); + const uint8_t max_channel = 127; + write_register(RF_CH,min(channel,max_channel)); } /****************************************************************************/ void RF24::setPayloadSize(uint8_t size) { - payload_size = min(size,32); + const uint8_t max_payload_size = 32; + payload_size = min(size,max_payload_size); } /****************************************************************************/ @@ -590,7 +592,9 @@ void RF24::openWritingPipe(uint64_t value) write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); write_register(TX_ADDR, reinterpret_cast(&value), 5); - write_register(RX_PW_P0,min(payload_size,32)); + + const uint8_t max_payload_size = 32; + write_register(RX_PW_P0,min(payload_size,max_payload_size)); } /****************************************************************************/ @@ -704,7 +708,8 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) csn(LOW); SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); - uint8_t data_len = min(len,32); + const uint8_t max_payload_size = 32; + uint8_t data_len = min(len,max_payload_size); while ( data_len-- ) SPI.transfer(*current++); From 066d79f21e5570251f4b7e56e14a3248200c0f05 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 12 Oct 2011 20:36:00 -0700 Subject: [PATCH 154/326] Flush all buffers on start and stop listening --- RF24.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index c919f56..c795f35 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -388,6 +388,7 @@ void RF24::startListening(void) // Flush buffers flush_rx(); + flush_tx(); // Go! ce(HIGH); @@ -401,6 +402,8 @@ void RF24::startListening(void) void RF24::stopListening(void) { ce(LOW); + flush_tx(); + flush_rx(); } /****************************************************************************/ From 8ea84e7f77fc4122d77b1bd035d78d18eaab0bda Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 19 Oct 2011 15:30:33 -0700 Subject: [PATCH 155/326] Update docs for 1.0 --- .gitignore | 3 +++ RF24.h | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 12bc711..ac32040 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ docs/ output/ ojam/ +out/ +16000000/ +8000000/ out_native/ version.h Session.vim diff --git a/RF24.h b/RF24.h index e575158..33d01d1 100644 --- a/RF24.h +++ b/RF24.h @@ -695,7 +695,10 @@ public: * @li Maximally compliant with the intended operation of the chip * @li Easy for beginners to use * @li Consumed with a public interface that's similiar to other Arduino standard libraries - * @li Built against the standard SPI library. + * + * NOW COMPATIBLE WITH ARDUINO 1.0 - Please use the + * 'arduino-1.0' branch + * for use with Arduino 1.0. This will be merged into master when Arduino 1.0 is released. * * Please refer to: * From f5c0ba25ee04dd4689f90261d533e5040ed888ce Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 19 Oct 2011 16:09:26 -0700 Subject: [PATCH 156/326] Using Arduino 21 --- tests/pingpair_test/Jamfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/pingpair_test/Jamfile b/tests/pingpair_test/Jamfile index 98ec12f..ebc0bae 100644 --- a/tests/pingpair_test/Jamfile +++ b/tests/pingpair_test/Jamfile @@ -18,7 +18,8 @@ ARDUINO_VERSION = 22 ; OLD_DIR = /opt/arduino-0021 ; AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; +#ARDUINO_DIR = /opt/Arduino ; +ARDUINO_DIR = $(OLD_DIR) ; ARDUINO_AVR = /usr/lib/avr/include ; } @@ -40,7 +41,7 @@ AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; CTUNING = -ffunction-sections -fdata-sections ; CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Werror -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; From d92ac14f8685f01a3ea2a1d94083b0ca99851f60 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 2 Nov 2011 21:28:28 -0700 Subject: [PATCH 157/326] Compatibility with Arduino 1.0 --- RF24.cpp | 5 +++++ examples/led_remote/printf.h | 2 -- examples/pingpair/printf.h | 2 -- examples/pingpair_dyn/printf.h | 2 -- examples/pingpair_irq/printf.h | 2 -- examples/pingpair_pl/printf.h | 2 -- examples/pingpair_sleepy/printf.h | 2 -- examples/scanner/printf.h | 2 -- examples/starping/printf.h | 2 -- 9 files changed, 5 insertions(+), 16 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 45623ff..98e0c42 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -6,7 +6,12 @@ version 2 as published by the Free Software Foundation. */ +#if ARDUINO < 100 #include +#else +#include +#endif + #include #include "nRF24L01.h" #include "RF24.h" diff --git a/examples/led_remote/printf.h b/examples/led_remote/printf.h index df6c46a..1b853db 100644 --- a/examples/led_remote/printf.h +++ b/examples/led_remote/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair/printf.h b/examples/pingpair/printf.h index df6c46a..1b853db 100644 --- a/examples/pingpair/printf.h +++ b/examples/pingpair/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair_dyn/printf.h b/examples/pingpair_dyn/printf.h index df6c46a..1b853db 100644 --- a/examples/pingpair_dyn/printf.h +++ b/examples/pingpair_dyn/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair_irq/printf.h b/examples/pingpair_irq/printf.h index df6c46a..1b853db 100644 --- a/examples/pingpair_irq/printf.h +++ b/examples/pingpair_irq/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair_pl/printf.h b/examples/pingpair_pl/printf.h index df6c46a..1b853db 100644 --- a/examples/pingpair_pl/printf.h +++ b/examples/pingpair_pl/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/pingpair_sleepy/printf.h b/examples/pingpair_sleepy/printf.h index df6c46a..1b853db 100644 --- a/examples/pingpair_sleepy/printf.h +++ b/examples/pingpair_sleepy/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/scanner/printf.h b/examples/scanner/printf.h index df6c46a..1b853db 100644 --- a/examples/scanner/printf.h +++ b/examples/scanner/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); diff --git a/examples/starping/printf.h b/examples/starping/printf.h index df6c46a..1b853db 100644 --- a/examples/starping/printf.h +++ b/examples/starping/printf.h @@ -16,8 +16,6 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" - int serial_putc( char c, FILE * ) { Serial.write( c ); From 3a2d94b698c463f0b3168d7080ea1398bc7a6303 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 2 Nov 2011 21:46:49 -0700 Subject: [PATCH 158/326] Added new example to go along with Getting Started tutorial blog post. --- examples/GettingStarted/GettingStarted.pde | 227 +++++++++++++++++++++ examples/GettingStarted/Jamfile | 219 ++++++++++++++++++++ examples/GettingStarted/printf.h | 31 +++ 3 files changed, 477 insertions(+) create mode 100644 examples/GettingStarted/GettingStarted.pde create mode 100644 examples/GettingStarted/Jamfile create mode 100644 examples/GettingStarted/printf.h diff --git a/examples/GettingStarted/GettingStarted.pde b/examples/GettingStarted/GettingStarted.pde new file mode 100644 index 0000000..800df43 --- /dev/null +++ b/examples/GettingStarted/GettingStarted.pde @@ -0,0 +1,227 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Example for Getting Started with nRF24L01+ radios. + * + * This is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(9,10); + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role = role_pong_back; + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // optionally, reduce the payload size. seems to + // improve reliability + radio.setPayloadSize(8); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (ok) + printf("ok..."); + else + printf("failed.\n\r"); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + + // Delay just a little bit to let the other unit + // make the transition to receiver + delay(20); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } + + // + // Change roles + // + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + // Become the primary transmitter (ping out) + role = role_ping_out; + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + // Become the primary receiver (pong back) + role = role_pong_back; + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/GettingStarted/Jamfile b/examples/GettingStarted/Jamfile new file mode 100644 index 0000000..9e113df --- /dev/null +++ b/examples/GettingStarted/Jamfile @@ -0,0 +1,219 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +F_CPU ?= 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; +OUT_DIR = $(F_CPU) ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 100 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 100 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino $(ARDUINO_DIR)/hardware/arduino/variants/standard ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + actions Pde + { + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) + } +} +else +{ + actions Pde + { + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) + } +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + case .ino : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/examples/GettingStarted/printf.h b/examples/GettingStarted/printf.h new file mode 100644 index 0000000..1b853db --- /dev/null +++ b/examples/GettingStarted/printf.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ From d4371639e612e8c74a9dcfb3148118439a6a9dac Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 2 Nov 2011 21:54:30 -0700 Subject: [PATCH 159/326] Added new example to documentation --- RF24.h | 13 +++++++++++++ examples/GettingStarted/GettingStarted.pde | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/RF24.h b/RF24.h index e575158..9af5844 100644 --- a/RF24.h +++ b/RF24.h @@ -618,6 +618,19 @@ public: /**@}*/ }; +/** + * @example GettingStarted.pde + * + * This is an example which corresponds to my "Getting Started" blog post + * for Getting Started with nRF24L01+ radios. + * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + /** * @example led_remote.pde * diff --git a/examples/GettingStarted/GettingStarted.pde b/examples/GettingStarted/GettingStarted.pde index 800df43..3cb32d5 100644 --- a/examples/GettingStarted/GettingStarted.pde +++ b/examples/GettingStarted/GettingStarted.pde @@ -60,7 +60,7 @@ void setup(void) Serial.begin(57600); printf_begin(); - printf("\n\rRF24/examples/pingpair/\n\r"); + printf("\n\rRF24/examples/GettingStarted/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); From 5726c1622a1dc4d12d9a71d90e3dce167dba5e27 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 28 Nov 2011 21:23:38 -0800 Subject: [PATCH 160/326] Fixed bug in scanner display, thanks to David Bath. Also upgraded to latest Jam toolchain and moved pins over to the new GettingStarted defaults. --- examples/scanner/Jamfile | 368 ++++++++++++++++++----------------- examples/scanner/scanner.pde | 10 +- 2 files changed, 191 insertions(+), 187 deletions(-) diff --git a/examples/scanner/Jamfile b/examples/scanner/Jamfile index 901f8da..1bf541e 100644 --- a/examples/scanner/Jamfile +++ b/examples/scanner/Jamfile @@ -1,206 +1,210 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; +# (1) Project Information -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; +PROJECT_LIBS = SPI RF24 ; -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; +# (2) Board Information + +UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_SPEED ?= 57600 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. -# Host-specific overrides for locations if $(OS) = MACOSX { -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN = /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; } -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; # Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +# +# Targets +# # Grab everything from the core directory CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; # Grab everything from libraries. To avoid this "grab everything" behaviour, you # can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; +# Grab everything from the current dir +PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; +# Main output executable +MAIN = $(PWD:B).elf ; -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; +Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +Hex $(MAIN:B).hex : $(MAIN) ; -rule GitVersion +# Upload targets +for _p in $(PORTS) { - Always $(<) ; - Depends all : $(<) ; + Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; } - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.pde index f8beaf9..6e74286 100644 --- a/examples/scanner/scanner.pde +++ b/examples/scanner/scanner.pde @@ -29,14 +29,14 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // // Channel info // -const short num_channels = 128; -short values[num_channels]; +const uint8_t num_channels = 128; +uint8_t values[num_channels]; // // Setup @@ -84,12 +84,12 @@ void setup(void) // Loop // -const short num_reps = 100; +const int num_reps = 100; void loop(void) { // Clear measurement values - memset(values,0,num_channels); + memset(values,0,sizeof(values)); // Scan all channels num_reps times int rep_counter = num_reps; From 9f806a5b00ee2809a530b2695661431970267db9 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 28 Nov 2011 21:24:37 -0800 Subject: [PATCH 161/326] Move default pins over to Getting Started defaults --- examples/led_remote/led_remote.pde | 2 +- examples/pingpair/pingpair.pde | 2 +- examples/pingpair_dyn/pingpair_dyn.pde | 2 +- examples/pingpair_irq/pingpair_irq.pde | 2 +- examples/pingpair_pl/pingpair_pl.pde | 2 +- examples/pingpair_sleepy/pingpair_sleepy.pde | 2 +- examples/starping/starping.pde | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index 45f971d..02f89f5 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -36,7 +36,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver // Leave open to be the 'remote' transmitter diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 2bf2267..3cedd66 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -26,7 +26,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde index 5d3be9e..cea56ee 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -23,7 +23,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index bf565cb..2c4f583 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -24,7 +24,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index 8980293..94a0da5 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -26,7 +26,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index dea0d3c..809679e 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -33,7 +33,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index ac5197a..97b0953 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -33,7 +33,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'pong' receiver. From f3934084a1368a2e1683ce9b7739a4099edf8c69 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 10 Dec 2011 00:42:23 -0800 Subject: [PATCH 162/326] Changes to work on Maple --- RF24.cpp | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 98e0c42..c6edc1a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -24,13 +24,19 @@ #endif // Avoid spurious warnings -#ifndef NATIVE +#if ! defined( NATIVE ) && defined( ARDUINO ) #undef PROGMEM #define PROGMEM __attribute__(( section(".progmem.data") )) #undef PSTR #define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) #endif +#ifdef ARDUINO +#define PRIPSTR "%S" +#else +#define PRIPSTR "%s" +#endif + /****************************************************************************/ void RF24::csn(int mode) @@ -39,9 +45,11 @@ void RF24::csn(int mode) // If we assume 2Mbs data rate and 16Mhz clock, a // divider of 4 is the minimum we want. // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz +#ifdef ARDUINO SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV4); +#endif digitalWrite(csn_pin,mode); } @@ -102,7 +110,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) { uint8_t status; - IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\n\r"),reg,value)); + IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); csn(LOW); status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); @@ -202,7 +210,7 @@ uint8_t RF24::get_status(void) void RF24::print_status(uint8_t status) { - printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\n\r"), + printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), status, (status & _BV(RX_DR))?1:0, (status & _BV(TX_DS))?1:0, @@ -216,7 +224,7 @@ void RF24::print_status(uint8_t status) void RF24::print_observe_tx(uint8_t value) { - printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\n\r"), + printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), value, (value >> PLOS_CNT) & B1111, (value >> ARC_CNT) & B1111 @@ -228,10 +236,10 @@ void RF24::print_observe_tx(uint8_t value) void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR("%S\t%c ="),name,extra_tab); + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); while (qty--) printf_P(PSTR(" 0x%02x"),read_register(reg++)); - printf_P(PSTR("\n\r")); + printf_P(PSTR("\r\n")); } /****************************************************************************/ @@ -239,7 +247,7 @@ void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR("%S\t%c ="),name,extra_tab); + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); while (qty--) { @@ -252,7 +260,7 @@ void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) printf_P(PSTR("%02x"),*bufptr); } - printf_P(PSTR("\n\r")); + printf_P(PSTR("\r\n")); } /****************************************************************************/ @@ -313,10 +321,10 @@ void RF24::printDetails(void) const char * rf24_crclength_e_str[] = { "Disabled", "8 bits", "16 bits" } ; const char * rf24_pa_dbm_e_str[] = { "PA_MIN", "PA_LOW", "LA_MED", "PA_HIGH"} ; - printf_P(PSTR("Data Rate\t = %s\n\r"),rf24_datarate_e_str[getDataRate()]); - printf_P(PSTR("Model\t\t = %s\n\r"),rf24_model_e_str[isPVariant()]); - printf_P(PSTR("CRC Length\t = %s\n\r"),rf24_crclength_e_str[getCRCLength()]); - printf_P(PSTR("PA Power\t = %s\n\r"),rf24_pa_dbm_e_str[getPALevel()]); + printf_P(PSTR("Data Rate\t = %s\r\n"),rf24_datarate_e_str[getDataRate()]); + printf_P(PSTR("Model\t\t = %s\r\n"),rf24_model_e_str[isPVariant()]); + printf_P(PSTR("CRC Length\t = %s\r\n"),rf24_crclength_e_str[getCRCLength()]); + printf_P(PSTR("PA Power\t = %s\r\n"),rf24_pa_dbm_e_str[getPALevel()]); } /****************************************************************************/ @@ -470,7 +478,7 @@ bool RF24::write( const void* buf, uint8_t len ) bool tx_ok, tx_fail; whatHappened(tx_ok,tx_fail,ack_payload_available); - //printf("%u%u%u\n\r",tx_ok,tx_fail,ack_payload_available); + //printf("%u%u%u\r\n",tx_ok,tx_fail,ack_payload_available); result = tx_ok; IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); @@ -670,7 +678,7 @@ void RF24::enableDynamicPayloads(void) write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); } - IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); // Enable dynamic payload on all pipes // @@ -699,7 +707,7 @@ void RF24::enableAckPayload(void) write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); } - IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); // // Enable dynamic payload on pipes 0 & 1 From 53cb579400a76db6583ee973332c6c6d17a7f7d5 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 10 Dec 2011 15:52:33 -0800 Subject: [PATCH 163/326] Moved environment-specific checking out to RF24_config.h --- RF24.cpp | 28 +---------------------- RF24.h | 3 +-- RF24_config.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 29 deletions(-) create mode 100644 RF24_config.h diff --git a/RF24.cpp b/RF24.cpp index c6edc1a..574910b 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -6,37 +6,11 @@ version 2 as published by the Free Software Foundation. */ -#if ARDUINO < 100 -#include -#else -#include -#endif - #include #include "nRF24L01.h" +#include "RF24_config.h" #include "RF24.h" -#undef SERIAL_DEBUG -#ifdef SERIAL_DEBUG -#define IF_SERIAL_DEBUG(x) ({x;}) -#else -#define IF_SERIAL_DEBUG(x) -#endif - -// Avoid spurious warnings -#if ! defined( NATIVE ) && defined( ARDUINO ) -#undef PROGMEM -#define PROGMEM __attribute__(( section(".progmem.data") )) -#undef PSTR -#define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) -#endif - -#ifdef ARDUINO -#define PRIPSTR "%S" -#else -#define PRIPSTR "%s" -#endif - /****************************************************************************/ void RF24::csn(int mode) diff --git a/RF24.h b/RF24.h index 9af5844..7d041ce 100644 --- a/RF24.h +++ b/RF24.h @@ -9,8 +9,7 @@ #ifndef __RF24_H__ #define __RF24_H__ -#include -#include +#include typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; diff --git a/RF24_config.h b/RF24_config.h new file mode 100644 index 0000000..321a0fd --- /dev/null +++ b/RF24_config.h @@ -0,0 +1,61 @@ + +/* + Copyright (C) 2011 James Coliz, Jr. + + 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 __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ + +#if ARDUINO < 100 +#include +#else +#include +#endif + +#include + +// Stuff that is normally provided by Arduino +#ifndef ARDUINO +#include +#include +#include +extern HardwareSPI SPI; +#define _BV(x) (1<<(x)) +#endif + +#undef SERIAL_DEBUG +#ifdef SERIAL_DEBUG +#define IF_SERIAL_DEBUG(x) ({x;}) +#else +#define IF_SERIAL_DEBUG(x) +#endif + +// Avoid spurious warnings +#if ! defined( NATIVE ) && defined( ARDUINO ) +#undef PROGMEM +#define PROGMEM __attribute__(( section(".progmem.data") )) +#undef PSTR +#define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) +#endif + +// Progmem is Arduino-specific +#ifdef ARDUINO +#include +#define PRIPSTR "%S" +#else +typedef char const prog_char; +typedef uint16_t prog_uint16_t; +#define PSTR(x) (x) +#define printf_P printf +#define strlen_P strlen +#define PROGMEM +#define pgm_read_word(p) (*(p)) +#define PRIPSTR "%s" +#endif + +#endif // __RF24_CONFIG_H__ +// vim:ai:cin:sts=2 sw=2 ft=cpp From a6bf911f5a99253c7b718d4c119f5a65d4f59d59 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 10 Dec 2011 15:54:11 -0800 Subject: [PATCH 164/326] Added example for Maple --- examples/pingpair_maple/Jamfile | 182 ++++++++++++++++ examples/pingpair_maple/main.cpp | 87 ++++++++ examples/pingpair_maple/pingpair_maple.pde | 240 +++++++++++++++++++++ 3 files changed, 509 insertions(+) create mode 100644 examples/pingpair_maple/Jamfile create mode 100644 examples/pingpair_maple/main.cpp create mode 100644 examples/pingpair_maple/pingpair_maple.pde diff --git a/examples/pingpair_maple/Jamfile b/examples/pingpair_maple/Jamfile new file mode 100644 index 0000000..798096c --- /dev/null +++ b/examples/pingpair_maple/Jamfile @@ -0,0 +1,182 @@ +MCU = cortex-m3 ; +CHIP = STM32F103ZE ; +BOARD = maple_native ; + +#CHIP = at91sam3u4 ; +#BOARD = sam3u-ek ; + +if ! $(TOOLSET) +{ + TOOLSET = devkit ; + Echo "Assuming TOOLSET=devkit" ; +} + +if $(TOOLSET) = yagarto +{ + TOOLS_PATH = ~/Source/yagarto-4.6.2/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} +if $(TOOLSET) = yagarto-install +{ + TOOLS_PATH = ~/Source/yagarto/install/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} +else if $(TOOLSET) = devkit +{ + TOOLS_PATH = /opt/devkitARM/bin ; + TOOLS_ARCH = arm-eabi- ; +} +else if $(TOOLSET) = maple +{ + TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} +else if $(TOOLSET) = ports +{ + TOOLS_PATH = /opt/local/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} + +CC = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc ; +C++ = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; +AS = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc -c ; +LINK = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; +OBJCOPY = $(TOOLS_PATH)/$(TOOLS_ARCH)objcopy ; +DFU = dfu-util ; + +DEFINES += VECT_TAB_FLASH BOARD_$(BOARD) MCU_$(CHIP) ERROR_LED_PORT=GPIOC ERROR_LED_PIN=15 STM32_HIGH_DENSITY MAPLE_IDE ; +OPTIM = -Os ; +MFLAGS = cpu=$(MCU) thumb arch=armv7-m ; +CCFLAGS = -Wall -m$(MFLAGS) -g -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections ; +C++FLAGS = $(CCFLAGS) -fno-rtti -fno-exceptions ; +LINKFLAGS += -m$(MFLAGS) -Xlinker --gc-sections ; +DFUFLAGS = -a1 -d 0x1eaf:0x0003 -R ; + +MAPLE_DIR = $(HOME)/Source/SAM3U/libmaple ; +MAPLE_LIBS = Servo LiquidCrystal Wire FreeRTOS ; +MAPLE_SUBDIRS = wirish wirish/comm wirish/boards libmaple libmaple/usb libmaple/usb/usb_lib ; + +SKETCH_DIR = $(HOME)/Source/Arduino ; +SKETCH_LIBS = RF24 ; + +MODULE_DIRS = . $(MAPLE_DIR)/$(MAPLE_SUBDIRS) $(MAPLE_DIR)/libraries/$(MAPLE_LIBS) $(SKETCH_DIR)/libraries/$(SKETCH_LIBS) ; +HDRS = $(MODULE_DIRS) ; +LOCATE_TARGET = out/$(TOOLSET) ; +LOCATE_SOURCE = $(LOCATE_TARGET) ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex $(>) $(<) +} + +rule Binary +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends binary : $(<) ; + Clean clean : $(<) ; +} + +actions Binary +{ + $(OBJCOPY) -O binary $(>) $(<) +} + +rule UserObject +{ + switch $(>:S) + { + case .S : As $(<) : $(>) ; + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Upload +{ + Depends up : $(<) ; + NotFile up ; + Always $(<) ; + Always up ; +} + +actions Upload +{ + $(DFU) $(DFUFLAGS) -D $(<) +} + +# Override base objects rule, so all output can go in the output dir +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +# Override base main rule, so all output can go in the output dir +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +# Modules +MODULES = [ GLOB $(MODULE_DIRS) : *.pde *.c *.cpp *.S ] ; + +# Main output executable +MAIN = $(PWD:B).elf ; + +# Linker script +LINK_DIR = $(MAPLE_DIR)/support/ld ; +LINKSCRIPT = $(LINK_DIR)/$(BOARD)/flash.ld ; + +# Bring in the map and link script +LINKFLAGS += -Wl,-Map=$(LOCATE_TARGET)/$(MAIN:B).map -T$(LINKSCRIPT) -L$(LINK_DIR) ; + +Main $(MAIN) : $(MODULES) ; +Binary $(MAIN:B).bin : $(MAIN) ; +Upload $(MAIN:B).bin ; diff --git a/examples/pingpair_maple/main.cpp b/examples/pingpair_maple/main.cpp new file mode 100644 index 0000000..b4f976d --- /dev/null +++ b/examples/pingpair_maple/main.cpp @@ -0,0 +1,87 @@ +#ifdef MAPLE_IDE + +#include +#include "wirish.h" + +extern void setup(void); +extern void loop(void); + +void board_start(const char* program_name) +{ + // Set up the LED to steady on + pinMode(BOARD_LED_PIN, OUTPUT); + digitalWrite(BOARD_LED_PIN, HIGH); + + // Setup the button as input + pinMode(BOARD_BUTTON_PIN, INPUT); + digitalWrite(BOARD_BUTTON_PIN, HIGH); + + SerialUSB.begin(); + SerialUSB.println("Press BUT"); + + // Wait for button press + while ( !isButtonPressed() ) + { + } + + SerialUSB.println("Welcome!"); + SerialUSB.println(program_name); + + int i = 11; + while (i--) + { + toggleLED(); + delay(50); + } +} + +/** + * Custom version of _write, which will print to the USB. + * In order to use it you MUST ADD __attribute__((weak)) + * to _write in libmaple/syscalls.c +*/ +extern "C" int _write (int file, char * ptr, int len) +{ + if ( (file != 1) && (file != 2) ) + return 0; + else + SerialUSB.write(ptr,len); + return len; +} + +/** + * Re-entrant version of _write. Yagarto and Devkit now use + * the re-entrant newlib, so these get called instead of the + * non_r versions. + */ +extern "C" int _write_r (void*, int file, char * ptr, int len) +{ + return _write( file, ptr, len); +} + +__attribute__((constructor)) __attribute__ ((weak)) void premain() +{ + init(); +} + +__attribute__((weak)) void setup(void) +{ + board_start("No program defined"); +} + +__attribute__((weak)) void loop(void) +{ +} + +__attribute__((weak)) int main(void) +{ + setup(); + + while (true) + { + loop(); + } + return 0; +} +#endif // ifdef MAPLE_IDE +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_maple/pingpair_maple.pde b/examples/pingpair_maple/pingpair_maple.pde new file mode 100644 index 0000000..a992570 --- /dev/null +++ b/examples/pingpair_maple/pingpair_maple.pde @@ -0,0 +1,240 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Example RF Radio Ping Pair ... for Maple + * + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +#include "WProgram.h" +#include +#include "nRF24L01.h" +#include "RF24.h" + +// +// Maple specific setup. Other than this section, the sketch is the same on Maple as on +// Arduino +// + +#ifdef MAPLE_IDE + +// External startup function +extern void board_start(const char* program_name); + +// Use SPI #2. +HardwareSPI SPI(2); + +#else +#define board_startup printf +#define toggleLED(x) (x) +#endif + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(7,6); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + board_start("\n\rRF24/examples/pingpair/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // optionally, reduce the payload size. seems to + // improve reliability + radio.setPayloadSize(8); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + toggleLED(); + + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (ok) + printf("ok...\r\n"); + else + printf("failed.\r\n"); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\r\n"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\r\n",got_time,millis()-got_time); + } + + toggleLED(); + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + + // Delay just a little bit to let the other unit + // make the transition to receiver + delay(20); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\r\n"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp From 840ad1cd3dc5ce1d23c0d070ef14343cd4ddaf40 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 14 Dec 2011 18:35:08 -0800 Subject: [PATCH 165/326] Clean up pin assignments --- examples/pingpair_maple/pingpair_maple.pde | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/pingpair_maple/pingpair_maple.pde b/examples/pingpair_maple/pingpair_maple.pde index a992570..24f5621 100644 --- a/examples/pingpair_maple/pingpair_maple.pde +++ b/examples/pingpair_maple/pingpair_maple.pde @@ -42,13 +42,15 @@ HardwareSPI SPI(2); // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 7 & 6 +// (This works for the Getting Started board plugged into the +// Maple Native backwards.) RF24 radio(7,6); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter -const int role_pin = 7; +const int role_pin = 10; // // Topology From 19fa3954c0d452f43ef9b8e4b8afca47d8b98a41 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 18 Dec 2011 10:54:47 -0800 Subject: [PATCH 166/326] Added new jamfile to this example --- examples/GettingStarted/Jamfile | 379 ++++++++++++++++---------------- 1 file changed, 185 insertions(+), 194 deletions(-) diff --git a/examples/GettingStarted/Jamfile b/examples/GettingStarted/Jamfile index 9e113df..6a2499a 100644 --- a/examples/GettingStarted/Jamfile +++ b/examples/GettingStarted/Jamfile @@ -1,219 +1,210 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; +# (1) Project Information +PROJECT_LIBS = SPI RF24 ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_SPEED ?= 57600 ; +MCU ?= atmega328p ; F_CPU ?= 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; -OUT_DIR = $(F_CPU) ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. -# Host-specific overrides for locations if $(OS) = MACOSX { -ARDUINO_VERSION = 100 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN = /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; } -# Where is everything? -ARDUINO_VERSION ?= 100 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino $(ARDUINO_DIR)/hardware/arduino/variants/standard ; +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; # Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +# +# Targets +# # Grab everything from the core directory CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; # Grab everything from libraries. To avoid this "grab everything" behaviour, you # can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; +# Grab everything from the current dir +PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; +# Main output executable +MAIN = $(PWD:B).elf ; -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; +Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +Hex $(MAIN:B).hex : $(MAIN) ; -rule GitVersion +# Upload targets +for _p in $(PORTS) { - Always $(<) ; - Depends all : $(<) ; + Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; } - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - actions Pde - { - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) - } -} -else -{ - actions Pde - { - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) - } -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - case .ino : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - From 26ea4583cdebbc219b5f4460096044d911874e9d Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 18 Dec 2011 10:56:12 -0800 Subject: [PATCH 167/326] Added #error to printf for examples that require Arduino --- examples/GettingStarted/printf.h | 8 +- examples/led_remote/printf.h | 8 +- examples/pingpair/Jamfile | 368 +++++++++++++++--------------- examples/pingpair/pingpair.pde | 2 +- examples/pingpair/printf.h | 8 +- examples/pingpair_dyn/printf.h | 8 +- examples/pingpair_irq/printf.h | 8 +- examples/pingpair_pl/printf.h | 8 +- examples/pingpair_sleepy/printf.h | 8 +- examples/starping/printf.h | 8 +- 10 files changed, 243 insertions(+), 191 deletions(-) diff --git a/examples/GettingStarted/printf.h b/examples/GettingStarted/printf.h index 1b853db..b2efd56 100644 --- a/examples/GettingStarted/printf.h +++ b/examples/GettingStarted/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/examples/led_remote/printf.h b/examples/led_remote/printf.h index 1b853db..b2efd56 100644 --- a/examples/led_remote/printf.h +++ b/examples/led_remote/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/examples/pingpair/Jamfile b/examples/pingpair/Jamfile index 901f8da..7f24589 100644 --- a/examples/pingpair/Jamfile +++ b/examples/pingpair/Jamfile @@ -1,206 +1,210 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; +# (1) Project Information -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; +PROJECT_LIBS = SPI RF24 ; -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; +# (2) Board Information + +UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_SPEED ?= 115200 ;; +MCU = atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. -# Host-specific overrides for locations if $(OS) = MACOSX { -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN = /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; } -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; # Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +# +# Targets +# # Grab everything from the core directory CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; # Grab everything from libraries. To avoid this "grab everything" behaviour, you # can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; +# Grab everything from the current dir +PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; +# Main output executable +MAIN = $(PWD:B).elf ; -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; +Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +Hex $(MAIN:B).hex : $(MAIN) ; -rule GitVersion +# Upload targets +for _p in $(PORTS) { - Always $(<) ; - Depends all : $(<) ; + Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; } - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 3cedd66..5f64005 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -70,7 +70,7 @@ void setup(void) delay(20); // Just to get a solid reading on the role pin // read the address pin, establish our role - if ( digitalRead(role_pin) ) + if ( ! digitalRead(role_pin) ) role = role_ping_out; else role = role_pong_back; diff --git a/examples/pingpair/printf.h b/examples/pingpair/printf.h index 1b853db..b2efd56 100644 --- a/examples/pingpair/printf.h +++ b/examples/pingpair/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/examples/pingpair_dyn/printf.h b/examples/pingpair_dyn/printf.h index 1b853db..b2efd56 100644 --- a/examples/pingpair_dyn/printf.h +++ b/examples/pingpair_dyn/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/examples/pingpair_irq/printf.h b/examples/pingpair_irq/printf.h index 1b853db..b2efd56 100644 --- a/examples/pingpair_irq/printf.h +++ b/examples/pingpair_irq/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/examples/pingpair_pl/printf.h b/examples/pingpair_pl/printf.h index 1b853db..b2efd56 100644 --- a/examples/pingpair_pl/printf.h +++ b/examples/pingpair_pl/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/examples/pingpair_sleepy/printf.h b/examples/pingpair_sleepy/printf.h index 1b853db..b2efd56 100644 --- a/examples/pingpair_sleepy/printf.h +++ b/examples/pingpair_sleepy/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/examples/starping/printf.h b/examples/starping/printf.h index 1b853db..b2efd56 100644 --- a/examples/starping/printf.h +++ b/examples/starping/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,6 +16,8 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ +#ifdef ARDUINO + int serial_putc( char c, FILE * ) { Serial.write( c ); @@ -28,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ From 50b18a9c7dc39bd27db7405567b7d27f338100ff Mon Sep 17 00:00:00 2001 From: maniacbug Date: Mon, 19 Dec 2011 06:19:25 -0800 Subject: [PATCH 168/326] Reduce delays in startWrite down to only required delays --- RF24.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 574910b..c25a869 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -481,7 +481,7 @@ void RF24::startWrite( const void* buf, uint8_t len ) { // Transmitter power-up write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); - delay(2); + delayMicroseconds(150); // Send the payload write_payload( buf, len ); @@ -489,7 +489,6 @@ void RF24::startWrite( const void* buf, uint8_t len ) // Allons! ce(HIGH); delayMicroseconds(15); - delay(2); ce(LOW); } From 8493b9a60e694d9824bf3a91c30c5d9e9e2094f8 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:32:21 -0800 Subject: [PATCH 169/326] Correct the comment for pin #'s --- examples/led_remote/led_remote.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index 02f89f5..5219a37 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -34,7 +34,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From a70da53c22540ba5d98f26f252c95156de89d1ee Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:32:45 -0800 Subject: [PATCH 170/326] Correct the comment for pin #'s --- examples/pingpair/pingpair.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index 5f64005..b9d74ac 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -24,7 +24,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From 6d25fbebfdec0d91ccf481a3bf46013717aa9ed9 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:33:17 -0800 Subject: [PATCH 171/326] Correct the comment for pin #'s --- examples/pingpair_dyn/pingpair_dyn.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde index cea56ee..eb7c594 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -21,7 +21,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From 332fd03192b0e8a499b28f98d33ea8401f6b626e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:33:40 -0800 Subject: [PATCH 172/326] Correct the comment for pin #'s --- examples/pingpair_irq/pingpair_irq.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index 2c4f583..dbfdb99 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -22,7 +22,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From 6a7c42bdeb0a878d3a3267453225bde967cabed5 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:38:56 -0800 Subject: [PATCH 173/326] Correct the comment for pin #'s --- examples/pingpair_pl/pingpair_pl.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index 94a0da5..0f97e36 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -24,7 +24,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From 0f9b17269701a4bcb512e9a6f8568ca761d1140a Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:44:55 -0800 Subject: [PATCH 174/326] Correct the comment for pin #'s --- examples/pingpair_sleepy/pingpair_sleepy.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index 809679e..68d53b7 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -31,7 +31,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From c1f71ebcb7b095b56d3ab786e210350d323c8e27 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:45:25 -0800 Subject: [PATCH 175/326] Correct the comment for pin #'s --- examples/scanner/scanner.pde | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.pde index 6e74286..4f8fc92 100644 --- a/examples/scanner/scanner.pde +++ b/examples/scanner/scanner.pde @@ -1,4 +1,3 @@ - /* Copyright (C) 2011 James Coliz, Jr. @@ -27,7 +26,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From de994ba5efb207886ef274c5be1f78e52a961c6e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 20 Dec 2011 15:45:50 -0800 Subject: [PATCH 176/326] Correct the comment for pin #'s --- examples/starping/starping.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index 97b0953..9b76ee9 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -31,7 +31,7 @@ // Hardware configuration // -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); From 5cb32c92cc709c2fc86508864fbdb014f5ab80c5 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 21 Dec 2011 08:32:57 -0800 Subject: [PATCH 177/326] Handle SPI.h better in Maple --- RF24.cpp | 1 - RF24_config.h | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 574910b..2d289c0 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -6,7 +6,6 @@ version 2 as published by the Free Software Foundation. */ -#include #include "nRF24L01.h" #include "RF24_config.h" #include "RF24.h" diff --git a/RF24_config.h b/RF24_config.h index 321a0fd..4f777da 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -19,7 +19,9 @@ #include // Stuff that is normally provided by Arduino -#ifndef ARDUINO +#ifdef ARDUINO +#include +#else #include #include #include From fc067607364c02b38474bb7a2f2ae778cd86fb1a Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 21 Dec 2011 10:17:16 -0800 Subject: [PATCH 178/326] Documentation update. 1.0 fully supported, Maple works, more examples, links and pictures for proejcts --- Doxyfile | 2 +- FAQ | 34 +- RF24.h | 55 ++- doxygen-custom.css | 835 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 900 insertions(+), 26 deletions(-) create mode 100644 doxygen-custom.css diff --git a/Doxyfile b/Doxyfile index 2bd1ea0..4604afc 100644 --- a/Doxyfile +++ b/Doxyfile @@ -800,7 +800,7 @@ HTML_FOOTER = # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! -HTML_STYLESHEET = +HTML_STYLESHEET = doxygen-custom.css # 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 diff --git a/FAQ b/FAQ index d134bb8..343d874 100644 --- a/FAQ +++ b/FAQ @@ -7,39 +7,35 @@ * * @ref range * + * @ref issues + * * @ref tests * * @section starting Where do I start? * - * Start with the pingpair example. Follow the instructions in the comments - * of that sketch. + * See my blog post: + * Getting Started with nRF24L01+ on Arduino * * @section hardware Where can I buy some hardware? * - * I've been using modules from mdfly.com. - * - * Mostly these units as the workhorse of the system: - * - * @li 2.4Ghz Wireless nRF24L01 Transceiver Module $6.95 - * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=82 - * - * And then also experimenting with these for greater range - * - * @li nRF24L01 2.4GHz Transceiver Module w/ Power Amplifier $12.95 - * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=394 - * @li 2.4GHz Transceiver Module w/ Power Amplifier $19.95 - * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=583 - * - * However, I also noticed that iTeadStudio has nRF24L01+ units for $4! + * @li iTeadStudio sells the basic 2.4G Wireless nRF24L01+ Module for $4. Such a deal! + * @li MDfly.com sells the same unit, 2.4Ghz Wireless nRF24L01+ Transceiver Module for $6.95, but it ships from the US so it gets there a lot faster. Great place to get a few units and get started quickly. + * @li MDfly.com also has the nRF24L01 2.4GHz Transceiver Module w/ Power Amplifier for $13.95, which increases range dramatically and uses a chip antenna + * @li MDfly.com also has the 2.4GHz Transceiver Module w/ Power Amplifier with an external antenna for $19.95 * * @section range What is the range of these units? * - * Here are some results from measurements I have taken. I recommend that everyone - * take their own measurements in their particular circumstances. + * Here are some results from measurements I have taken, using the basic $4 iTeadStudio units. + * I recommend that everyone take their own measurements in their particular circumstances. * * @li non-plus unit, 2MBps (worst case), 41+ ft line of sight indoors, immediate dropoff with any deviation from LOS. (41 ft is as far as I can go in my house without turning a corner) * @li Plus unit, 250kbps (best case), 46 ft around two corners indoors, 49 ft around one corner. More importantly, at 250k, packet loss is almost negligible through almost all of that range. * @li Both units at 1MBps, plus unit gets about 10% range improvement over non-plus in almost all situations. + * + * @section issues What should I do if I find a problem? + * + * Please open an issue on github if you find any problems using it with any version of Arduino or Maple. + * * * @section tests Why are the examples in the 'tests' directory failing? * diff --git a/RF24.h b/RF24.h index f2b36a6..6408d44 100644 --- a/RF24.h +++ b/RF24.h @@ -620,8 +620,8 @@ public: /** * @example GettingStarted.pde * - * This is an example which corresponds to my "Getting Started" blog post - * for Getting Started with nRF24L01+ radios. + * This is an example which corresponds to my "Getting Started" blog post: + * Getting Started with nRF24L01+ on Arduino. * * It is an example of how to use the RF24 class. Write this sketch to two * different nodes. Put one of the nodes into 'transmit' mode by connecting @@ -649,6 +649,21 @@ public: * The ping node can then see how long the whole cycle took. */ +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + /** * @example starping.pde * @@ -703,15 +718,26 @@ public: /** * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver * - * Design Goals: This library is designed to be... + * @section Goals Design Goals + * + * This library is designed to be... * @li Maximally compliant with the intended operation of the chip * @li Easy for beginners to use * @li Consumed with a public interface that's similiar to other Arduino standard libraries * - * NOW COMPATIBLE WITH ARDUINO 1.0 - Please use the - * 'arduino-1.0' branch - * for use with Arduino 1.0. This will be merged into master when Arduino 1.0 is released. + * @section News News + * + * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. + * Please open an issue if you find any problems using it with any version of Arduino. * + * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the + * Maple Native, + * and should work with any Maple board. See the pingpair_maple example. + * Note that only the pingpair_maple example has been tested on Maple, although + * the others can certainly be adapted. + * + * @section Useful Useful References + * * Please refer to: * * @li Documentation Main Page @@ -726,6 +752,23 @@ public: * @section More More Information * * @subpage FAQ + * + * @section Projects Projects + * + * Stuff I have built with RF24 + * + * + * RF24 Getting Started - Finished Product + * + * Getting Started with nRF24L01+ on Arduino + * + * RF Duinode V3 (2V4) + * + * Low-Power Wireless Sensor Node + * + * nRF24L01+ connected to Leaf Labs Maple Native + * + * nRF24L01+ Running on Maple */ #endif // __RF24_H__ diff --git a/doxygen-custom.css b/doxygen-custom.css new file mode 100644 index 0000000..d7d0e12 --- /dev/null +++ b/doxygen-custom.css @@ -0,0 +1,835 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 12px; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + padding: 2px; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code { + color: #4665A2; +} + +a.codeRef { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 10px; + margin-right: 5px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + +} + +.memdoc { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 2px 5px; + background-color: #FBFCFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: left; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + From 07a4bcf425d91c99105dbdbad0226296c7cd3a93 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 24 Dec 2011 10:07:16 -0800 Subject: [PATCH 179/326] Use latest copyright --- RF24.cpp | 2 +- RF24.h | 2 +- RF24_config.h | 2 +- examples/led_remote/led_remote.pde | 2 +- examples/pingpair/pingpair.pde | 2 +- examples/pingpair_dyn/pingpair_dyn.pde | 2 +- examples/pingpair_irq/pingpair_irq.pde | 2 +- examples/pingpair_maple/pingpair_maple.pde | 2 +- examples/pingpair_pl/pingpair_pl.pde | 2 +- examples/pingpair_sleepy/pingpair_sleepy.pde | 2 +- examples/scanner/printf.h | 2 +- examples/scanner/scanner.pde | 2 +- examples/starping/starping.pde | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 3294cc9..62b4be7 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/RF24.h b/RF24.h index 6408d44..3b90d94 100644 --- a/RF24.h +++ b/RF24.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/RF24_config.h b/RF24_config.h index 4f777da..fc6d696 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -1,6 +1,6 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index 5219a37..7aeeb3b 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.pde index b9d74ac..3a57a67 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.pde index eb7c594..7108f3b 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index dbfdb99..173f5fe 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/pingpair_maple/pingpair_maple.pde b/examples/pingpair_maple/pingpair_maple.pde index 24f5621..2d3925b 100644 --- a/examples/pingpair_maple/pingpair_maple.pde +++ b/examples/pingpair_maple/pingpair_maple.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde index 0f97e36..70aed6e 100644 --- a/examples/pingpair_pl/pingpair_pl.pde +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde index 68d53b7..49daa69 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ b/examples/pingpair_sleepy/pingpair_sleepy.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/scanner/printf.h b/examples/scanner/printf.h index 1b853db..66f6438 100644 --- a/examples/scanner/printf.h +++ b/examples/scanner/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.pde index 4f8fc92..1a43d72 100644 --- a/examples/scanner/scanner.pde +++ b/examples/scanner/scanner.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/examples/starping/starping.pde b/examples/starping/starping.pde index 9b76ee9..4813a77 100644 --- a/examples/starping/starping.pde +++ b/examples/starping/starping.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License From 76f79e62ed1fa28887019aca9505196926bc9a3e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 24 Dec 2011 10:09:46 -0800 Subject: [PATCH 180/326] Fix bug in pingpair Jamfile --- examples/pingpair/Jamfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pingpair/Jamfile b/examples/pingpair/Jamfile index 7f24589..0fc425f 100644 --- a/examples/pingpair/Jamfile +++ b/examples/pingpair/Jamfile @@ -5,7 +5,7 @@ PROJECT_LIBS = SPI RF24 ; # (2) Board Information UPLOAD_PROTOCOL ?= stk500v1 ; -UPLOAD_SPEED ?= 115200 ;; +UPLOAD_SPEED ?= 115200 ; MCU = atmega328p ; F_CPU ?= 16000000 ; CORE ?= arduino ; From 36167a9e02fecc3d45f863083b43a619953cb98e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 4 Jan 2012 20:11:28 -0800 Subject: [PATCH 181/326] New 'nordic_fob' example started, based on pingpair --- examples/nordic_fob/Jamfile | 219 ++++++++++++++++++++++++++++ examples/nordic_fob/nordic_fob.pde | 220 +++++++++++++++++++++++++++++ examples/nordic_fob/printf.h | 37 +++++ 3 files changed, 476 insertions(+) create mode 100644 examples/nordic_fob/Jamfile create mode 100644 examples/nordic_fob/nordic_fob.pde create mode 100644 examples/nordic_fob/printf.h diff --git a/examples/nordic_fob/Jamfile b/examples/nordic_fob/Jamfile new file mode 100644 index 0000000..f26d4a4 --- /dev/null +++ b/examples/nordic_fob/Jamfile @@ -0,0 +1,219 @@ +# (1) Project Information + +PROJECT_LIBS = RF24 SPI ; +PROJECT_DIRS = $(PWD) ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_SPEED ?= 115200 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. + +if $(OS) = MACOSX +{ + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN = /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; +} + +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; + +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; + +# Search everywhere for headers + +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Library +{ + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +rule Arduino +{ + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : core libs ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } +} + +# +# Targets +# + +# Grab everything from the core directory +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/examples/nordic_fob/nordic_fob.pde b/examples/nordic_fob/nordic_fob.pde new file mode 100644 index 0000000..3a57a67 --- /dev/null +++ b/examples/nordic_fob/nordic_fob.pde @@ -0,0 +1,220 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(9,10); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( ! digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // optionally, reduce the payload size. seems to + // improve reliability + radio.setPayloadSize(8); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (ok) + printf("ok..."); + else + printf("failed.\n\r"); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + + // Delay just a little bit to let the other unit + // make the transition to receiver + delay(20); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/nordic_fob/printf.h b/examples/nordic_fob/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/examples/nordic_fob/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ From d1653e8cad74daf6fa4fa60a5646804592ee8290 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 4 Jan 2012 21:00:15 -0800 Subject: [PATCH 182/326] nordic_fob example fully working. --- examples/nordic_fob/nordic_fob.pde | 215 +++++++++-------------------- 1 file changed, 68 insertions(+), 147 deletions(-) diff --git a/examples/nordic_fob/nordic_fob.pde b/examples/nordic_fob/nordic_fob.pde index 3a57a67..353013b 100644 --- a/examples/nordic_fob/nordic_fob.pde +++ b/examples/nordic_fob/nordic_fob.pde @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 J. Coliz + Copyright (C) 2012 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -7,17 +7,16 @@ */ /** - * Example RF Radio Ping Pair + * Example Nordic FOB Receiver * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. */ #include +#include #include "nRF24L01.h" -#include "RF24.h" #include "printf.h" // @@ -26,96 +25,53 @@ // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(9,10); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; +RF24 radio(8,9); // -// Topology +// Payload // -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; +struct payload_t +{ + uint8_t buttons; + uint16_t id; + uint8_t empty; +}; + +const char* button_names[] = { "Up", "Down", "Left", "Right", "Center" }; +const int num_buttons = 5; // -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin +// Forward declarations // -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; +uint16_t flip_endian(uint16_t in); -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; +// +// Setup +// void setup(void) { - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( ! digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - // // Print preamble // Serial.begin(57600); printf_begin(); - printf("\n\rRF24/examples/pingpair/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("\r\nRF24/examples/nordic_fob/\r\n"); // - // Setup and configure rf radio + // Setup and configure rf radio according to the built-in parameters + // of the FOB. // radio.begin(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); - - // optionally, reduce the payload size. seems to - // improve reliability - radio.setPayloadSize(8); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } + radio.setChannel(2); + radio.setPayloadSize(4); + radio.setAutoAck(false); + radio.setCRCLength(RF24_CRC_8); + radio.openReadingPipe(1,0xE7E7E7E7E7LL); // // Start listening @@ -130,91 +86,56 @@ void setup(void) radio.printDetails(); } +// +// Loop +// + void loop(void) { // - // Ping out role. Repeatedly send the current time + // Receive each packet, dump it out // - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); - - if (ok) - printf("ok..."); - else - printf("failed.\n\r"); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { // if there is data ready if ( radio.available() ) { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) + // Get the packet from the radio + payload_t payload; + radio.read( &payload, sizeof(payload) ); + + // Print the ID of this message. Note that the message + // is sent 'big-endian', so we have to flip it. + printf("#%05u Buttons ",flip_endian(payload.id)); + + // Print the name of each button + int i = num_buttons; + while (i--) { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - delay(20); + if ( ! ( payload.buttons & _BV(i) ) ) + { + printf("%s ",button_names[i]); + } } - // First, stop listening so we can talk - radio.stopListening(); + // If no buttons, print None + if ( payload.buttons == _BV(num_buttons) - 1 ) + printf("None"); - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); + printf("\r\n"); } - } } + +// +// Helper functions +// + +// Change a big-endian word into a little-endian +uint16_t flip_endian(uint16_t in) +{ + uint16_t low = in >> 8; + uint16_t high = in << 8; + + return high | low; +} + // vim:cin:ai:sts=2 sw=2 ft=cpp From b3f7f51510aee1699386be83995c336da8fa3c44 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 4 Jan 2012 21:20:39 -0800 Subject: [PATCH 183/326] docupdate --- RF24.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/RF24.h b/RF24.h index 3b90d94..8e5ca21 100644 --- a/RF24.h +++ b/RF24.h @@ -6,13 +6,36 @@ version 2 as published by the Free Software Foundation. */ +/** + * @file RF24.h + * + * Class declaration for RF24 and helper enums + */ + #ifndef __RF24_H__ #define __RF24_H__ #include +/** + * Power Amplifier level. + * + * For use with setPALevel() + */ typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; + +/** + * Data rate. How fast data moves through the air. + * + * For use with setDataRate() + */ typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +/** + * CRC Length. How big (if any) of a CRC is included. + * + * For use with setCRCLength() + */ typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; /** @@ -630,6 +653,14 @@ public: * node can then see how long the whole cycle took. */ +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + /** * @example led_remote.pde * From 33e59af725074afd19e0e70f08c0711779f38b4c Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 4 Jan 2012 21:21:02 -0800 Subject: [PATCH 184/326] Fix Jamfile for proper ordering of libraries --- examples/nordic_fob/Jamfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nordic_fob/Jamfile b/examples/nordic_fob/Jamfile index f26d4a4..ec519f7 100644 --- a/examples/nordic_fob/Jamfile +++ b/examples/nordic_fob/Jamfile @@ -67,7 +67,7 @@ AVRDUDE = $(AVR_BIN)/avrdude ; DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; @@ -196,7 +196,7 @@ rule Arduino { LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; Main $(<) : $(>) ; - LinkLibraries $(<) : core libs ; + LinkLibraries $(<) : libs core ; Hex $(<:B).hex : $(<) ; for _p in $(PORTS) { From cb6613d62fdc7f68327811d5cb2e36821b55c695 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Wed, 4 Jan 2012 21:23:15 -0800 Subject: [PATCH 185/326] Really...Pins 9 & 10 this time. I mean it\! --- examples/nordic_fob/nordic_fob.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/nordic_fob/nordic_fob.pde b/examples/nordic_fob/nordic_fob.pde index 353013b..eb9ca65 100644 --- a/examples/nordic_fob/nordic_fob.pde +++ b/examples/nordic_fob/nordic_fob.pde @@ -25,7 +25,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(8,9); +RF24 radio(9,10); // // Payload From e31f8c8211c82a8984bc7a1e2dfa098d99108a71 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 8 Jan 2012 08:30:01 -0800 Subject: [PATCH 186/326] Update docs and comments for new nordic fob blog post --- RF24.h | 5 ++++- examples/nordic_fob/nordic_fob.pde | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/RF24.h b/RF24.h index 8e5ca21..23a5bba 100644 --- a/RF24.h +++ b/RF24.h @@ -788,11 +788,14 @@ public: * * Stuff I have built with RF24 * - * * RF24 Getting Started - Finished Product * * Getting Started with nRF24L01+ on Arduino * + * Nordic FOB and nRF24L01+ + * + * Using the Sparkfun Nordic FOB + * * RF Duinode V3 (2V4) * * Low-Power Wireless Sensor Node diff --git a/examples/nordic_fob/nordic_fob.pde b/examples/nordic_fob/nordic_fob.pde index eb9ca65..5a316a0 100644 --- a/examples/nordic_fob/nordic_fob.pde +++ b/examples/nordic_fob/nordic_fob.pde @@ -10,8 +10,9 @@ * Example Nordic FOB Receiver * * This is an example of how to use the RF24 class to receive signals from the - * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . - * Thanks to Kirk Mower for providing test hardware. + * Sparkfun Nordic FOB. Thanks to Kirk Mower for providing test hardware. + * + * See blog post at http://maniacbug.wordpress.com/2012/01/08/nordic-fob/ */ #include From eb3b2d800a240ce097e45423816dc0db9b423ef2 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 Jan 2012 22:05:44 -0800 Subject: [PATCH 187/326] Reduce RAM footprint by moving static const data to progmem, and refactoring out switch statements. --- RF24.cpp | 183 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 103 insertions(+), 80 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 62b4be7..2b9048d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -273,6 +273,39 @@ uint8_t RF24::getPayloadSize(void) /****************************************************************************/ +static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; +static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; +static const char rf24_datarate_e_str_2[] PROGMEM = "250KBPS"; +static const char * const rf24_datarate_e_str_P[] PROGMEM = { + rf24_datarate_e_str_0, + rf24_datarate_e_str_1, + rf24_datarate_e_str_2, +}; +static const char rf24_model_e_str_0[] PROGMEM = "nRF24L01"; +static const char rf24_model_e_str_1[] PROGMEM = "nRF24L01+"; +static const char * const rf24_model_e_str_P[] PROGMEM = { + rf24_model_e_str_0, + rf24_model_e_str_1, +}; +static const char rf24_crclength_e_str_0[] PROGMEM = "Disabled"; +static const char rf24_crclength_e_str_1[] PROGMEM = "8 bits"; +static const char rf24_crclength_e_str_2[] PROGMEM = "16 bits" ; +static const char * const rf24_crclength_e_str_P[] PROGMEM = { + rf24_crclength_e_str_0, + rf24_crclength_e_str_1, + rf24_crclength_e_str_2, +}; +static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_MED"; +static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_HIGH"; +static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { + rf24_pa_dbm_e_str_0, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + void RF24::printDetails(void) { print_status(get_status()); @@ -289,15 +322,10 @@ void RF24::printDetails(void) print_byte_register(PSTR("CONFIG"),CONFIG); print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); - const char * rf24_datarate_e_str[] = { "1MBPS", "2MBPS", "250KBPS" }; - const char * rf24_model_e_str[] = { "nRF24L01", "nRF24L01+" } ; - const char * rf24_crclength_e_str[] = { "Disabled", "8 bits", "16 bits" } ; - const char * rf24_pa_dbm_e_str[] = { "PA_MIN", "PA_LOW", "LA_MED", "PA_HIGH"} ; - - printf_P(PSTR("Data Rate\t = %s\r\n"),rf24_datarate_e_str[getDataRate()]); - printf_P(PSTR("Model\t\t = %s\r\n"),rf24_model_e_str[isPVariant()]); - printf_P(PSTR("CRC Length\t = %s\r\n"),rf24_crclength_e_str[getCRCLength()]); - printf_P(PSTR("PA Power\t = %s\r\n"),rf24_pa_dbm_e_str[getPALevel()]); + printf_P(PSTR("Data Rate\t = %S\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); + printf_P(PSTR("Model\t\t = %S\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); + printf_P(PSTR("CRC Length\t = %S\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); + printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); } /****************************************************************************/ @@ -587,21 +615,21 @@ void RF24::openWritingPipe(uint64_t value) /****************************************************************************/ +static const uint8_t child_pipe[] PROGMEM = +{ + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 +}; +static const uint8_t child_payload_size[] PROGMEM = +{ + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 +}; +static const uint8_t child_pipe_enable[] PROGMEM = +{ + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 +}; + void RF24::openReadingPipe(uint8_t child, uint64_t address) { - const uint8_t child_pipe[] = - { - RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 - }; - const uint8_t child_payload_size[] = - { - RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 - }; - const uint8_t child_pipe_enable[] = - { - ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 - }; - // If this is pipe 0, cache the address. This is needed because // openWritingPipe() will overwrite the pipe 0 address, so // startListening() will have to restore it. @@ -612,11 +640,11 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) { // For pipes 2-5, only write the LSB if ( child < 2 ) - write_register(child_pipe[child], reinterpret_cast(&address), 5); + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); else - write_register(child_pipe[child], reinterpret_cast(&address), 1); + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); - write_register(child_payload_size[child],payload_size); + write_register(pgm_read_byte(&child_payload_size[child]),payload_size); // Note it would be more efficient to set all of the bits for all open // pipes at once. However, I thought it would make the calling code @@ -770,27 +798,27 @@ void RF24::setPALevel(rf24_pa_dbm_e level) uint8_t setup = read_register(RF_SETUP) ; setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - switch( level ) + // switch uses RAM (evil!) + if ( level == RF24_PA_MAX ) { - case RF24_PA_MAX: setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - break ; - - case RF24_PA_HIGH: + } + else if ( level == RF24_PA_HIGH ) + { setup |= _BV(RF_PWR_HIGH) ; - break ; - - case RF24_PA_LOW: - setup |= _BV(RF_PWR_LOW) ; - break ; - - case RF24_PA_MIN: - break ; - - case RF24_PA_ERROR: + } + else if ( level == RF24_PA_LOW ) + { + setup |= _BV(RF_PWR_LOW); + } + else if ( level == RF24_PA_MIN ) + { + // nothing + } + else if ( level == RF24_PA_ERROR ) + { // On error, go to maximum PA setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - break ; } write_register( RF_SETUP, setup ) ; @@ -803,23 +831,22 @@ rf24_pa_dbm_e RF24::getPALevel(void) rf24_pa_dbm_e result = RF24_PA_ERROR ; uint8_t power = read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - switch( power ) + // switch uses RAM (evil!) + if ( power == (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ) { - case (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)): result = RF24_PA_MAX ; - break ; - - case _BV(RF_PWR_HIGH): + } + else if ( power == _BV(RF_PWR_HIGH) ) + { result = RF24_PA_HIGH ; - break ; - - case _BV(RF_PWR_LOW): + } + else if ( power == _BV(RF_PWR_LOW) ) + { result = RF24_PA_LOW ; - break ; - - default: + } + else + { result = RF24_PA_MIN ; - break ; } return result ; @@ -877,27 +904,25 @@ bool RF24::setDataRate(rf24_datarate_e speed) rf24_datarate_e RF24::getDataRate( void ) { rf24_datarate_e result ; - uint8_t setup = read_register(RF_SETUP) ; - + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + // switch uses RAM (evil!) // Order matters in our case below - switch( setup & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ) + if ( dr == _BV(RF_DR_LOW) ) { - case _BV(RF_DR_LOW): // '10' = 250KBPS result = RF24_250KBPS ; - break ; - - case _BV(RF_DR_HIGH): + } + else if ( dr == _BV(RF_DR_HIGH) ) + { // '01' = 2MBPS result = RF24_2MBPS ; - break ; - - default: + } + else + { // '00' = 1MBPS result = RF24_1MBPS ; - break ; } - return result ; } @@ -906,23 +931,21 @@ rf24_datarate_e RF24::getDataRate( void ) void RF24::setCRCLength(rf24_crclength_e length) { uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - - switch (length) + + // switch uses RAM (evil!) + if ( length == RF24_CRC_DISABLED ) { - case RF24_CRC_DISABLED: - break; - - case RF24_CRC_8: - config |= _BV(EN_CRC); - break; - - case RF24_CRC_16: - default: - config |= _BV(EN_CRC); - config |= _BV( CRCO ); - break; + // Do nothing, we turned it off above. + } + else if ( length == RF24_CRC_8 ) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV( CRCO ); } - write_register( CONFIG, config ) ; } From c2e956467895ffe73d705afd1b84c5c68bf65775 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 Jan 2012 22:06:27 -0800 Subject: [PATCH 188/326] Use latest Jamfile --- examples/pingpair/Jamfile | 53 +++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/examples/pingpair/Jamfile b/examples/pingpair/Jamfile index 0fc425f..18244ec 100644 --- a/examples/pingpair/Jamfile +++ b/examples/pingpair/Jamfile @@ -1,12 +1,13 @@ # (1) Project Information PROJECT_LIBS = SPI RF24 ; +PROJECT_DIRS = $(PWD) ; # (2) Board Information -UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_PROTOCOL ?= arduino ; UPLOAD_SPEED ?= 115200 ; -MCU = atmega328p ; +MCU ?= atmega328p ; F_CPU ?= 16000000 ; CORE ?= arduino ; VARIANT ?= standard ; @@ -29,13 +30,13 @@ PORT_u2 = /dev/ttyUSB2 ; if $(OS) = MACOSX { - AVR_BIN = /usr/local/avrtools/bin ; + AVR_BIN ?= /usr/local/avrtools/bin ; AVR_ETC = /usr/local/avrtools/etc ; AVR_INCLUDE = /usr/local/avrtools/include ; } else { - AVR_BIN = /usr/bin ; + AVR_BIN ?= /usr/bin ; AVR_INCLUDE = /usr/lib/avr/include ; AVR_ETC = /etc ; } @@ -57,21 +58,23 @@ SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; CC = $(AVR_BIN)/avr-gcc ; C++ = $(AVR_BIN)/avr-g++ ; LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE = $(AVR_BIN)/avrdude ; +AVRDUDE ?= $(AVR_BIN)/avrdude ; # Flags DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; # Search everywhere for headers -HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; # Output locations @@ -149,6 +152,12 @@ rule Objects } } +rule Library +{ + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + rule Main { MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; @@ -183,28 +192,28 @@ actions UploadAction $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i } +rule Arduino +{ + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : core libs ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } +} + # # Targets # # Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; # Grab everything from libraries. To avoid this "grab everything" behaviour, you # can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Grab everything from the current dir -PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; # Main output executable -MAIN = $(PWD:B).elf ; - -Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -Hex $(MAIN:B).hex : $(MAIN) ; - -# Upload targets -for _p in $(PORTS) -{ - Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; -} +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; From dec61af85fa5316ade24168006797c547dc8bbc4 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 Jan 2012 22:07:13 -0800 Subject: [PATCH 189/326] pingpair_blocking test now uses expect and runs on linux --- tests/pingpair_blocking/Jamfile | 289 +++++++++--------- tests/pingpair_blocking/pingpair_blocking.pde | 3 + tests/pingpair_blocking/printf.h | 8 +- tests/pingpair_blocking/runtest.py | 4 +- tests/pingpair_blocking/runtests.sh | 8 +- tests/pingpair_blocking/test.ex | 11 + 6 files changed, 175 insertions(+), 148 deletions(-) create mode 100755 tests/pingpair_blocking/test.ex diff --git a/tests/pingpair_blocking/Jamfile b/tests/pingpair_blocking/Jamfile index 98ec12f..6268f4b 100644 --- a/tests/pingpair_blocking/Jamfile +++ b/tests/pingpair_blocking/Jamfile @@ -1,206 +1,219 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; +# (1) Project Information -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; +PROJECT_LIBS = SPI RF24 ; +PROJECT_DIRS = $(PWD) ; -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; +# (2) Board Information + +UPLOAD_PROTOCOL ?= arduino ; +UPLOAD_SPEED ?= 57600 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. -# Host-specific overrides for locations if $(OS) = MACOSX { -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; + AVR_BIN ?= /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN ?= /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; } -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Werror -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE ?= $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; # Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; +# Output locations -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; +# +# Custom rules +# rule GitVersion { - Always $(<) ; - Depends all : $(<) ; + Always $(<) ; + Depends all : $(<) ; } actions GitVersion { - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) } GitVersion version.h ; -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - rule Pde { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; } actions Pde { - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) } -rule AvrPde +rule C++Pde { - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; } -rule AvrObject +rule UserObject { - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } } -rule AvrObjects +rule Objects { - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } } -rule AvrMainFromObjects +rule Library { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; } -actions AvrMainFromObjects +rule Main { - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; } -rule AvrMain +rule Hex { - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; } -rule AvrHex +actions Hex { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) } -actions AvrHex +rule Upload { - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; } -rule AvrUpload +actions UploadAction { - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i } -actions AvrUploadAction +rule Arduino { - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : core libs ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } } -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; +# +# Targets +# -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; +# Grab everything from the core directory +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/tests/pingpair_blocking/pingpair_blocking.pde b/tests/pingpair_blocking/pingpair_blocking.pde index 817310d..1501d37 100644 --- a/tests/pingpair_blocking/pingpair_blocking.pde +++ b/tests/pingpair_blocking/pingpair_blocking.pde @@ -166,6 +166,9 @@ void setup(void) // radio.printDetails(); + + if ( role == role_pong_back ) + printf("\n\r+OK "); } void loop(void) diff --git a/tests/pingpair_blocking/printf.h b/tests/pingpair_blocking/printf.h index df6c46a..b2efd56 100644 --- a/tests/pingpair_blocking/printf.h +++ b/tests/pingpair_blocking/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,7 +16,7 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" +#ifdef ARDUINO int serial_putc( char c, FILE * ) { @@ -30,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/tests/pingpair_blocking/runtest.py b/tests/pingpair_blocking/runtest.py index 45fb65c..0772f95 100755 --- a/tests/pingpair_blocking/runtest.py +++ b/tests/pingpair_blocking/runtest.py @@ -1,10 +1,10 @@ -#!/opt/local/bin/python +#!/usr/bin/python import sys,serial def read_until(token): while 1: - line = ser.readline(None,"\r") + line = ser.readline(None) sys.stdout.write(line) if (line.startswith(token)): diff --git a/tests/pingpair_blocking/runtests.sh b/tests/pingpair_blocking/runtests.sh index 53b16f9..e106448 100755 --- a/tests/pingpair_blocking/runtests.sh +++ b/tests/pingpair_blocking/runtests.sh @@ -1,9 +1,5 @@ #!/bin/sh -# Connect p6 to receiver, p4 to sender +# Connect u0 to receiver, u1 to sender -jam p4 p6 || exit 1 -./runtest.py /dev/tty.usbserial-A600eHIs 1 & -./runtest.py /dev/tty.usbserial-A40081RP 1 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -exit 0 +jam u0 u1 && expect test.ex diff --git a/tests/pingpair_blocking/test.ex b/tests/pingpair_blocking/test.ex new file mode 100755 index 0000000..ea992ad --- /dev/null +++ b/tests/pingpair_blocking/test.ex @@ -0,0 +1,11 @@ +#/usr/bin/expect + +set timeout 100 +spawn picocom -b 57600 /dev/ttyUSB0 +expect "+READY" +send "1" +expect "+OK" +spawn picocom -b 57600 /dev/ttyUSB1 +expect "+READY" +send "1" +expect "+OK" From dc48235343acae2e23a16278b24aa96665fea04b Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 Jan 2012 22:15:45 -0800 Subject: [PATCH 190/326] Refactor pingpair_test to remove all serial io from within the irq handler, as necessitated by Arduino 1.0 --- tests/pingpair_test/Jamfile | 290 ++++++++++++++------------ tests/pingpair_test/pingpair_test.pde | 48 +++-- tests/pingpair_test/printf.h | 8 +- tests/pingpair_test/runtests.sh | 16 +- tests/pingpair_test/test.ex | 11 + 5 files changed, 208 insertions(+), 165 deletions(-) create mode 100755 tests/pingpair_test/test.ex diff --git a/tests/pingpair_test/Jamfile b/tests/pingpair_test/Jamfile index ebc0bae..6268f4b 100644 --- a/tests/pingpair_test/Jamfile +++ b/tests/pingpair_test/Jamfile @@ -1,207 +1,219 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; +# (1) Project Information -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; +PROJECT_LIBS = SPI RF24 ; +PROJECT_DIRS = $(PWD) ; -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; +# (2) Board Information + +UPLOAD_PROTOCOL ?= arduino ; +UPLOAD_SPEED ?= 57600 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. -# Host-specific overrides for locations if $(OS) = MACOSX { -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -#ARDUINO_DIR = /opt/Arduino ; -ARDUINO_DIR = $(OLD_DIR) ; -ARDUINO_AVR = /usr/lib/avr/include ; + AVR_BIN ?= /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN ?= /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; } -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE ?= $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; # Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; +# Output locations -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; +# +# Custom rules +# rule GitVersion { - Always $(<) ; - Depends all : $(<) ; + Always $(<) ; + Depends all : $(<) ; } actions GitVersion { - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) } GitVersion version.h ; -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - rule Pde { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; } actions Pde { - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) } -rule AvrPde +rule C++Pde { - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; } -rule AvrObject +rule UserObject { - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } } -rule AvrObjects +rule Objects { - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } } -rule AvrMainFromObjects +rule Library { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; } -actions AvrMainFromObjects +rule Main { - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; } -rule AvrMain +rule Hex { - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; } -rule AvrHex +actions Hex { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) } -actions AvrHex +rule Upload { - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; } -rule AvrUpload +actions UploadAction { - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i } -actions AvrUploadAction +rule Arduino { - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : core libs ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } } -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; +# +# Targets +# -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; +# Grab everything from the core directory +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index c16db2b..61ad29b 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -230,7 +230,7 @@ void setup(void) // Dump the configuration of the rf unit for debugging // - radio.printDetails(); + //radio.printDetails(); // // Attach interrupt handler to interrupt #0 (using pin 2) @@ -238,8 +238,23 @@ void setup(void) // attachInterrupt(0, check_radio, FALLING); + + if ( role == role_receiver ) + printf("\n\r+OK "); } +// +// Print buffer +// +// Printing from the interrupt handler is a bad idea, so we print from there +// to this intermediate buffer +// + +char prbuf[1000]; +char *prbuf_end = prbuf + sizeof(prbuf); +char *prbuf_in = prbuf; +char *prbuf_out = prbuf; + // // Loop // @@ -274,7 +289,7 @@ void loop(void) delay(interval); // Timeout if we have not received anything back ever - if ( ! last_message_count && millis() > interval * 10 ) + if ( ! last_message_count && millis() > interval * 100 ) { printf("No responses received. Are interrupts connected??\n\r"); done = true; @@ -285,6 +300,17 @@ void loop(void) // Receiver role: Does nothing! All the work is in IRQ // + // + // Spew print buffer + // + + size_t write_length = prbuf_in - prbuf_out; + if ( write_length ) + { + Serial.write(reinterpret_cast(prbuf_out),write_length); + prbuf_out += write_length; + } + // // Stop the test if we're done and report results // @@ -299,8 +325,6 @@ void loop(void) printf("FAIL\n\r\n\r"); } - // - // } void check_radio(void) @@ -313,10 +337,10 @@ void check_radio(void) if ( tx ) { if ( role == role_sender ) - printf("Send:OK "); + prbuf_in += sprintf(prbuf_in,"Send:OK "); if ( role == role_receiver ) - printf("Ack Payload:Sent\n\r"); + prbuf_in += sprintf(prbuf_in,"Ack Payload:Sent\n\r"); } // Have we failed to transmit? @@ -324,14 +348,14 @@ void check_radio(void) { if ( role == role_sender ) { - printf("Send:Failed "); + prbuf_in += sprintf(prbuf_in,"Send:Failed "); // log status of this line one_failed(); } if ( role == role_receiver ) - printf("Ack Payload:Failed\n\r"); + prbuf_in += sprintf(prbuf_in,"Ack Payload:Failed\n\r"); } // Transmitter can power down for now, because @@ -346,19 +370,19 @@ void check_radio(void) if ( role == role_sender ) { radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu ",message_count); + prbuf_in += sprintf(prbuf_in,"Ack:%lu ",message_count); // is this ack what we were expecting? to account // for failures, we simply want to make sure we get a // DIFFERENT ack every time. if ( ( message_count != last_message_count ) || ( configuration=='3' && message_count == 16 ) ) { - printf("OK "); + prbuf_in += sprintf(prbuf_in,"OK "); one_ok(); } else { - printf("FAILED "); + prbuf_in += sprintf(prbuf_in,"FAILED "); one_failed(); } last_message_count = message_count; @@ -382,7 +406,7 @@ void check_radio(void) receive_payload[len] = 0; // Spew it - printf("Got payload size=%i value=%s strlen=%u\n\r",len,receive_payload,strlen(receive_payload)); + prbuf_in += sprintf(prbuf_in,"Recv size=%i val=%s len=%u\n\r",len,receive_payload,strlen(receive_payload)); // Add an ack packet for the next time around. // Here we will report back how many bytes we got this time. diff --git a/tests/pingpair_test/printf.h b/tests/pingpair_test/printf.h index df6c46a..b2efd56 100644 --- a/tests/pingpair_test/printf.h +++ b/tests/pingpair_test/printf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 James Coliz, Jr. + Copyright (C) 2011 J. Coliz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,7 +16,7 @@ #ifndef __PRINTF_H__ #define __PRINTF_H__ -#include "WProgram.h" +#ifdef ARDUINO int serial_putc( char c, FILE * ) { @@ -30,4 +30,8 @@ void printf_begin(void) fdevopen( &serial_putc, 0 ); } +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + #endif // __PRINTF_H__ diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 76f8f77..53360c3 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -1,16 +1,8 @@ #!/bin/sh -# Connect p6 to receiver, p4 to sender +# Connect u0 to receiver, u0 to sender # WARNING: Test config 2 only works with PLUS units. -jam p4 p6 || exit 1 -./runtest.py /dev/tty.usbserial-A600eHIs 1 & -./runtest.py /dev/tty.usbserial-A40081RP 1 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -./runtest.py /dev/tty.usbserial-A600eHIs 2 & -./runtest.py /dev/tty.usbserial-A40081RP 2 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -./runtest.py /dev/tty.usbserial-A600eHIs 3 & -./runtest.py /dev/tty.usbserial-A40081RP 3 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -exit 0 +jam u0 u1 && expect test.ex 1 +jam u0 u1 && expect test.ex 2 +jam u0 u1 && expect test.ex 3 diff --git a/tests/pingpair_test/test.ex b/tests/pingpair_test/test.ex new file mode 100755 index 0000000..a14ffef --- /dev/null +++ b/tests/pingpair_test/test.ex @@ -0,0 +1,11 @@ +#/usr/bin/expect + +set timeout 100 +spawn picocom -b 57600 /dev/ttyUSB0 +expect "+READY" +send [lindex $argv 0] +expect "+OK" +spawn picocom -b 57600 /dev/ttyUSB1 +expect "+READY" +send [lindex $argv 0] +expect "+OK" From c6262edbce48bb839d5fed4412da0fad39e0250a Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 Jan 2012 22:23:10 -0800 Subject: [PATCH 191/326] FAQ- How much RAM?? --- FAQ | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/FAQ b/FAQ index 343d874..eccd03a 100644 --- a/FAQ +++ b/FAQ @@ -9,6 +9,8 @@ * * @ref issues * + * @ref ram + * * @ref tests * * @section starting Where do I start? @@ -35,8 +37,12 @@ * @section issues What should I do if I find a problem? * * Please open an issue on github if you find any problems using it with any version of Arduino or Maple. + * + * @section ram What is the RAM footprint of this library? + * + * 16 bytes. A single radio object consumes 16 bytes of RAM, and the library + * does not use any other RAM statically. * - * * @section tests Why are the examples in the 'tests' directory failing? * * The sketches in the 'tests' directory are not for general use. @@ -44,9 +50,4 @@ * * The 'tests' directory is only for people making changes to the library * to ensure that their changes do not break anything. - * - * - * - * - * */ From 94635e8cc50843db3603971d9c8978f1942a2d5e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 15 Jan 2012 23:35:07 -0800 Subject: [PATCH 192/326] runtests now hups between each test instead of uploading --- tests/pingpair_test/runtests.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 53360c3..3416f86 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -4,5 +4,9 @@ # WARNING: Test config 2 only works with PLUS units. jam u0 u1 && expect test.ex 1 -jam u0 u1 && expect test.ex 2 -jam u0 u1 && expect test.ex 3 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 && sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 && sleep 1 +expect test.ex 2 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 && sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 && sleep 1 +expect test.ex 3 From 205d4b5cc12d119149e75e62e62b98fce0f38ac7 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 17 Jan 2012 22:04:20 -0800 Subject: [PATCH 193/326] Tune timing and upload rate of tests --- tests/pingpair_blocking/Jamfile | 2 +- tests/pingpair_test/Jamfile | 2 +- tests/pingpair_test/runtests.sh | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/pingpair_blocking/Jamfile b/tests/pingpair_blocking/Jamfile index 6268f4b..18244ec 100644 --- a/tests/pingpair_blocking/Jamfile +++ b/tests/pingpair_blocking/Jamfile @@ -6,7 +6,7 @@ PROJECT_DIRS = $(PWD) ; # (2) Board Information UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 57600 ; +UPLOAD_SPEED ?= 115200 ; MCU ?= atmega328p ; F_CPU ?= 16000000 ; CORE ?= arduino ; diff --git a/tests/pingpair_test/Jamfile b/tests/pingpair_test/Jamfile index 6268f4b..18244ec 100644 --- a/tests/pingpair_test/Jamfile +++ b/tests/pingpair_test/Jamfile @@ -6,7 +6,7 @@ PROJECT_DIRS = $(PWD) ; # (2) Board Information UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 57600 ; +UPLOAD_SPEED ?= 115200 ; MCU ?= atmega328p ; F_CPU ?= 16000000 ; CORE ?= arduino ; diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 3416f86..9f1a73f 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -4,9 +4,13 @@ # WARNING: Test config 2 only works with PLUS units. jam u0 u1 && expect test.ex 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB0 && sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB1 && sleep 1 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 expect test.ex 2 -stty 57600 raw ignbrk hup < /dev/ttyUSB0 && sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB1 && sleep 1 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 expect test.ex 3 From f6e59b9de8d8424c3a1daecfbdafdcbfcd85ccac Mon Sep 17 00:00:00 2001 From: maniacbug Date: Tue, 17 Jan 2012 22:05:11 -0800 Subject: [PATCH 194/326] Remove prog_char, for compliance with gcc 4.6.1 --- RF24.cpp | 4 +- RF24.h | 4 +- RF24_config.h | 6 +- examples/pingpair_irq/Jamfile | 289 +++++++++++++------------ examples/pingpair_irq/pingpair_irq.pde | 2 +- 5 files changed, 160 insertions(+), 145 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 2b9048d..cee374f 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -206,7 +206,7 @@ void RF24::print_observe_tx(uint8_t value) /****************************************************************************/ -void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) +void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); @@ -217,7 +217,7 @@ void RF24::print_byte_register(prog_char* name, uint8_t reg, uint8_t qty) /****************************************************************************/ -void RF24::print_address_register(prog_char* name, uint8_t reg, uint8_t qty) +void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) { char extra_tab = strlen_P(name) < 8 ? '\t' : 0; printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); diff --git a/RF24.h b/RF24.h index 23a5bba..141bdab 100644 --- a/RF24.h +++ b/RF24.h @@ -194,7 +194,7 @@ protected: * @param reg Which register. Use constants from nRF24L01.h * @param qty How many successive registers to print */ - void print_byte_register(prog_char* name, uint8_t reg, uint8_t qty = 1); + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); /** * Print the name and value of a 40-bit address register to stdout @@ -207,7 +207,7 @@ protected: * @param reg Which register. Use constants from nRF24L01.h * @param qty How many successive registers to print */ - void print_address_register(prog_char* name, uint8_t reg, uint8_t qty = 1); + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); /** * Turn on or off the special features of the chip diff --git a/RF24_config.h b/RF24_config.h index fc6d696..fc7397f 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -37,11 +37,13 @@ extern HardwareSPI SPI; #endif // Avoid spurious warnings +#if 1 #if ! defined( NATIVE ) && defined( ARDUINO ) #undef PROGMEM #define PROGMEM __attribute__(( section(".progmem.data") )) #undef PSTR -#define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) +#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) +#endif #endif // Progmem is Arduino-specific @@ -49,7 +51,7 @@ extern HardwareSPI SPI; #include #define PRIPSTR "%S" #else -typedef char const prog_char; +typedef char const char; typedef uint16_t prog_uint16_t; #define PSTR(x) (x) #define printf_P printf diff --git a/examples/pingpair_irq/Jamfile b/examples/pingpair_irq/Jamfile index 901f8da..18244ec 100644 --- a/examples/pingpair_irq/Jamfile +++ b/examples/pingpair_irq/Jamfile @@ -1,206 +1,219 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; +# (1) Project Information -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; +PROJECT_LIBS = SPI RF24 ; +PROJECT_DIRS = $(PWD) ; -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; +# (2) Board Information + +UPLOAD_PROTOCOL ?= arduino ; +UPLOAD_SPEED ?= 115200 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. -# Host-specific overrides for locations if $(OS) = MACOSX { -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; + AVR_BIN ?= /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN ?= /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; } -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE ?= $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; # Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; +# Output locations -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; +# +# Custom rules +# rule GitVersion { - Always $(<) ; - Depends all : $(<) ; + Always $(<) ; + Depends all : $(<) ; } actions GitVersion { - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) } GitVersion version.h ; -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - rule Pde { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; } actions Pde { - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) } -rule AvrPde +rule C++Pde { - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; } -rule AvrObject +rule UserObject { - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } } -rule AvrObjects +rule Objects { - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } } -rule AvrMainFromObjects +rule Library { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; } -actions AvrMainFromObjects +rule Main { - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; } -rule AvrMain +rule Hex { - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; } -rule AvrHex +actions Hex { - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) } -actions AvrHex +rule Upload { - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; } -rule AvrUpload +actions UploadAction { - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i } -actions AvrUploadAction +rule Arduino { - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : core libs ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } } -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; +# +# Targets +# -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; +# Grab everything from the core directory +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde index 173f5fe..47084a1 100644 --- a/examples/pingpair_irq/pingpair_irq.pde +++ b/examples/pingpair_irq/pingpair_irq.pde @@ -24,7 +24,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(9,10); +RF24 radio(8,9); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter From 2c9718632983e4ca563f559558cdede2358ccb35 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sun, 26 Feb 2012 19:53:08 -0800 Subject: [PATCH 195/326] Fix bug introduced in the move to PROGMEM. Props to @basilrx for finding this. Also added a test case to demonstrate the bug, and guard against more like it in the future. --- RF24.cpp | 2 +- tests/pingpair_test/pingpair_test.pde | 22 +++++++++++++++++++--- tests/pingpair_test/runtests.sh | 5 +++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index cee374f..9471583 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -649,7 +649,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) // Note it would be more efficient to set all of the bits for all open // pipes at once. However, I thought it would make the calling code // more simple to do it this way. - write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(child_pipe_enable[child])); + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); } } diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 61ad29b..6acbf51 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -89,6 +89,8 @@ const int interval = 100; //*< ms to wait between sends */ char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ +uint8_t pipe_number = 1; // Which pipe to send on. + void one_ok(void) { // Have we received enough yet? @@ -203,6 +205,20 @@ void setup(void) radio.enableDynamicPayloads(); } + // Config 4 tests out a higher pipe ## + if (configuration == '4' && role == role_sender) + { + // Set top 4 bytes of the address in pipe 1 + radio.openReadingPipe(1,pipe & 0xFFFFFFFF00ULL); + + // indicate the pipe to use + pipe_number = 5; + } + else if ( role == role_sender ) + { + radio.openReadingPipe(5,0); + } + // // Open pipes to other nodes for communication // @@ -216,7 +232,7 @@ void setup(void) } else { - radio.openReadingPipe(1,pipe); + radio.openReadingPipe(pipe_number,pipe); } // @@ -230,7 +246,7 @@ void setup(void) // Dump the configuration of the rf unit for debugging // - //radio.printDetails(); + radio.printDetails(); // // Attach interrupt handler to interrupt #0 (using pin 2) @@ -410,7 +426,7 @@ void check_radio(void) // Add an ack packet for the next time around. // Here we will report back how many bytes we got this time. - radio.writeAckPayload( 1, &len, sizeof(len) ); + radio.writeAckPayload( pipe_number, &len, sizeof(len) ); ++message_count; } } diff --git a/tests/pingpair_test/runtests.sh b/tests/pingpair_test/runtests.sh index 9f1a73f..4d02310 100755 --- a/tests/pingpair_test/runtests.sh +++ b/tests/pingpair_test/runtests.sh @@ -14,3 +14,8 @@ stty 57600 raw ignbrk hup < /dev/ttyUSB0 sleep 1 stty 57600 raw ignbrk hup < /dev/ttyUSB1 expect test.ex 3 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 +expect test.ex 4 From 84e464def4a52a3a697ee95d26eb66997db0ea76 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 23 Jun 2012 19:46:42 -0700 Subject: [PATCH 196/326] latest jamfiles --- examples/GettingStarted/GettingStarted.pde | 12 ++++++------ examples/GettingStarted/Jamfile | 6 +++--- examples/pingpair_irq/Jamfile | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/GettingStarted/GettingStarted.pde b/examples/GettingStarted/GettingStarted.pde index 3cb32d5..bf1851a 100644 --- a/examples/GettingStarted/GettingStarted.pde +++ b/examples/GettingStarted/GettingStarted.pde @@ -75,7 +75,7 @@ void setup(void) // optionally, reduce the payload size. seems to // improve reliability - radio.setPayloadSize(8); + //radio.setPayloadSize(8); // // Open pipes to other nodes for communication @@ -86,15 +86,15 @@ void setup(void) // Open 'our' pipe for writing // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - if ( role == role_ping_out ) + //if ( role == role_ping_out ) { - radio.openWritingPipe(pipes[0]); + //radio.openWritingPipe(pipes[0]); radio.openReadingPipe(1,pipes[1]); } - else + //else { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); + //radio.openWritingPipe(pipes[1]); + //radio.openReadingPipe(1,pipes[0]); } // diff --git a/examples/GettingStarted/Jamfile b/examples/GettingStarted/Jamfile index 6a2499a..9a5f2c4 100644 --- a/examples/GettingStarted/Jamfile +++ b/examples/GettingStarted/Jamfile @@ -4,7 +4,7 @@ PROJECT_LIBS = SPI RF24 ; # (2) Board Information -UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_PROTOCOL ?= arduino ; UPLOAD_SPEED ?= 57600 ; MCU ?= atmega328p ; F_CPU ?= 16000000 ; @@ -35,8 +35,8 @@ if $(OS) = MACOSX } else { - AVR_BIN = /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; + AVR_BIN ?= /usr/bin ; + AVR_INCLUDE ?= /usr/lib/avr/include ; AVR_ETC = /etc ; } diff --git a/examples/pingpair_irq/Jamfile b/examples/pingpair_irq/Jamfile index 18244ec..97237bc 100644 --- a/examples/pingpair_irq/Jamfile +++ b/examples/pingpair_irq/Jamfile @@ -37,7 +37,7 @@ if $(OS) = MACOSX else { AVR_BIN ?= /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; + AVR_INCLUDE ?= /usr/lib/avr/include ; AVR_ETC = /etc ; } From 49f22eae21c2b7a872897a6a627bd255369ff2b2 Mon Sep 17 00:00:00 2001 From: Jason Becht Date: Wed, 6 Feb 2013 18:43:42 -0600 Subject: [PATCH 197/326] Update examples/led_remote/led_remote.pde Line 172 was using button_pins instead of led_pins. This caused leds to be lit dim when set to HIGH. Changed to correct var. --- examples/led_remote/led_remote.pde | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/led_remote/led_remote.pde b/examples/led_remote/led_remote.pde index 7aeeb3b..80e2955 100644 --- a/examples/led_remote/led_remote.pde +++ b/examples/led_remote/led_remote.pde @@ -169,7 +169,7 @@ void setup(void) int i = num_led_pins; while(i--) { - pinMode(button_pins[i],OUTPUT); + pinMode(led_pins[i],OUTPUT); led_states[i] = HIGH; digitalWrite(led_pins[i],led_states[i]); } From ebcd0d1d0b3061fcb57444e1dbe5829ef25705cd Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 3 Oct 2013 06:53:28 -0700 Subject: [PATCH 198/326] Add isValid --- RF24.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RF24.h b/RF24.h index 141bdab..18914f0 100644 --- a/RF24.h +++ b/RF24.h @@ -637,6 +637,15 @@ public: */ bool testRPD(void) ; + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + /**@}*/ }; From 4cf0fb0c5adc305d40547f42366161e809122b94 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 18 Mar 2014 07:07:03 -0600 Subject: [PATCH 199/326] High Speed Data Transfers/Optimization Changes: - Enable PTX at radio begin, in standby-I mode - Enable PTX at radio stopListening(); in standy-I mode - Radio startListening - removed delay. What is there to wait for when the radio will just be listening anyway? - Radio powerUp - radio will need to be powered up after configuration - radio will not power down unless instructed to - need to issue radio.txStandBy(); when finished transmitting to engage standby-I mode - Added txStandBy() to drop to standBy-I mode if not switching to RX mode - Radio Write: Sped up - radio previously did not use standby mode effectively, after TX sending of each packet, the radio is powered down introducing a 1.5ms delay in addition to the 130us delay from standby. - No observe-tx code, a simple NOP cmd is sent to get the status info - No millis() timeout, hanging condition should only occur because of faulty code - Simplified whatHappened() code - Flush TX FIFO if MAX_RT met as lib does not support re-transmit instead of flush every time. - Remove ack size monitoring - No powerdown needed, - Added writeClear() function for writes that need the interrupt flags cleared (ack payloads, etc). Same as write() otherwise - Radio startWrite: Sped up - Enable PTX mode is now done in stopListening() code. Enter TX mode by writing CE high. This brings the data transfer capabilities up to a reasonable level - No need to power up, already in Standby-I mode - No need for delayMicroseconds() in write functon. - No need for delay after writing CE high - Does not enter Standby-I mode be default. It is up to the MCU to call the new txStandBy() function to enter standby-I mode from PTX if not switching back to PRX - Radio available: - Now checks the FIFO buffer instead of the interrupt flag (no missed data) - Moved clearing of interrupt flags to Read() - Radio read: - No longer returns any value - use available to check for more data - Radio isAckPayloadAvailable() now checks the FIFO directly - Radio getDynamicPayloadSize() - Per the datasheet, added fix to flush corrupted packets --- RF24.cpp | 200 +++++++++++++++++++++++++------------------------------ RF24.h | 54 ++++++++------- 2 files changed, 121 insertions(+), 133 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 9471583..c875fc9 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -103,9 +103,9 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) uint8_t data_len = min(len,payload_size); uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - + //printf("[Writing %u bytes %u blanks]",data_len,blank_len); - + csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); while ( data_len-- ) @@ -126,9 +126,9 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) uint8_t data_len = min(len,payload_size); uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - + //printf("[Reading %u bytes %u blanks]",data_len,blank_len); - + csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); while ( data_len-- ) @@ -239,7 +239,7 @@ void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) /****************************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), + ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), pipe0_reading_address(0) { @@ -299,7 +299,7 @@ static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_MED"; static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_HIGH"; -static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { +static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { rf24_pa_dbm_e_str_0, rf24_pa_dbm_e_str_1, rf24_pa_dbm_e_str_2, @@ -366,14 +366,14 @@ void RF24::begin(void) { p_variant = true ; } - + // Then set the data rate to the slowest (and most reliable) speed supported by all // hardware. setDataRate( RF24_1MBPS ) ; // Initialize CRC and request 2-byte (16bit) CRC setCRCLength( RF24_CRC_16 ) ; - + // Disable dynamic payloads, to match dynamic_payloads_enabled setting write_register(DYNPD,0); @@ -389,6 +389,11 @@ void RF24::begin(void) // Flush buffers flush_rx(); flush_tx(); + + //Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + + } /****************************************************************************/ @@ -409,8 +414,6 @@ void RF24::startListening(void) // Go! ce(HIGH); - // wait for the radio to come up (130us actually only needed) - delayMicroseconds(130); } /****************************************************************************/ @@ -420,6 +423,8 @@ void RF24::stopListening(void) ce(LOW); flush_tx(); flush_rx(); + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + } /****************************************************************************/ @@ -431,6 +436,7 @@ void RF24::powerDown(void) /****************************************************************************/ +//Power up now. Radio will not power down unless instructed by MCU for config changes etc. void RF24::powerUp(void) { write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); @@ -438,85 +444,69 @@ void RF24::powerUp(void) /******************************************************************/ -bool RF24::write( const void* buf, uint8_t len ) +//Similar to the previous write, clears the interrupt flags +bool RF24::writeClear( const void* buf, uint8_t len ) { - bool result = false; + //Start Writing + startWrite(buf,len); - // Begin the write - startWrite(buf,len); + //Wait until complete or failed + //ACK payloads that are handled improperly will cause this to hang + //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } - // ------------ - // At this point we could return from a non-blocking write, and then call - // the rest after an interrupt + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - // Instead, we are going to block here until we get TX_DS (transmission completed and ack'd) - // or MAX_RT (maximum retries, transmission failed). Also, we'll timeout in case the radio - // is flaky and we get neither. - - // IN the end, the send should be blocking. It comes back in 60ms worst case, or much faster - // if I tighted up the retry logic. (Default settings will be 1500us. - // Monitor the send - uint8_t observe_tx; - uint8_t status; - uint32_t sent_at = millis(); - const uint32_t timeout = 500; //ms to wait for timeout - do - { - status = read_register(OBSERVE_TX,&observe_tx,1); - IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); + //Max retries exceeded + if( status & _BV(MAX_RT)){ + //Flush the FIFO since this is a single-packet-write function, and FIFO is NOT cleared when MAX_RT is reached + flush_tx(); } - while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( millis() - sent_at < timeout ) ); - - // The part above is what you could recreate with your own interrupt handler, - // and then call this when you got an interrupt - // ------------ - - // Call this when you get an interrupt - // The status tells us three things - // * The send was successful (TX_DS) - // * The send failed, too many retries (MAX_RT) - // * There is an ack packet waiting (RX_DR) - bool tx_ok, tx_fail; - whatHappened(tx_ok,tx_fail,ack_payload_available); - - //printf("%u%u%u\r\n",tx_ok,tx_fail,ack_payload_available); - - result = tx_ok; - IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); - - // Handle the ack packet - if ( ack_payload_available ) - { - ack_payload_length = getDynamicPayloadSize(); - IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); - IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); - } - - // Yay, we are done. - - // Power down - powerDown(); - - // Flush buffers (Is this a relic of past experimentation, and not needed anymore??) - flush_tx(); - - return result; + //TX OK 1 or 0 + return status & _BV(TX_DS);//tx_ok; } + /****************************************************************************/ -void RF24::startWrite( const void* buf, uint8_t len ) +//For general use, the flags are not important to clear +bool RF24::write( const void* buf, uint8_t len ) { - // Transmitter power-up - write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); - delayMicroseconds(150); + //Start Writing + startWrite(buf,len); - // Send the payload - write_payload( buf, len ); + //Wait until complete or failed + //ACK payloads that are handled improperly will cause this to hang + //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload + uint8_t status; + while( ! ( status = get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } - // Allons! - ce(HIGH); - delayMicroseconds(15); - ce(LOW); + //Max retries exceeded + if( status & _BV(MAX_RT)){ + //Flush the FIFO since this is a single-packet-write function, and FIFO is NOT cleared when MAX_RT is reached + flush_tx(); + } + //TX OK 1 or 0 + return status & _BV(TX_DS);//tx_ok; +} + + +/****************************************************************************/ + +//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high +//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) +//Otherwise we enter Standby-II mode, which is still faster than standby mode +//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data + +void RF24::startWrite( const void* buf, uint8_t len ){ //TMRh20 + + write_payload( buf,len); + ce(HIGH); + +} + + +void RF24::txStandBy(){ + ce(LOW); } /****************************************************************************/ @@ -530,6 +520,7 @@ uint8_t RF24::getDynamicPayloadSize(void) result = SPI.transfer(0xff); csn(HIGH); + if(result > 32) { flush_rx(); return 0; } return result; } @@ -544,45 +535,35 @@ bool RF24::available(void) bool RF24::available(uint8_t* pipe_num) { - uint8_t status = get_status(); - // Too noisy, enable if you really want lots o data!! - //IF_SERIAL_DEBUG(print_status(status)); + //Check the FIFO buffer to see if data is waitng to be read - bool result = ( status & _BV(RX_DR) ); + if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ - if (result) - { // If the caller wants the pipe number, include that - if ( pipe_num ) + if ( pipe_num ){ + uint8_t status = get_status(); *pipe_num = ( status >> RX_P_NO ) & B111; - - // Clear the status bit - - // ??? Should this REALLY be cleared now? Or wait until we - // actually READ the payload? - - write_register(STATUS,_BV(RX_DR) ); - - // Handle ack payload receipt - if ( status & _BV(TX_DS) ) - { - write_register(STATUS,_BV(TX_DS)); - } + } + return 1; } - return result; + return 0; + + + } /****************************************************************************/ -bool RF24::read( void* buf, uint8_t len ) -{ +void RF24::read( void* buf, uint8_t len ){ + // Fetch the payload read_payload( buf, len ); - // was this the last of the data available? - return read_register(FIFO_STATUS) & _BV(RX_EMPTY); + //Clear the two possible interrupt flags with one command + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) ); + } /****************************************************************************/ @@ -736,9 +717,10 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) bool RF24::isAckPayloadAvailable(void) { - bool result = ack_payload_available; - ack_payload_available = false; - return result; + if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ + return 1; + } + return 0; } /****************************************************************************/ @@ -905,7 +887,7 @@ rf24_datarate_e RF24::getDataRate( void ) { rf24_datarate_e result ; uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); - + // switch uses RAM (evil!) // Order matters in our case below if ( dr == _BV(RF_DR_LOW) ) @@ -931,11 +913,11 @@ rf24_datarate_e RF24::getDataRate( void ) void RF24::setCRCLength(rf24_crclength_e length) { uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - + // switch uses RAM (evil!) if ( length == RF24_CRC_DISABLED ) { - // Do nothing, we turned it off above. + // Do nothing, we turned it off above. } else if ( length == RF24_CRC_8 ) { diff --git a/RF24.h b/RF24.h index 18914f0..83cb562 100644 --- a/RF24.h +++ b/RF24.h @@ -51,9 +51,10 @@ private: bool p_variant; /* False for RF24L01 and true for RF24L01P */ uint8_t payload_size; /**< Fixed size of payloads */ bool ack_payload_available; /**< Whether there is an ack payload waiting */ - bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ + bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + bool avail; protected: /** @@ -219,6 +220,11 @@ protected: /**@}*/ public: + +//TMRh20 + void txStandBy(); + bool writeClear( const void* buf, uint8_t len ); + /** * @name Primary public interface * @@ -301,7 +307,7 @@ public: * @param len Maximum number of bytes to read into the buffer * @return True if the payload was delivered successfully false if not */ - bool read( void* buf, uint8_t len ); + void read( void* buf, uint8_t len ); /** * Open a pipe for writing @@ -351,7 +357,7 @@ public: /**@}*/ /** - * @name Optional Configurators + * @name Optional Configurators * * Methods you can use to get or set the configuration of the chip. * None are required. Calling begin() sets up a reasonable set of @@ -406,7 +412,7 @@ public: * @return Payload length of last-received dynamic payload */ uint8_t getDynamicPayloadSize(void); - + /** * Enable custom payloads on the acknowledge packets * @@ -486,7 +492,7 @@ public: * @return true if the change was successful */ bool setDataRate(rf24_datarate_e speed); - + /** * Fetches the transmission data rate * @@ -518,9 +524,9 @@ public: /**@}*/ /** - * @name Advanced Operation + * @name Advanced Operation * - * Methods you can use to drive the chip in more advanced ways + * Methods you can use to drive the chip in more advanced ways */ /**@{*/ @@ -642,9 +648,9 @@ public: * debugging. Setting either pin to 0xff is the way to * indicate that this is not a real radio. * - * @return true if this is a legitimate radio + * @return true if this is a legitimate radio */ - bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } /**@}*/ }; @@ -653,12 +659,12 @@ public: * @example GettingStarted.pde * * This is an example which corresponds to my "Getting Started" blog post: - * Getting Started with nRF24L01+ on Arduino. + * Getting Started with nRF24L01+ on Arduino. * - * It is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting - * with the serial monitor and sending a 'T'. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping * node can then see how long the whole cycle took. */ @@ -690,14 +696,14 @@ public: */ /** - * @example pingpair_maple.pde + * @example pingpair_maple.pde * * This is an example of how to use the RF24 class on the Maple. For a more * detailed explanation, see my blog post: * nRF24L01+ Running on Maple * * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. - * + * * Write this sketch to two different nodes, * connect the role_pin to ground on one. The ping node sends the current time to the pong node, * which responds by sending the value back. The ping node can then see how long the whole cycle @@ -759,25 +765,25 @@ public: * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver * * @section Goals Design Goals - * + * * This library is designed to be... * @li Maximally compliant with the intended operation of the chip * @li Easy for beginners to use * @li Consumed with a public interface that's similiar to other Arduino standard libraries * * @section News News - * - * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. + * + * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. * Please open an issue if you find any problems using it with any version of Arduino. * - * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the - * Maple Native, + * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the + * Maple Native, * and should work with any Maple board. See the pingpair_maple example. * Note that only the pingpair_maple example has been tested on Maple, although * the others can certainly be adapted. * * @section Useful Useful References - * + * * Please refer to: * * @li Documentation Main Page @@ -799,11 +805,11 @@ public: * * RF24 Getting Started - Finished Product * - * Getting Started with nRF24L01+ on Arduino + * Getting Started with nRF24L01+ on Arduino * * Nordic FOB and nRF24L01+ * - * Using the Sparkfun Nordic FOB + * Using the Sparkfun Nordic FOB * * RF Duinode V3 (2V4) * From dfc75db8e61276e9920e1cbc6be59939966ca4d9 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 18 Mar 2014 08:32:10 -0600 Subject: [PATCH 200/326] Update readme temporarily - advisory of non-compliance with current documentation --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c0e71c0..66d22c6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ # Arduino driver for nRF24L01 2.4GHz Wireless Transceiver +Note: March 2014, this fork was just published and is still undergoing fine tuning. +The attached documentation is close but does NOT match the library. +See http://TMRh20.blogspot.com for usage information and updates. +This readme will be updated once more is finalized. + Design Goals: This library is designed to be... * Maximally compliant with the intended operation of the chip +* As efficient in operation as possible * Easy for beginners to use * Consumed with a public interface that's similiar to other Arduino standard libraries * Built against the standard SPI library. From d0ec0c461ce6c2e1f15b334344330cdf8f993417 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 00:46:47 -0600 Subject: [PATCH 201/326] Changed writing modes - Changed writeClear to the default radio.write() option - Changed write() to writeBlock() - uses the auto-retry functionality of the chip and will block forever until successful --- RF24.cpp | 48 +++++++++++++++++++++++++++++------------------- RF24.h | 3 ++- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index c875fc9..db109a0 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -445,7 +445,7 @@ void RF24::powerUp(void) /******************************************************************/ //Similar to the previous write, clears the interrupt flags -bool RF24::writeClear( const void* buf, uint8_t len ) +bool RF24::write( const void* buf, uint8_t len ) { //Start Writing startWrite(buf,len); @@ -459,37 +459,47 @@ bool RF24::writeClear( const void* buf, uint8_t len ) //Max retries exceeded if( status & _BV(MAX_RT)){ - //Flush the FIFO since this is a single-packet-write function, and FIFO is NOT cleared when MAX_RT is reached - flush_tx(); + flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush + return 0; } //TX OK 1 or 0 - return status & _BV(TX_DS);//tx_ok; + return 1; } /****************************************************************************/ //For general use, the flags are not important to clear -bool RF24::write( const void* buf, uint8_t len ) +bool RF24::writeBlocking( const void* buf, uint8_t len ) { - //Start Writing - startWrite(buf,len); + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //This way the FIFO will fill up and allow blocking until packets go through + //The radio will auto-clear everything in the FIFO as long as CE remains high - //Wait until complete or failed - //ACK payloads that are handled improperly will cause this to hang - //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload - uint8_t status; - while( ! ( status = get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + //Start Writing + startWrite(buf,len); - //Max retries exceeded - if( status & _BV(MAX_RT)){ - //Flush the FIFO since this is a single-packet-write function, and FIFO is NOT cleared when MAX_RT is reached - flush_tx(); - } - //TX OK 1 or 0 - return status & _BV(TX_DS);//tx_ok; + while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful + + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + reUseTX(); //Set re-transmit + ce(LOW); //Re-Transfer packet + ce(HIGH); + } + + } + + return 1; } +void RF24::reUseTX(){ + csn(LOW); + SPI.transfer( REUSE_TX_PL ); + csn(HIGH); + +} /****************************************************************************/ //Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high diff --git a/RF24.h b/RF24.h index 83cb562..6c332b3 100644 --- a/RF24.h +++ b/RF24.h @@ -223,7 +223,8 @@ public: //TMRh20 void txStandBy(); - bool writeClear( const void* buf, uint8_t len ); + bool writeBlocking( const void* buf, uint8_t len ); + void reUseTX(); /** * @name Primary public interface From 281ace4ba24fb9c0cf814572c72752eebd3b8d95 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 03:19:39 -0600 Subject: [PATCH 202/326] Added writeFast() cmd - fill FIFO or return 0 - WriteFast allows the FIFO buffers to be filled up before returning 0 - The current payload will be dropped, and the previous payload resent - The user should just keep trying to send the same data if a 0 is received --- RF24.cpp | 40 +++++++++++++++++++++++++++++++++++++--- RF24.h | 1 + 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index db109a0..d8cdd94 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -468,7 +468,7 @@ bool RF24::write( const void* buf, uint8_t len ) /****************************************************************************/ -//For general use, the flags are not important to clear +//For general use, the interrupt flags are not important to clear bool RF24::writeBlocking( const void* buf, uint8_t len ) { //Block until the FIFO is NOT full. @@ -476,8 +476,6 @@ bool RF24::writeBlocking( const void* buf, uint8_t len ) //This way the FIFO will fill up and allow blocking until packets go through //The radio will auto-clear everything in the FIFO as long as CE remains high - //Start Writing - startWrite(buf,len); while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful @@ -486,9 +484,12 @@ bool RF24::writeBlocking( const void* buf, uint8_t len ) reUseTX(); //Set re-transmit ce(LOW); //Re-Transfer packet ce(HIGH); + delayMicroseconds(15); } } + //Start Writing + startWrite(buf,len); return 1; } @@ -500,6 +501,39 @@ void RF24::reUseTX(){ csn(HIGH); } + +/****************************************************************************/ + +//This is for when every bit of data is important +bool RF24::writeFast( const void* buf, uint8_t len ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //Return 0 so the user can control the retrys and set a timer or failure counter if required + //The radio will auto-clear everything in the FIFO as long as CE remains high + + + while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful + + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + reUseTX(); //Set re-transmit + ce(LOW); //Re-Transfer packet + ce(HIGH); + delayMicroseconds(15); //CE needs to stay high for 10us, for TX_REUSE to engage + return 0; //Return 0. The previous payload has been retransmitted + //From the user perspective, if you get a 0, just keep trying to send the same payload + } + + } + //Start Writing + startWrite(buf,len); + + return 1; +} + + + /****************************************************************************/ //Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high diff --git a/RF24.h b/RF24.h index 6c332b3..3190ecd 100644 --- a/RF24.h +++ b/RF24.h @@ -225,6 +225,7 @@ public: void txStandBy(); bool writeBlocking( const void* buf, uint8_t len ); void reUseTX(); + bool writeFast( const void* buf, uint8_t len ); //Fills FIFO buffer, uses packet re-use function of the chip and returns 0 if packet failed and re-sent /** * @name Primary public interface From 77b1e2c1400e755d13bfd9345bd235075b056ad4 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 03:43:13 -0600 Subject: [PATCH 203/326] - Slight modification to reUseTX cmd - put the CE HIGH, LOW changes in the cmd for non-internal use - Put the clearing of MAX_RT in the reUse cmd for non-internal use - Removed unneeded delay from writeBlocking() --- RF24.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index d8cdd94..0ce3e65 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -480,11 +480,7 @@ bool RF24::writeBlocking( const void* buf, uint8_t len ) while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful if( get_status() & _BV(MAX_RT)){ - write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag reUseTX(); //Set re-transmit - ce(LOW); //Re-Transfer packet - ce(HIGH); - delayMicroseconds(15); } } @@ -496,10 +492,12 @@ bool RF24::writeBlocking( const void* buf, uint8_t len ) void RF24::reUseTX(){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag csn(LOW); SPI.transfer( REUSE_TX_PL ); csn(HIGH); - + ce(LOW); //Re-Transfer packet + ce(HIGH); } /****************************************************************************/ @@ -518,8 +516,6 @@ bool RF24::writeFast( const void* buf, uint8_t len ) if( get_status() & _BV(MAX_RT)){ write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag reUseTX(); //Set re-transmit - ce(LOW); //Re-Transfer packet - ce(HIGH); delayMicroseconds(15); //CE needs to stay high for 10us, for TX_REUSE to engage return 0; //Return 0. The previous payload has been retransmitted //From the user perspective, if you get a 0, just keep trying to send the same payload From 1e2f41c3f8143c9be0ae4aaa165cf98cb3052d84 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 08:26:59 -0600 Subject: [PATCH 204/326] Inspired idea to change TX standby code Realised that data would be lost if dropping to standby or power down mode before the FIFO is empty. txStandby now returns a boolean value to indicate whether the transmission is complete. If there is data in the TX FIFO the radio will not drop out of TX mode. eg: while( !txStandBy() ){} --- RF24.cpp | 9 +++++++-- RF24.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 0ce3e65..5ef039d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -545,8 +545,13 @@ void RF24::startWrite( const void* buf, uint8_t len ){ //TMRh20 } -void RF24::txStandBy(){ - ce(LOW); +bool RF24::txStandBy(){ + + if ( (read_register(FIFO_STATUS) & _BV(TX_EMPTY))){ + ce(LOW); + return 1; + } + return 0; } /****************************************************************************/ diff --git a/RF24.h b/RF24.h index 3190ecd..b666a76 100644 --- a/RF24.h +++ b/RF24.h @@ -222,7 +222,7 @@ protected: public: //TMRh20 - void txStandBy(); + bool txStandBy(); bool writeBlocking( const void* buf, uint8_t len ); void reUseTX(); bool writeFast( const void* buf, uint8_t len ); //Fills FIFO buffer, uses packet re-use function of the chip and returns 0 if packet failed and re-sent From 14187cd1a1257ee889fc41b5986ce75324f5500d Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 19:13:26 -0600 Subject: [PATCH 205/326] Updated readme and h file for doxygen See http://tmrh20.github.io/RF24/class_r_f24.html for the class documenation. --- README.md | 24 ++++--- RF24.h | 204 ++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 165 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 66d22c6..24f69e1 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,26 @@ -# Arduino driver for nRF24L01 2.4GHz Wireless Transceiver +# Optimized High Speed Arduino driver for nRF24L01 2.4GHz Wireless Transceiver -Note: March 2014, this fork was just published and is still undergoing fine tuning. -The attached documentation is close but does NOT match the library. -See http://TMRh20.blogspot.com for usage information and updates. -This readme will be updated once more is finalized. +Note: March 2014, this fork was just published and is still undergoing fine tuning. + +Class documenation is now available at http://tmrh20.github.io/RF24/class_r_f24.html + +See http://TMRh20.blogspot.com for an overview of the new functionality. Design Goals: This library is designed to be... -* Maximally compliant with the intended operation of the chip -* As efficient in operation as possible +* More compliat with the manufacturer specified operation of the chip +* Utilize the capabilities of the radio to their full potential via Arduino +* More reliable and feature rich * Easy for beginners to use * Consumed with a public interface that's similiar to other Arduino standard libraries * Built against the standard SPI library. Please refer to: -* [Documentation Main Page](http://maniacbug.github.com/RF24) -* [RF24 Class Documentation](http://maniacbug.github.com/RF24/classRF24.html) -* [Source Code](https://github.com/maniacbug/RF24) -* [Downloads](https://github.com/maniacbug/RF24/archives/master) +* [Documentation Main Page](http://tmrh20.github.io/) +* [Class Reference]( http://tmrh20.github.io/RF24/class_r_f24.html) +* [Source Code](https://github.com/tmrh20/RF24) +* [Downloads](https://github.com/tmrh20/RF24/archives/master) * [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or diff --git a/RF24.h b/RF24.h index b666a76..ec01c9c 100644 --- a/RF24.h +++ b/RF24.h @@ -221,12 +221,6 @@ protected: public: -//TMRh20 - bool txStandBy(); - bool writeBlocking( const void* buf, uint8_t len ); - void reUseTX(); - bool writeFast( const void* buf, uint8_t len ); //Fills FIFO buffer, uses packet re-use function of the chip and returns 0 if packet failed and re-sent - /** * @name Primary public interface * @@ -249,6 +243,9 @@ public: * Begin operation of the chip * * Call this in setup(), before calling any other methods. + * + * @note Optimization: The radio is partially configured in PTX mode + * when begin is called to make the transition to PTX mode simpler. */ void begin(void); @@ -265,10 +262,15 @@ public: * Stop listening for incoming messages * * Do this before calling write(). + * + * @note Optimization: The radio will be taken out of PRX mode as soon + * as listening is stopped. Enables quicker and simpler engaging of + * primary transmitter (PTX) mode. */ void stopListening(void); /** + * Optimization: Improved performance and reliability * Write to the open writing pipe * * Be sure to call openWritingPipe() first to set the destination @@ -282,15 +284,121 @@ public: * getPayloadSize(). However, you can write less, and the remainder * will just be filled with zeroes. * + * TX/RX/RT interrupt flags will be cleared every time write is called + * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not */ bool write( const void* buf, uint8_t len ); + /** + * Optimization: New Command + * Write to the open writing pipe filling up the FIFO buffers + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This will not block until the 3 FIFO buffers are filled with data. + * Once the FIFOs are full, writeFast will simply return 0. From a user + * perspective, just keep trying to send. The library will keep auto + * retrying the previous payload using the built in functionality. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * TX/RX/RT interrupt flags will be cleared every time write is called + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool writeFast( const void* buf, uint8_t len ); + + /** + * Optimization: New Command + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This will not block until the 3 FIFO buffers are filled with data or + * a timeout is detected. If so the library will auto retry until a new + * payload is written or the TX buffers are flushed. Interrupts can be + * used to control the timeout period. + * + * This will never return a 0. It will not return until a packet is + * sent successfully + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * TX/RX/RT interrupt flags will be cleared every time write is called + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool writeBlocking( const void* buf, uint8_t len ); + + /** + * Optimization: New Command + * This function should be called as soon as transmission is finished to + * drop the radio back to STANDBY-I mode. If not issued, the radio will + * remain in STANDBY-II mode which, per the data sheet, is not a recommended + * operating mode. + * + * @note When transmitting data in rapid succession, it is still recommended by + * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is + * time enough between sends for the FIFOs to empty. + * + * @note This does NOT need to be called when using per-payload noACK commands. + * Per the datasheet, the radio will automatically engage STANDBY-I mode when + * using the W_TX_PAYLOAD_NOACK command. + * + * @code + * Example: + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * while( !txStandBy() ){} //Waits for TX complete or timeout + * + * @endcode + * + * @return True if transmission is finished and the radio has been commanded + * to enter STANDBY-I operating mode. + * + */ + bool txStandBy(); + + /** + * Optimization: New Command + * + * This function is mainly used internally to take advantage of the auto payload + * re-use functionality of the chip, but can be beneficial to users as well. + * + * The function will instruct the radio to re-use the data in the FIFO buffers, + * and instructs the radio to re-send once the timeout limit has been reached. + * Used by writeFast and writeBlocking to initiate retries when a TX failure + * occurs. Retries are automatically initiated except with the standard write(). + * This way, data is not flushed from the buffer until switching between modes. + * + * @note This is to be used AFTER auto-retry fails if wanting to resend + * using the built-in payload reuse features. + */ + void reUseTX(); + /** * Test whether there are bytes available to be read * + * @note Optimization: The available functino now checks the FIFO + * buffers directly for data instead of relying of interrupt flags. + * + * @note: Interrupt flags will not be cleared until a payload is + * actually read from the FIFO + * * @return True if there is a payload available, false if none is */ bool available(void); @@ -298,16 +406,18 @@ public: /** * Read the payload * - * Return the last payload received - * * The size of data read is the fixed payload size, see getPayloadSize() * * @note I specifically chose 'void*' as a data type to make it easier * for beginners to use. No casting needed. * + * @note Optimization: No longer boolean. Use available to + * determine if packets are available. Interrupt flags are now cleared + * during reads instead of when calling available(). + * * @param buf Pointer to a buffer where the data should be written * @param len Maximum number of bytes to read into the buffer - * @return True if the payload was delivered successfully false if not + * @return No return value. Use available. */ void read( void* buf, uint8_t len ); @@ -411,6 +521,9 @@ public: * For dynamic payloads, this pulls the size of the payload off * the chip * + * Optimization: Corrupt packets are now detected and flushed per the + * manufacturer. + * * @return Payload length of last-received dynamic payload */ uint8_t getDynamicPayloadSize(void); @@ -544,6 +657,9 @@ public: * * To return to normal power mode, either write() some data or * startListening, or powerUp(). + * + * Optimization: The radio will never enter power down unless instructed + * by the MCU via this command. */ void powerDown(void); @@ -555,10 +671,12 @@ public: void powerUp(void) ; /** - * Test whether there are bytes available to be read + * Test whether there are bytes available to be read in the + * FIFO buffers. This optimized version does not rely on interrupt + * flags, but checks the actual FIFO buffers. * - * Use this version to discover on which pipe the message - * arrived. + * @note Optimization: Interrupt flags are no longer cleared when available is called, + * but will be reset only when the data is read from the FIFO buffers. * * @param[out] pipe_num Which pipe has the payload available * @return True if there is a payload available, false if none is @@ -571,6 +689,10 @@ public: * Just like write(), but it returns immediately. To find out what happened * to the send, catch the IRQ and then call whatHappened(). * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * * @see write() * @see whatHappened() * @@ -587,7 +709,7 @@ public: * be sent back in the acknowledgement. * * @warning According to the data sheet, only three of these can be pending - * at any time. I have not tested this. + * at any time as there are only 3 FIFO buffers. * * @param pipe Which pipe# (typically 1-5) will get this response. * @param buf Pointer to data that is sent @@ -602,10 +724,9 @@ public: * * Call read() to retrieve the ack payload. * - * @warning Calling this function clears the internal flag which indicates - * a payload is available. If it returns true, you must read the packet - * out as the very next interaction with the radio, or the results are - * undefined. + * @note Optimization: Calling this function NO LONGER clears the interrupt + * flag. The new functionality checks the RX FIFO buffer for an ACK payload + * instead of relying on interrupt flags. * * @return True if an ack payload is available. */ @@ -764,62 +885,41 @@ public: */ /** - * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + * @mainpage Optimized High Speed Driver for nRF24L01(+) 2.4GHz Wireless Transceiver * * @section Goals Design Goals * - * This library is designed to be... - * @li Maximally compliant with the intended operation of the chip + * This library fork is designed to be... + * @li More compliant with the manufacturer specified operation of the chip + * @li Utilize the capabilities of the radio to their full potential via Arduino + * @li More reliable, responsive and feature rich * @li Easy for beginners to use * @li Consumed with a public interface that's similiar to other Arduino standard libraries * * @section News News * - * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. - * Please open an issue if you find any problems using it with any version of Arduino. - * - * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the - * Maple Native, - * and should work with any Maple board. See the pingpair_maple example. - * Note that only the pingpair_maple example has been tested on Maple, although - * the others can certainly be adapted. + * March 2014: Fork currently being optimized for high speed and more responsive data transfers * * @section Useful Useful References * * Please refer to: * - * @li Documentation Main Page - * @li RF24 Class Documentation - * @li Source Code - * @li Downloads Page + * @li Documentation Main Page + * @li RF24 Class Documentation + * @li Source Code + * @li Downloads Page * @li Chip Datasheet + * @li Original Library * * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or * the SPI hardware will go into 'slave' mode. * * @section More More Information * - * @subpage FAQ + * @section Info and Projects * - * @section Projects Projects - * - * Stuff I have built with RF24 - * - * RF24 Getting Started - Finished Product - * - * Getting Started with nRF24L01+ on Arduino - * - * Nordic FOB and nRF24L01+ - * - * Using the Sparkfun Nordic FOB - * - * RF Duinode V3 (2V4) - * - * Low-Power Wireless Sensor Node - * - * nRF24L01+ connected to Leaf Labs Maple Native - * - * nRF24L01+ Running on Maple + * @li Project blog: + * @li TMRh20.blogspot.com */ #endif // __RF24_H__ From 63ee0ce2c077373e4d25cc4ef30fac1036df6854 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 19:27:07 -0600 Subject: [PATCH 206/326] Corrected part of the documentation --- RF24.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RF24.h b/RF24.h index ec01c9c..a7ccc6a 100644 --- a/RF24.h +++ b/RF24.h @@ -308,7 +308,7 @@ public: * getPayloadSize(). However, you can write less, and the remainder * will just be filled with zeroes. * - * TX/RX/RT interrupt flags will be cleared every time write is called + * ONLY max retry interrupt flags will be cleared when writeFast is called * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent @@ -335,7 +335,7 @@ public: * getPayloadSize(). However, you can write less, and the remainder * will just be filled with zeroes. * - * TX/RX/RT interrupt flags will be cleared every time write is called + * ONLY max retry interrupt flags will be cleared when writeBlocking is called * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent @@ -899,7 +899,7 @@ public: * @section News News * * March 2014: Fork currently being optimized for high speed and more responsive data transfers - * + * If issues are discovered with the documentation, please report them here: here * @section Useful Useful References * * Please refer to: From 99571d653001b143d8305425e2eab193136b38fe Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 20:23:33 -0600 Subject: [PATCH 207/326] Updated some examples to show the new lib usage Updated GettingStarted, pingpair, added medium speed version of transfer rate test --- RF24.h | 6 +- examples/GettingStarted/GettingStarted.ino | 179 ++++++++++++++++++ examples/Transfer/Transfer.ino | 163 ++++++++++++++++ examples/Transfer/printf.h | 37 ++++ .../pingpair/{pingpair.pde => pingpair.ino} | 108 ++++------- 5 files changed, 419 insertions(+), 74 deletions(-) create mode 100644 examples/GettingStarted/GettingStarted.ino create mode 100644 examples/Transfer/Transfer.ino create mode 100644 examples/Transfer/printf.h rename examples/pingpair/{pingpair.pde => pingpair.ino} (55%) diff --git a/RF24.h b/RF24.h index a7ccc6a..199bd88 100644 --- a/RF24.h +++ b/RF24.h @@ -301,8 +301,8 @@ public: * * This will not block until the 3 FIFO buffers are filled with data. * Once the FIFOs are full, writeFast will simply return 0. From a user - * perspective, just keep trying to send. The library will keep auto - * retrying the previous payload using the built in functionality. + * perspective, just keep trying to send the same data. The library will + * keep auto retrying the previous payload using the built in functionality. * * The maximum size of data written is the fixed payload size, see * getPayloadSize(). However, you can write less, and the remainder @@ -329,7 +329,7 @@ public: * used to control the timeout period. * * This will never return a 0. It will not return until a packet is - * sent successfully + * loaded successfully into the FIFO and TX is complete. * * The maximum size of data written is the fixed payload size, see * getPayloadSize(). However, you can write less, and the remainder diff --git a/examples/GettingStarted/GettingStarted.ino b/examples/GettingStarted/GettingStarted.ino new file mode 100644 index 0000000..d74db81 --- /dev/null +++ b/examples/GettingStarted/GettingStarted.ino @@ -0,0 +1,179 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Example for Getting Started with nRF24L01+ radios. + * + * This is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +//March 2014 - TMRh20 - Updated to utilize High Speed RF24 Library fork +//This + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// Hardware configuration +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(48,49); + +// Topology +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +// +// Role management +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role = role_pong_back; + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // + // Setup and configure rf radio + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + + radio.setRetries(15,15); // Optionally, increase the delay between retries & # of retries + + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + + radio.startListening(); // Start listening + + radio.printDetails(); // Dump the configuration of the rf unit for debugging +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + + radio.stopListening(); // First, stop listening so we can talk. + + unsigned long time = millis(); // Take the time, and send it. This will block until complete + printf("Now sending %lu...",time); + bool ok = radio.writeFast( &time, sizeof(unsigned long) );//New function for proper use of FIFO buffers + while( ! radio.txStandBy() ){} //Called when STANDBY-I mode is engaged (User is finished sending) + if (!ok) + printf("failed.\n\r"); + + + radio.startListening(); // Now, continue listening + + unsigned long started_waiting_at = millis(); // Wait here until we get a response, or timeout (250ms) + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + if ( timeout ) // Describe the results + { + printf("Failed, response timed out.\n\r"); + } + else + { + unsigned long got_time; // Grab the response, compare, and send to debugging spew + radio.read( &got_time, sizeof(unsigned long) ); + unsigned long tim = millis(); + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,tim-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + if( radio.available()){ + unsigned long got_time; // Dump the payloads until we've gotten everything + bool done = false; + while (radio.available()) { // While there is data ready + // Fetch the payload, and see if this was the last one. + radio.read( &got_time, sizeof(unsigned long) ); + } + // Spew it + //printf("Got payload %lu...",got_time); + + // Delay just a little bit to let the other unit + // make the transition to receiver + //delay(2); + + radio.stopListening(); // First, stop listening so we can talk + + radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. + + radio.startListening(); // Now, resume listening so we catch the next packets. + + //printf("Sent response.\n\r"); + + } + } + + // + // Change roles + // + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + role = role_ping_out; // Become the primary transmitter (ping out) + //radio.openWritingPipe(pipes[0]); + //radio.openReadingPipe(1,pipes[1]); + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + role = role_pong_back; // Become the primary receiver (pong back) + //radio.openWritingPipe(pipes[1]); + //radio.openReadingPipe(1,pipes[0]); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/examples/Transfer/Transfer.ino b/examples/Transfer/Transfer.ino new file mode 100644 index 0000000..5949105 --- /dev/null +++ b/examples/Transfer/Transfer.ino @@ -0,0 +1,163 @@ +/* +TMRh20 2014 + + 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. + */ + +/** General Data Transfer Rate Test + * This example demonstrates basic data transfer functionality with the + updated library. This example will display the transfer rates acheived using + the slower form of high-speed transfer using blocking-writes. + */ + + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// Hardware configuration +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(48,49); + +// Topology +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +// Role management +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. + +// The various roles supported by this sketch +typedef enum { role_TX = 1, role_RX } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +//Set this to determine the default role of the current running sketch +role_e role = role_RX; + +//Data buffer for testing data transfer speeds +byte data[32]; + +//Counter and timer for keeping track transfer info +unsigned long counter, rxTimer; +float startTime, stopTime; + +void setup(void) +{ + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + radio.begin(); // Setup and configure rf radio + radio.setChannel(1); + radio.setPALevel(RF24_PA_MAX); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(2,3); // Optionally, increase the delay between retries & # of retries + //radio.disableCRC(); + radio.setCRCLength(RF24_CRC_8); + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + + radio.startListening(); // Start listening + radio.printDetails(); // Dump the configuration of the rf unit for debugging + + randomSeed(analogRead(0)); //Seed for random number generation + + for(int i=0; i<32; i++){ + data[i] = random(255); //Load the buffer with random data + } + radio.powerUp(); +} + +void loop(void){ + + + //TX with pre-blocking writes + if(role == role_TX){ + + delay(1500); + + printf("Initiating Basic Data Transfer\n\r"); + + + float cycles = 10000; + + startTime = millis(); + + for(int i=0; i 1000){ + rxTimer = millis(); +// printf("Rx Rate: "); +// Serial.print(counter*32/1000); +// printf(" KB/s\n\r"); + printf("Payload Count: %d \n\r", counter); + counter = 0; + } + + } + + // + // Change roles + // + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_RX ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + radio.stopListening(); + role = role_TX; // Become the primary transmitter (ping out) + //radio.openWritingPipe(pipes[0]); + //radio.openReadingPipe(1,pipes[1]); + } + else if ( c == 'R' && role == role_TX ) + { + radio.startListening(); + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + role = role_RX; // Become the primary receiver (pong back) + + //radio.openWritingPipe(pipes[1]); + //radio.openReadingPipe(1,pipes[0]); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/Transfer/printf.h b/examples/Transfer/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/examples/Transfer/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/examples/pingpair/pingpair.pde b/examples/pingpair/pingpair.ino similarity index 55% rename from examples/pingpair/pingpair.pde rename to examples/pingpair/pingpair.ino index 3a57a67..80baba5 100644 --- a/examples/pingpair/pingpair.pde +++ b/examples/pingpair/pingpair.ino @@ -15,6 +15,8 @@ * took. */ +//March 2014 TMRh20 - Updated to utilize High Speed RF24 Library fork + #include #include "nRF24L01.h" #include "RF24.h" @@ -22,20 +24,14 @@ // // Hardware configuration -// - // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter -const int role_pin = 7; +const int role_pin = 5; -// // Topology -// - // Radio pipe addresses for the 2 nodes to communicate. const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; @@ -63,14 +59,11 @@ void setup(void) // // Role // + pinMode(role_pin, INPUT); // set up the role pin + digitalWrite(role_pin,HIGH); //Change this to HIGH/LOW to pre-choose a role + delay(20); // Just to get a solid reading on the role pin - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( ! digitalRead(role_pin) ) + if ( ! digitalRead(role_pin) ) // read the address pin, establish our role role = role_ping_out; else role = role_pong_back; @@ -89,13 +82,10 @@ void setup(void) // radio.begin(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); - - // optionally, reduce the payload size. seems to - // improve reliability - radio.setPayloadSize(8); + + radio.setRetries(15,15); // optionally, increase the delay between retries & # of retries + + radio.setPayloadSize(8); // optionally, reduce the payload size. seems to improve reliability // // Open pipes to other nodes for communication @@ -117,17 +107,9 @@ void setup(void) radio.openReadingPipe(1,pipes[0]); } - // - // Start listening - // + radio.startListening(); // Start listening - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); + radio.printDetails(); // Dump the configuration of the rf unit for debugging } void loop(void) @@ -137,47 +119,39 @@ void loop(void) // if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); + { + radio.stopListening(); // First, stop listening so we can talk. - // Take the time, and send it. This will block until complete - unsigned long time = millis(); + unsigned long time = millis(); // Take the time, and send it. This will block until complete printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); + bool ok = radio.writeFast( &time, sizeof(unsigned long) ); // NEW command to buffer data more efficiently - if (ok) - printf("ok..."); - else + if (!ok) printf("failed.\n\r"); - // Now, continue listening - radio.startListening(); + while(!radio.txStandBy()){} //NEW command needs to be called after done sending. Allows FIFO buffers to be filled + + radio.startListening(); // Now, continue listening - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); + unsigned long started_waiting_at = millis(); // Wait here until we get a response, or timeout (250ms) bool timeout = false; while ( ! radio.available() && ! timeout ) if (millis() - started_waiting_at > 200 ) timeout = true; - // Describe the results - if ( timeout ) + if ( timeout ) // Describe the results { printf("Failed, response timed out.\n\r"); } else { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; + unsigned long got_time; // Grab the response, compare, and send to debugging spew radio.read( &got_time, sizeof(unsigned long) ); - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); // Spew it } - // Try again 1s later - delay(1000); + delay(1000); // Try again 1s later } // @@ -186,35 +160,27 @@ void loop(void) if ( role == role_pong_back ) { - // if there is data ready - if ( radio.available() ) + if ( radio.available() ) // if there is data ready { - // Dump the payloads until we've gotten everything - unsigned long got_time; + unsigned long got_time; // Dump the payloads until we've gotten everything bool done = false; - while (!done) + while (radio.available()) { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); + radio.read( &got_time, sizeof(unsigned long) ); // Fetch the payload, and see if this was the last one. + } + printf("Got payload %lu...",got_time); // Spew it // Delay just a little bit to let the other unit // make the transition to receiver - delay(20); - } + //delay(20); - // First, stop listening so we can talk - radio.stopListening(); + radio.stopListening(); // First, stop listening so we can talk - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); + radio.write( &got_time, sizeof(unsigned long) );// Send the final one back. printf("Sent response.\n\r"); - // Now, resume listening so we catch the next packets. - radio.startListening(); + radio.startListening(); // Now, resume listening so we catch the next packets. } } } -// vim:cin:ai:sts=2 sw=2 ft=cpp +// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file From bcd48f2f471e0c9536b30b0be7ead0f62f163f5a Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 20:47:37 -0600 Subject: [PATCH 208/326] Fix: Standby after regular write cmd Realized that the normal write command should always drop to standby-I mode immediately since the FIFO cannot be filled using this method, and TX will be complete. --- RF24.cpp | 2 ++ RF24.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 5ef039d..d2b4025 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -455,6 +455,8 @@ bool RF24::write( const void* buf, uint8_t len ) //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + ce(LOW); //Set the radio back to STANDBY-I mode since we can only fill one buffer at a time using this method, and tx is complete + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); //Max retries exceeded diff --git a/RF24.h b/RF24.h index 199bd88..158fc9f 100644 --- a/RF24.h +++ b/RF24.h @@ -354,7 +354,9 @@ public: * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is * time enough between sends for the FIFOs to empty. * - * @note This does NOT need to be called when using per-payload noACK commands. + * @note This does NOT need to be called when using per-payload noACK commands, + * or when using the regular write command since it is only capable of single + * payload transmission.. * Per the datasheet, the radio will automatically engage STANDBY-I mode when * using the W_TX_PAYLOAD_NOACK command. * From 4d8da2f01de1d5e4ed2354d28f46c0ffec2a49d7 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 19 Mar 2014 21:16:38 -0600 Subject: [PATCH 209/326] Fixed link --- RF24.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.h b/RF24.h index 158fc9f..629cdab 100644 --- a/RF24.h +++ b/RF24.h @@ -907,7 +907,7 @@ public: * Please refer to: * * @li Documentation Main Page - * @li RF24 Class Documentation + * @li RF24 Class Documentation * @li Source Code * @li Downloads Page * @li Chip Datasheet From f3b3fb1f444f0e0c6d32d4731860d966d8779272 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 22 Mar 2014 01:52:22 -0600 Subject: [PATCH 210/326] Fix: txStandBy hang. Updated Transfer example - txStandBy was hanging if the last of the data timed out. Now clears the MAX_RT flag if timed out - Finished updating example file Transfer.ino for demonstrating half-rate transfers using the FIFO buffers --- RF24.cpp | 6 +- RF24.h | 32 +++++++--- examples/Transfer/Transfer.ino | 113 ++++++++++++++------------------- 3 files changed, 73 insertions(+), 78 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index d2b4025..190e67d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -524,7 +524,7 @@ bool RF24::writeFast( const void* buf, uint8_t len ) } } - //Start Writing + //Start Writing startWrite(buf,len); return 1; @@ -553,6 +553,10 @@ bool RF24::txStandBy(){ ce(LOW); return 1; } + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + return 0; + } return 0; } diff --git a/RF24.h b/RF24.h index 629cdab..7f242ea 100644 --- a/RF24.h +++ b/RF24.h @@ -270,7 +270,7 @@ public: void stopListening(void); /** - * Optimization: Improved performance and reliability + * @note Optimization: Improved performance and reliability * Write to the open writing pipe * * Be sure to call openWritingPipe() first to set the destination @@ -293,16 +293,17 @@ public: bool write( const void* buf, uint8_t len ); /** - * Optimization: New Command + * @note Optimization: New Command * Write to the open writing pipe filling up the FIFO buffers * * Be sure to call openWritingPipe() first to set the destination * of where to write to. * * This will not block until the 3 FIFO buffers are filled with data. - * Once the FIFOs are full, writeFast will simply return 0. From a user - * perspective, just keep trying to send the same data. The library will - * keep auto retrying the previous payload using the built in functionality. + * Once the FIFOs are full, writeFast will simply wait for success or + * timeout, and return 1 or 0 respectively. From a user perspective, just + * keep trying to send the same data. The library will keep auto retrying + * the current payload using the built in functionality. * * The maximum size of data written is the fixed payload size, see * getPayloadSize(). However, you can write less, and the remainder @@ -317,7 +318,7 @@ public: bool writeFast( const void* buf, uint8_t len ); /** - * Optimization: New Command + * @note Optimization: New Command * Write to the open writing pipe * * Be sure to call openWritingPipe() first to set the destination @@ -344,7 +345,7 @@ public: bool writeBlocking( const void* buf, uint8_t len ); /** - * Optimization: New Command + * @note Optimization: New Command * This function should be called as soon as transmission is finished to * drop the radio back to STANDBY-I mode. If not issued, the radio will * remain in STANDBY-II mode which, per the data sheet, is not a recommended @@ -781,8 +782,8 @@ public: }; /** - * @example GettingStarted.pde - * + * @example GettingStarted.ino + * Updated: TMRh20 * This is an example which corresponds to my "Getting Started" blog post: * Getting Started with nRF24L01+ on Arduino. * @@ -793,6 +794,17 @@ public: * node can then see how long the whole cycle took. */ +/** + * @example Transfer.ino + * Updated: TMRh20 + * This example demonstrates half-rate transfer using the FIFO buffers + * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The data transfer will begin, + * with the receiver displaying the payload count. (32Byte Payloads) + */ + /** * @example nordic_fob.pde * @@ -812,7 +824,7 @@ public: */ /** - * @example pingpair.pde + * @example pingpair.ino * * This is an example of how to use the RF24 class. Write this sketch to two * different nodes, connect the role_pin to ground on one. The ping node sends diff --git a/examples/Transfer/Transfer.ino b/examples/Transfer/Transfer.ino index 5949105..080c208 100644 --- a/examples/Transfer/Transfer.ino +++ b/examples/Transfer/Transfer.ino @@ -19,105 +19,92 @@ TMRh20 2014 #include "RF24.h" #include "printf.h" -// Hardware configuration -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +/************* USER Configuration *****************************/ + // Hardware configuration +RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +boolean RADIO_NO = 0; //SET THIS TO 0 or 1 for 1st or 2nd radio -RF24 radio(48,49); +/***************************************************************/ -// Topology -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. -// Role management -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. +byte data[32]; //Data buffer for testing data transfer speeds -// The various roles supported by this sketch -typedef enum { role_TX = 1, role_RX } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -//Set this to determine the default role of the current running sketch -role_e role = role_RX; - -//Data buffer for testing data transfer speeds -byte data[32]; - -//Counter and timer for keeping track transfer info -unsigned long counter, rxTimer; +unsigned long counter, rxTimer; //Counter and timer for keeping track transfer info float startTime, stopTime; +bool TX=1,RX=0,role=0; -void setup(void) -{ +void setup(void) { Serial.begin(57600); printf_begin(); - printf("\n\rRF24/examples/GettingStarted/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - radio.begin(); // Setup and configure rf radio + radio.begin(); // Setup and configure rf radio radio.setChannel(1); radio.setPALevel(RF24_PA_MAX); - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.setRetries(2,3); // Optionally, increase the delay between retries & # of retries - //radio.disableCRC(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(1,1); // Optionally, increase the delay between retries & # of retries radio.setCRCLength(RF24_CRC_8); - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - + + if(RADIO_NO){ //Lets the two radios choose the correct pipes depending on user selection + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + }else{ + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + radio.startListening(); // Start listening radio.printDetails(); // Dump the configuration of the rf unit for debugging + printf("\n\rRF24/examples/Transfer Rates/\n\r"); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + randomSeed(analogRead(0)); //Seed for random number generation for(int i=0; i<32; i++){ data[i] = random(255); //Load the buffer with random data } - radio.powerUp(); + radio.powerUp(); //Power up the radio } void loop(void){ - - //TX with pre-blocking writes - if(role == role_TX){ + + if(role == TX){ delay(1500); printf("Initiating Basic Data Transfer\n\r"); - float cycles = 10000; + float cycles = 1000; //Change this to a higher or lower number. startTime = millis(); - for(int i=0; i Date: Sat, 22 Mar 2014 18:39:46 -0600 Subject: [PATCH 211/326] Lib now passes all tests Added back original startWrite function for when users want to use manual methods. The library uses startFastWrite for its internal functions. Provides compatibility with prev. code while still using the new methods for everything else. --- RF24.cpp | 28 +++++++++++++++++++++++----- RF24.h | 25 +++++++++++++++++++++---- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 190e67d..ec6c448 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -448,7 +448,7 @@ void RF24::powerUp(void) bool RF24::write( const void* buf, uint8_t len ) { //Start Writing - startWrite(buf,len); + startFastWrite(buf,len); //Wait until complete or failed //ACK payloads that are handled improperly will cause this to hang @@ -487,7 +487,7 @@ bool RF24::writeBlocking( const void* buf, uint8_t len ) } //Start Writing - startWrite(buf,len); + startFastWrite(buf,len); return 1; } @@ -525,7 +525,7 @@ bool RF24::writeFast( const void* buf, uint8_t len ) } //Start Writing - startWrite(buf,len); + startFastWrite(buf,len); return 1; } @@ -539,7 +539,7 @@ bool RF24::writeFast( const void* buf, uint8_t len ) //Otherwise we enter Standby-II mode, which is still faster than standby mode //Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data -void RF24::startWrite( const void* buf, uint8_t len ){ //TMRh20 +void RF24::startFastWrite( const void* buf, uint8_t len ){ //TMRh20 write_payload( buf,len); ce(HIGH); @@ -547,6 +547,24 @@ void RF24::startWrite( const void* buf, uint8_t len ){ //TMRh20 } +//Added the original startWrite back in so users can still use interrupts, ack payloads, etc +//Allows the library to pass all tests +void RF24::startWrite( const void* buf, uint8_t len ) +{ + // Transmitter power-up + write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); + delayMicroseconds(150); + + // Send the payload + write_payload( buf, len ); + + // Allons! + ce(HIGH); + delayMicroseconds(15); + ce(LOW); +} + + bool RF24::txStandBy(){ if ( (read_register(FIFO_STATUS) & _BV(TX_EMPTY))){ @@ -613,7 +631,7 @@ void RF24::read( void* buf, uint8_t len ){ read_payload( buf, len ); //Clear the two possible interrupt flags with one command - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) ); + write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); } diff --git a/RF24.h b/RF24.h index 7f242ea..840b26c 100644 --- a/RF24.h +++ b/RF24.h @@ -66,6 +66,22 @@ protected: */ /**@{*/ + /** + * Non-blocking write to the open writing pipe used for buffered writes + * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * + * @see writeFast() + * @see writeBlocking() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + void startFastWrite( const void* buf, uint8_t len ); + /** * Set chip select pin * @@ -692,16 +708,16 @@ public: * Just like write(), but it returns immediately. To find out what happened * to the send, catch the IRQ and then call whatHappened(). * - * @note Optimization: This function now leaves the CE pin high, so the radio - * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. - * This allows the chip to be used to its full potential in TX mode. + * @note Optimization: This function again behaves as it did previously. + * startFastWrite() has been moved to an internal function * * @see write() + * @see startFastWrite() * @see whatHappened() * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not + * */ void startWrite( const void* buf, uint8_t len ); @@ -934,6 +950,7 @@ public: * * @li Project blog: * @li TMRh20.blogspot.com + * @li ManiacBug on GitHub (Original Library Author) */ #endif // __RF24_H__ From 1cc7454d97d0995e443745c788e37e1f9192d62e Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 22 Mar 2014 20:32:31 -0600 Subject: [PATCH 212/326] txStandby() function modified completely txStandBy() now either works in two modes. The main mode will block until success or fail. Using txStandBy(1) will block and retry forever until succesful. Using a while loop with the function is no longer required. See the updated class reference at http://tmrh20.github.io/RF24/class_r_f24.html --- RF24.cpp | 30 ++++++++++++++++-------- RF24.h | 69 +++++++++++++++++++++++++++++--------------------------- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index ec6c448..bbd9b02 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -564,18 +564,30 @@ void RF24::startWrite( const void* buf, uint8_t len ) ce(LOW); } - bool RF24::txStandBy(){ + txStandBy(0); +} - if ( (read_register(FIFO_STATUS) & _BV(TX_EMPTY))){ - ce(LOW); - return 1; +bool RF24::txStandBy(bool block){ + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + if(block){ + ce(LOW); //Set re-transmit + ce(HIGH); + delayMicroseconds(15); + + }else{ + flush_tx(); //Non blocking, flush the data + ce(LOW); //Set STANDBY-I mode + return 0; + } + } } - if( get_status() & _BV(MAX_RT)){ - write_register(STATUS,_BV(MAX_RT) ); - return 0; - } - return 0; + ce(LOW); //Set STANDBY-I mode + return 1; + } /****************************************************************************/ diff --git a/RF24.h b/RF24.h index 840b26c..b31e2ea 100644 --- a/RF24.h +++ b/RF24.h @@ -382,32 +382,18 @@ public: * radio.writeFast(&buf,32); * radio.writeFast(&buf,32); * radio.writeFast(&buf,32); //Fills the FIFO buffers up - * while( !txStandBy() ){} //Waits for TX complete or timeout + * bool ok = txStandBy(0); //Returns 0 if failed. 1 if success. + * //Blocks only until timeout or success. Data flushed on fail. + * + * Using txStandBy(1) will not return until the data is transmitted. It will never return 0. * * @endcode * - * @return True if transmission is finished and the radio has been commanded - * to enter STANDBY-I operating mode. + * @return True if transmission is successful * */ bool txStandBy(); - - /** - * Optimization: New Command - * - * This function is mainly used internally to take advantage of the auto payload - * re-use functionality of the chip, but can be beneficial to users as well. - * - * The function will instruct the radio to re-use the data in the FIFO buffers, - * and instructs the radio to re-send once the timeout limit has been reached. - * Used by writeFast and writeBlocking to initiate retries when a TX failure - * occurs. Retries are automatically initiated except with the standard write(). - * This way, data is not flushed from the buffer until switching between modes. - * - * @note This is to be used AFTER auto-retry fails if wanting to resend - * using the built-in payload reuse features. - */ - void reUseTX(); + bool txStandBy(bool block); /** * Test whether there are bytes available to be read @@ -689,19 +675,6 @@ public: */ void powerUp(void) ; - /** - * Test whether there are bytes available to be read in the - * FIFO buffers. This optimized version does not rely on interrupt - * flags, but checks the actual FIFO buffers. - * - * @note Optimization: Interrupt flags are no longer cleared when available is called, - * but will be reset only when the data is read from the FIFO buffers. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - bool available(uint8_t* pipe_num); - /** * Non-blocking write to the open writing pipe * @@ -721,6 +694,23 @@ public: */ void startWrite( const void* buf, uint8_t len ); + /** + * Optimization: New Command + * + * This function is mainly used internally to take advantage of the auto payload + * re-use functionality of the chip, but can be beneficial to users as well. + * + * The function will instruct the radio to re-use the data in the FIFO buffers, + * and instructs the radio to re-send once the timeout limit has been reached. + * Used by writeFast and writeBlocking to initiate retries when a TX failure + * occurs. Retries are automatically initiated except with the standard write(). + * This way, data is not flushed from the buffer until switching between modes. + * + * @note This is to be used AFTER auto-retry fails if wanting to resend + * using the built-in payload reuse features. + */ + void reUseTX(); + /** * Write an ack payload for the specified pipe * @@ -751,6 +741,19 @@ public: */ bool isAckPayloadAvailable(void); + /** + * Test whether there are bytes available to be read in the + * FIFO buffers. This optimized version does not rely on interrupt + * flags, but checks the actual FIFO buffers. + * + * @note Optimization: Interrupt flags are no longer cleared when available is called, + * but will be reset only when the data is read from the FIFO buffers. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + bool available(uint8_t* pipe_num); + /** * Call this when you get an interrupt to find out why * From 0ddec5256343186aafef9d4c653d12ace6a2e712 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 23 Mar 2014 01:05:04 -0600 Subject: [PATCH 213/326] Forgot to move startFastWrite to public for use in wireless audio sketch, needed to move startWrite() to a public function --- RF24.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/RF24.h b/RF24.h index b31e2ea..78f5322 100644 --- a/RF24.h +++ b/RF24.h @@ -66,22 +66,6 @@ protected: */ /**@{*/ - /** - * Non-blocking write to the open writing pipe used for buffered writes - * - * @note Optimization: This function now leaves the CE pin high, so the radio - * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. - * This allows the chip to be used to its full potential in TX mode. - * - * @see writeFast() - * @see writeBlocking() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - void startFastWrite( const void* buf, uint8_t len ); - /** * Set chip select pin * @@ -395,6 +379,22 @@ public: bool txStandBy(); bool txStandBy(bool block); + /** + * Non-blocking write to the open writing pipe used for buffered writes + * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * + * @see writeFast() + * @see writeBlocking() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + void startFastWrite( const void* buf, uint8_t len ); + /** * Test whether there are bytes available to be read * From 6263bccd0241aaf0c0e45106d514ea34516f39e9 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 29 Mar 2014 05:29:29 -0600 Subject: [PATCH 214/326] Extended timeouts, txStandBy changes - For use in noisy or low signal scenarios - Allows user specified timeout period of extended duration - Modified txStandBy() and writeBlocking() functions to rely on a user defined timeout period, automatic retries and payload reUse --- RF24.cpp | 43 ++-- RF24.h | 172 ++++++++----- examples/GettingStarted/GettingStarted.ino | 17 +- examples/GettingStarted/GettingStarted.pde | 227 ------------------ .../TransferTimeouts/TransferTimeouts.ino | 189 +++++++++++++++ examples/TransferTimeouts/printf.h | 37 +++ 6 files changed, 366 insertions(+), 319 deletions(-) delete mode 100644 examples/GettingStarted/GettingStarted.pde create mode 100644 examples/TransferTimeouts/TransferTimeouts.ino create mode 100644 examples/TransferTimeouts/printf.h diff --git a/RF24.cpp b/RF24.cpp index bbd9b02..e0713ac 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -471,25 +471,27 @@ bool RF24::write( const void* buf, uint8_t len ) /****************************************************************************/ //For general use, the interrupt flags are not important to clear -bool RF24::writeBlocking( const void* buf, uint8_t len ) +bool RF24::writeBlocking( const void* buf, uint8_t len, unsigned long timeout ) { //Block until the FIFO is NOT full. //Keep track of the MAX retries and set auto-retry if seeing failures //This way the FIFO will fill up and allow blocking until packets go through //The radio will auto-clear everything in the FIFO as long as CE remains high + unsigned long timer = millis(); //Get the time that the payload transmission started while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful - if( get_status() & _BV(MAX_RT)){ - reUseTX(); //Set re-transmit + if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached + reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag } - + if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 } - //Start Writing - startFastWrite(buf,len); - return 1; + //Start Writing + startFastWrite(buf,len); //Write the payload if a buffer is clear + + return 1; //Return 1 to indicate successful transmission } @@ -504,7 +506,6 @@ void RF24::reUseTX(){ /****************************************************************************/ -//This is for when every bit of data is important bool RF24::writeFast( const void* buf, uint8_t len ) { //Block until the FIFO is NOT full. @@ -516,9 +517,7 @@ bool RF24::writeFast( const void* buf, uint8_t len ) while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful if( get_status() & _BV(MAX_RT)){ - write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag reUseTX(); //Set re-transmit - delayMicroseconds(15); //CE needs to stay high for 10us, for TX_REUSE to engage return 0; //Return 0. The previous payload has been retransmitted //From the user perspective, if you get a 0, just keep trying to send the same payload } @@ -565,23 +564,31 @@ void RF24::startWrite( const void* buf, uint8_t len ) } bool RF24::txStandBy(){ - txStandBy(0); + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + flush_tx(); //Non blocking, flush the data + ce(LOW); //Set STANDBY-I mode + return 0; + } + } + + ce(LOW); //Set STANDBY-I mode + return 1; } -bool RF24::txStandBy(bool block){ +bool RF24::txStandBy(unsigned long timeout){ + + unsigned long start = millis(); while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ if( get_status() & _BV(MAX_RT)){ write_register(STATUS,_BV(MAX_RT) ); - if(block){ + if(timeout > 0){ ce(LOW); //Set re-transmit ce(HIGH); - delayMicroseconds(15); + if(millis() - start > timeout){ ce(LOW); flush_tx(); return 0; } - }else{ - flush_tx(); //Non blocking, flush the data - ce(LOW); //Set STANDBY-I mode - return 0; } } } diff --git a/RF24.h b/RF24.h index 78f5322..35e262c 100644 --- a/RF24.h +++ b/RF24.h @@ -152,13 +152,6 @@ protected: */ uint8_t flush_rx(void); - /** - * Empty the transmit buffer - * - * @return Current value of status register - */ - uint8_t flush_tx(void); - /** * Retrieve the current status of the chip * @@ -270,7 +263,7 @@ public: void stopListening(void); /** - * @note Optimization: Improved performance and reliability + * @note Optimization: Improved performance slightly * Write to the open writing pipe * * Be sure to call openWritingPipe() first to set the destination @@ -293,56 +286,57 @@ public: bool write( const void* buf, uint8_t len ); /** - * @note Optimization: New Command - * Write to the open writing pipe filling up the FIFO buffers - * - * Be sure to call openWritingPipe() first to set the destination - * of where to write to. - * + * @note Optimization: New Command * * This will not block until the 3 FIFO buffers are filled with data. * Once the FIFOs are full, writeFast will simply wait for success or * timeout, and return 1 or 0 respectively. From a user perspective, just * keep trying to send the same data. The library will keep auto retrying * the current payload using the built in functionality. * - * The maximum size of data written is the fixed payload size, see - * getPayloadSize(). However, you can write less, and the remainder - * will just be filled with zeroes. - * * ONLY max retry interrupt flags will be cleared when writeFast is called * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. + * @endcode + * + * @see txStandBy() + * @see write() + * @see writeBlocking() + * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent * @return True if the payload was delivered successfully false if not */ bool writeFast( const void* buf, uint8_t len ); - /** - * @note Optimization: New Command - * Write to the open writing pipe - * - * Be sure to call openWritingPipe() first to set the destination - * of where to write to. - * - * This will not block until the 3 FIFO buffers are filled with data or - * a timeout is detected. If so the library will auto retry until a new - * payload is written or the TX buffers are flushed. Interrupts can be - * used to control the timeout period. - * - * This will never return a 0. It will not return until a packet is - * loaded successfully into the FIFO and TX is complete. - * - * The maximum size of data written is the fixed payload size, see - * getPayloadSize(). However, you can write less, and the remainder - * will just be filled with zeroes. - * - * ONLY max retry interrupt flags will be cleared when writeBlocking is called - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - bool writeBlocking( const void* buf, uint8_t len ); + /** + * @note Optimization: New Command + * This function extends the auto-retry mechanism to any specified duration. + * It will not block until the 3 FIFO buffers are filled with data. + * If so the library will auto retry until a new payload is written + * or the user specified timeout period is reached. + * + * ONLY max retry interrupt flags will be cleared when writeBlocking is called + * @code + * Example (Full blocking): + * + * radio.writeBlocking(&buf,32,1000); //Writes 1 payload to the buffers with extended timeout of 1 second + * txStandBy(1000); //Returns 0 if failed after timeout period. 1 if success. + * //Blocks only until user timeout or success. Data flushed on fail. + * @endcode + * @see txStandBy() + * @see write() + * @see writeFast() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param timeout User defined timeout in milliseconds. + * @return True if the payload was delivered successfully false if not + */ + bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); /** * @note Optimization: New Command @@ -355,29 +349,42 @@ public: * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is * time enough between sends for the FIFOs to empty. * - * @note This does NOT need to be called when using per-payload noACK commands, - * or when using the regular write command since it is only capable of single - * payload transmission.. - * Per the datasheet, the radio will automatically engage STANDBY-I mode when - * using the W_TX_PAYLOAD_NOACK command. + * Relies on built-in auto retry functionality. * * @code - * Example: + * Example (Partial blocking): + * * radio.writeFast(&buf,32); * radio.writeFast(&buf,32); * radio.writeFast(&buf,32); //Fills the FIFO buffers up - * bool ok = txStandBy(0); //Returns 0 if failed. 1 if success. - * //Blocks only until timeout or success. Data flushed on fail. - * - * Using txStandBy(1) will not return until the data is transmitted. It will never return 0. - * + * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. + * //Blocks only until MAX_RT timeout or success. Data flushed on fail. * @endcode - * + * @see txStandBy(unsigned long timeout) * @return True if transmission is successful * */ - bool txStandBy(); - bool txStandBy(bool block); + bool txStandBy(); + + /** + * @note Optimization: New Command + * + * This function allows extended blocking and auto-retries per a user defined timeout + * @code + * Fully Blocking Example: + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. + * //Blocks only until user defined timeout or success. Data flushed on fail. + * @endcode + * + * @param timeout Number of milliseconds to retry failed payloads + * @return True if transmission is successful + * + */ + bool txStandBy(unsigned long timeout); /** * Non-blocking write to the open writing pipe used for buffered writes @@ -386,7 +393,9 @@ public: * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. * This allows the chip to be used to its full potential in TX mode. * + * @see write() * @see writeFast() + * @see startWrite() * @see writeBlocking() * * @param buf Pointer to the data to be sent @@ -401,9 +410,14 @@ public: * @note Optimization: The available functino now checks the FIFO * buffers directly for data instead of relying of interrupt flags. * - * @note: Interrupt flags will not be cleared until a payload is + * @note Interrupt flags will not be cleared until a payload is * actually read from the FIFO * + * @see txStandBy() + * @see startWrite() + * @see write() + * @see writeFast() + * * @return True if there is a payload available, false if none is */ bool available(void); @@ -422,7 +436,7 @@ public: * * @param buf Pointer to a buffer where the data should be written * @param len Maximum number of bytes to read into the buffer - * @return No return value. Use available. + * @return No return value. Use available(). */ void read( void* buf, uint8_t len ); @@ -526,7 +540,7 @@ public: * For dynamic payloads, this pulls the size of the payload off * the chip * - * Optimization: Corrupt packets are now detected and flushed per the + * @note Optimization: Corrupt packets are now detected and flushed per the * manufacturer. * * @return Payload length of last-received dynamic payload @@ -663,7 +677,7 @@ public: * To return to normal power mode, either write() some data or * startListening, or powerUp(). * - * Optimization: The radio will never enter power down unless instructed + * @note Optimization: The radio will never enter power down unless instructed * by the MCU via this command. */ void powerDown(void); @@ -681,10 +695,12 @@ public: * Just like write(), but it returns immediately. To find out what happened * to the send, catch the IRQ and then call whatHappened(). * - * @note Optimization: This function again behaves as it did previously. - * startFastWrite() has been moved to an internal function + * @note Optimization: This function again behaves as it did previously for backwards-compatibility. + * with user code. The library uses startFastWrite() internally. + * This is mainly used for single-payload transactions. * * @see write() + * @see writeFast() * @see startFastWrite() * @see whatHappened() * @@ -708,6 +724,8 @@ public: * * @note This is to be used AFTER auto-retry fails if wanting to resend * using the built-in payload reuse features. + * After issuing reUseTX(), it will keep reending the same payload forever or until + * a payload is written to the FIFO, or a flush_tx command is given. */ void reUseTX(); @@ -735,7 +753,7 @@ public: * * @note Optimization: Calling this function NO LONGER clears the interrupt * flag. The new functionality checks the RX FIFO buffer for an ACK payload - * instead of relying on interrupt flags. + * instead of relying on interrupt flags.Reading the payload will clear the flags. * * @return True if an ack payload is available. */ @@ -766,6 +784,13 @@ public: */ void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); + /** * Test whether there was a carrier on the line for the * previous listening period. @@ -797,6 +822,11 @@ public: */ bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + /** + * @see txStandBy() + */ + bool txStandBy(bool block); + /**@}*/ }; @@ -824,6 +854,18 @@ public: * with the receiver displaying the payload count. (32Byte Payloads) */ +/** + * @example TransferTimeouts.ino + * Updated: TMRh20 + * This example demonstrates the use of and extended timeout period and + * auto-retries/auto-reUse to increase reliability in noisy or low signal scenarios. + * + * Write this sketch to two different nodes. Put one of the nodes into 'transmit' + * mode by connecting with the serial monitor and sending a 'T'. The data + * transfer will begin, with the receiver displaying the payload count and the + * data transfer rate. + */ + /** * @example nordic_fob.pde * diff --git a/examples/GettingStarted/GettingStarted.ino b/examples/GettingStarted/GettingStarted.ino index d74db81..1dfd8ab 100644 --- a/examples/GettingStarted/GettingStarted.ino +++ b/examples/GettingStarted/GettingStarted.ino @@ -89,8 +89,8 @@ void loop(void) unsigned long time = millis(); // Take the time, and send it. This will block until complete printf("Now sending %lu...",time); - bool ok = radio.writeFast( &time, sizeof(unsigned long) );//New function for proper use of FIFO buffers - while( ! radio.txStandBy() ){} //Called when STANDBY-I mode is engaged (User is finished sending) + radio.writeFast( &time, sizeof(unsigned long) ); //New function for proper use of FIFO buffers + bool ok = radio.txStandBy(); //Called when STANDBY-I mode is engaged (User is finished sending) if (!ok) printf("failed.\n\r"); @@ -142,8 +142,8 @@ void loop(void) radio.stopListening(); // First, stop listening so we can talk - radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. - + radio.writeFast( &got_time, sizeof(unsigned long) ); // Send the final one back. + radio.txStandBy(); radio.startListening(); // Now, resume listening so we catch the next packets. //printf("Sent response.\n\r"); @@ -163,17 +163,16 @@ void loop(void) printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); role = role_ping_out; // Become the primary transmitter (ping out) - //radio.openWritingPipe(pipes[0]); - //radio.openReadingPipe(1,pipes[1]); + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); } else if ( c == 'R' && role == role_ping_out ) { printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); role = role_pong_back; // Become the primary receiver (pong back) - //radio.openWritingPipe(pipes[1]); - //radio.openReadingPipe(1,pipes[0]); + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); } } } -// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/examples/GettingStarted/GettingStarted.pde b/examples/GettingStarted/GettingStarted.pde deleted file mode 100644 index bf1851a..0000000 --- a/examples/GettingStarted/GettingStarted.pde +++ /dev/null @@ -1,227 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example for Getting Started with nRF24L01+ radios. - * - * This is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting - * with the serial monitor and sending a 'T'. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role = role_pong_back; - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/GettingStarted/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); - - // optionally, reduce the payload size. seems to - // improve reliability - //radio.setPayloadSize(8); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - //if ( role == role_ping_out ) - { - //radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - //else - { - //radio.openWritingPipe(pipes[1]); - //radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); - - if (ok) - printf("ok..."); - else - printf("failed.\n\r"); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - delay(20); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } - - // - // Change roles - // - - if ( Serial.available() ) - { - char c = toupper(Serial.read()); - if ( c == 'T' && role == role_pong_back ) - { - printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); - - // Become the primary transmitter (ping out) - role = role_ping_out; - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else if ( c == 'R' && role == role_ping_out ) - { - printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); - - // Become the primary receiver (pong back) - role = role_pong_back; - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/TransferTimeouts/TransferTimeouts.ino b/examples/TransferTimeouts/TransferTimeouts.ino new file mode 100644 index 0000000..65fce99 --- /dev/null +++ b/examples/TransferTimeouts/TransferTimeouts.ino @@ -0,0 +1,189 @@ +/* +TMRh20 2014 + + 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. + */ + +/** Reliably transmitting large volumes of data with a low signal or in noisy environments + * This example demonstrates data transfer functionality with the use of auto-retry + and auto-reUse functionality enabled. This sketch demonstrates how a user can extend + the auto-retry functionality to any chosen time period, preventing data loss and ensuring + the consistency of data. + + This sketh demonstrates use of the writeBlocking() functionality, and extends the standard + retry functionality of the radio. Payloads will be auto-retried until successful or the + extended timeout period is reached. + */ + + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +/************* USER Configuration *****************************/ + +RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 7 & 8 +unsigned long timeoutPeriod = 3000; // Set a user-defined timeout period. With auto-retransmit set to (15,15) retransmission will take up to 60ms and as little as 7.5ms with it set to (1,15). + // With a timeout period of 1000, the radio will retry each payload for up to 1 second before giving up on the transmission and starting over + +/***************************************************************/ + +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +byte data[32]; //Data buffer + +volatile unsigned long counter; +unsigned long rxTimer,startTime, stopTime, payloads = 0; +bool TX=1,RX=0,role=0, transferInProgress = 0; + + +void setup(void) { + + Serial.begin(57600); + printf_begin(); + + radio.begin(); // Setup and configure rf radio + radio.setChannel(1); // Set the channel + radio.setPALevel(RF24_PA_LOW); // Set PA LOW for this demonstration. We want the radio to be as lossy as possible for this example. + radio.setDataRate(RF24_1MBPS); // Raise the data rate to reduce transmission distance and increase lossiness + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(2,15); // Optionally, increase the delay between retries. Want the number of auto-retries as high as possible (15) + radio.setCRCLength(RF24_CRC_16); // Set CRC length to 16-bit to ensure quality of data + radio.openWritingPipe(pipes[0]); // Open the default reading and writing pipe + radio.openReadingPipe(1,pipes[1]); + + radio.startListening(); // Start listening + radio.printDetails(); // Dump the configuration of the rf unit for debugging + + printf("\n\rRF24/examples/Transfer Rates/\n\r"); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + randomSeed(analogRead(0)); //Seed for random number generation + for(int i=0; i<32; i++){ + data[i] = random(255); //Load the buffer with random data + } + radio.powerUp(); //Power up the radio + + +} + + + +void loop(void){ + + + if(role == TX){ + delay(2000); // Pause for a couple seconds between transfers + printf("Initiating Extended Timeout Data Transfer\n\r"); + + unsigned long cycles = 1000; // Change this to a higher or lower number. This is the number of payloads that will be sent. + + unsigned long transferCMD[] = {'H','S',cycles }; // Indicate to the other radio that we are starting, and provide the number of payloads that will be sent + radio.writeFast(&transferCMD,12); // Send the transfer command + if(radio.txStandBy(timeoutPeriod)){ // If transfer initiation was successful, do the following + + startTime = millis(); // For calculating transfer rate + boolean timedOut = 0; // Boolean for keeping track of failures + + for(int i=0; i timeoutPeriod){ // If no data available, check the timeout period + Serial.println("Transfer Failed"); // If per-payload timeout exceeeded, end the transfer + transferInProgress = 0; + }else + if(counter >= payloads){ // If the specified number of payloads is reached, transfer is completed + startTime = millis() - startTime; // Calculate the total time spent during transfer + float numBytes = counter*32; // Calculate the number of bytes transferred + Serial.print("Rate: "); // Print the transfer rate and number of payloads + Serial.print(numBytes/startTime); + Serial.println(" KB/s"); + printf("Payload Count: %d \n\r", counter); + transferInProgress = 0; // End the transfer as complete + } + } + + + } + + // + // Change roles + // + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == RX ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.stopListening(); + role = TX; // Become the primary transmitter (ping out) + } + else if ( c == 'R' && role == TX ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + radio.startListening(); + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + role = RX; // Become the primary receiver (pong back) + } + } +} + + diff --git a/examples/TransferTimeouts/printf.h b/examples/TransferTimeouts/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/examples/TransferTimeouts/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ From 2f7e4489bad411ee79f068b593c83c59c1f8c515 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 1 Apr 2014 23:47:19 -0600 Subject: [PATCH 215/326] Fix: Ext. timeout hang, examples, board support - Initial support for Due and ATTiny boards - Fix for 0 timeout period causing hang - Remove unnecessary delays from startWrite - Cleaned up/Updated example files - Optimization is nearing completion --- README.md | 53 +- RF24.cpp | 87 +- RF24.h | 1008 +++++++++-------- RF24_config.h | 160 ++- examples/GettingStarted/GettingStarted.ino | 167 ++- examples/GettingStarted/Jamfile | 210 ---- .../GettingStarted_CallResponse.ino | 124 ++ .../printf.h | 0 examples/Transfer/Transfer.ino | 50 +- examples/{ => Usage}/led_remote/Jamfile | 0 .../{ => Usage}/led_remote/led_remote.pde | 0 .../{nordic_fob => Usage/led_remote}/printf.h | 0 examples/{ => Usage}/nordic_fob/Jamfile | 0 .../{ => Usage}/nordic_fob/nordic_fob.pde | 0 .../{pingpair => Usage/nordic_fob}/printf.h | 0 examples/{ => Usage}/pingpair_maple/Jamfile | 0 examples/{ => Usage}/pingpair_maple/main.cpp | 0 .../pingpair_maple/pingpair_maple.pde | 0 examples/pingpair/Jamfile | 219 ---- examples/pingpair/pingpair.ino | 186 --- examples/pingpair_ack/pingpair_ack.ino | 123 ++ .../{pingpair_pl => pingpair_ack}/printf.h | 0 examples/pingpair_irq/Jamfile | 219 ---- examples/pingpair_irq/pingpair_irq.ino | 144 +++ examples/pingpair_irq/pingpair_irq.pde | 216 ---- examples/pingpair_pl/Jamfile | 206 ---- examples/pingpair_pl/pingpair_pl.pde | 180 --- examples/pingpair_sleepy/Jamfile | 206 ---- examples/pingpair_sleepy/pingpair_sleepy.ino | 226 ++++ examples/pingpair_sleepy/pingpair_sleepy.pde | 288 ----- keywords.txt | 3 + 31 files changed, 1437 insertions(+), 2638 deletions(-) delete mode 100644 examples/GettingStarted/Jamfile create mode 100644 examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino rename examples/{led_remote => GettingStarted_CallResponse}/printf.h (100%) rename examples/{ => Usage}/led_remote/Jamfile (100%) rename examples/{ => Usage}/led_remote/led_remote.pde (100%) rename examples/{nordic_fob => Usage/led_remote}/printf.h (100%) rename examples/{ => Usage}/nordic_fob/Jamfile (100%) rename examples/{ => Usage}/nordic_fob/nordic_fob.pde (100%) rename examples/{pingpair => Usage/nordic_fob}/printf.h (100%) rename examples/{ => Usage}/pingpair_maple/Jamfile (100%) rename examples/{ => Usage}/pingpair_maple/main.cpp (100%) rename examples/{ => Usage}/pingpair_maple/pingpair_maple.pde (100%) delete mode 100644 examples/pingpair/Jamfile delete mode 100644 examples/pingpair/pingpair.ino create mode 100644 examples/pingpair_ack/pingpair_ack.ino rename examples/{pingpair_pl => pingpair_ack}/printf.h (100%) delete mode 100644 examples/pingpair_irq/Jamfile create mode 100644 examples/pingpair_irq/pingpair_irq.ino delete mode 100644 examples/pingpair_irq/pingpair_irq.pde delete mode 100644 examples/pingpair_pl/Jamfile delete mode 100644 examples/pingpair_pl/pingpair_pl.pde delete mode 100644 examples/pingpair_sleepy/Jamfile create mode 100644 examples/pingpair_sleepy/pingpair_sleepy.ino delete mode 100644 examples/pingpair_sleepy/pingpair_sleepy.pde diff --git a/README.md b/README.md index 24f69e1..a71fb6b 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,29 @@ # Optimized High Speed Arduino driver for nRF24L01 2.4GHz Wireless Transceiver - -Note: March 2014, this fork was just published and is still undergoing fine tuning. - -Class documenation is now available at http://tmrh20.github.io/RF24/class_r_f24.html - -See http://TMRh20.blogspot.com for an overview of the new functionality. - -Design Goals: This library is designed to be... - -* More compliat with the manufacturer specified operation of the chip -* Utilize the capabilities of the radio to their full potential via Arduino -* More reliable and feature rich -* Easy for beginners to use -* Consumed with a public interface that's similiar to other Arduino standard libraries -* Built against the standard SPI library. - -Please refer to: - -* [Documentation Main Page](http://tmrh20.github.io/) -* [Class Reference]( http://tmrh20.github.io/RF24/class_r_f24.html) -* [Source Code](https://github.com/tmrh20/RF24) -* [Downloads](https://github.com/tmrh20/RF24/archives/master) -* [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) - -This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or -the SPI hardware will go into 'slave' mode. + +Design Goals: This library is designed to be... + +* More complianct with the manufacturer specified operation of the chip +* More reliable and feature rich +* Easy for beginners to use +* Consumed with a public interface that's similiar to other Arduino standard libraries +* Built against the standard SPI library. + +March 2014: Optimization begun +April 2014: Optimization nearing completion +* The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds +* Changes to read() functionality have increased reliability and response +* Extended timeout periods have been added to aid in noisy or otherwise unreliable environments +* Delays have been removed where possible to ensure maximum efficiency +* More! See the links below and class documentation for more info. + +Please refer to: + +* [Documentation Main Page](http://tmrh20.github.io/) +* [Class Reference]( http://tmrh20.github.io/RF24/classRF24.html) +* [Source Code](https://github.com/tmrh20/RF24) +* [Downloads](https://github.com/TMRh20/RF24/archive/master.zip) +* [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) + +This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or +the SPI hardware will go into 'slave' mode. diff --git a/RF24.cpp b/RF24.cpp index e0713ac..f534b7d 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -19,9 +19,15 @@ void RF24::csn(int mode) // divider of 4 is the minimum we want. // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz #ifdef ARDUINO + #if !defined( __AVR_ATtiny85__ ) && !defined( __AVR_ATtiny84__) SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV4); + #ifdef __arm__ // Shouldn't need to set the clock divider on DUE + SPI.setClockDivider(21); + #else + SPI.setClockDivider(SPI_CLOCK_DIV4); + #endif +#endif #endif digitalWrite(csn_pin,mode); } @@ -273,6 +279,8 @@ uint8_t RF24::getPayloadSize(void) /****************************************************************************/ +#if !defined (MINIMAL) + static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; static const char rf24_datarate_e_str_2[] PROGMEM = "250KBPS"; @@ -328,6 +336,7 @@ void RF24::printDetails(void) printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); } +#endif /****************************************************************************/ void RF24::begin(void) @@ -440,6 +449,7 @@ void RF24::powerDown(void) void RF24::powerUp(void) { write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); + delay(2); } /******************************************************************/ @@ -484,8 +494,9 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, unsigned long timeout ) if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag + if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 } - if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 + } //Start Writing @@ -548,32 +559,29 @@ void RF24::startFastWrite( const void* buf, uint8_t len ){ //TMRh20 //Added the original startWrite back in so users can still use interrupts, ack payloads, etc //Allows the library to pass all tests -void RF24::startWrite( const void* buf, uint8_t len ) -{ - // Transmitter power-up - write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) ) & ~_BV(PRIM_RX) ); - delayMicroseconds(150); +void RF24::startWrite( const void* buf, uint8_t len ){ // Send the payload - write_payload( buf, len ); - // Allons! + write_payload( buf, len ); ce(HIGH); - delayMicroseconds(15); ce(LOW); + + } bool RF24::txStandBy(){ + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ if( get_status() & _BV(MAX_RT)){ write_register(STATUS,_BV(MAX_RT) ); + ce(LOW); flush_tx(); //Non blocking, flush the data - ce(LOW); //Set STANDBY-I mode return 0; } } - ce(LOW); //Set STANDBY-I mode + ce(LOW); //Set STANDBY-I mode return 1; } @@ -584,12 +592,11 @@ bool RF24::txStandBy(unsigned long timeout){ while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ if( get_status() & _BV(MAX_RT)){ write_register(STATUS,_BV(MAX_RT) ); - if(timeout > 0){ ce(LOW); //Set re-transmit ce(HIGH); - if(millis() - start > timeout){ ce(LOW); flush_tx(); return 0; } - - } + if(millis() - start >= timeout){ + ce(LOW); flush_tx(); return 0; + } } } ce(LOW); //Set STANDBY-I mode @@ -1051,5 +1058,51 @@ void RF24::setRetries(uint8_t delay, uint8_t count) write_register(SETUP_RETR,(delay&0xf)< -64dBm, false if not + */ + bool testRPD(void) ; + + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + + /** + * + */ + void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); + + /**@}*/ + /**@}*/ /** * @name Optional Configurators @@ -495,6 +516,7 @@ public: * defaults. */ /**@{*/ + /** * Set the number and delay of retries upon failed submit * @@ -656,258 +678,211 @@ public: */ void disableCRC( void ) ; - /**@}*/ +private: + /** - * @name Advanced Operation + * @name Low-level internal interface. * - * Methods you can use to drive the chip in more advanced ways + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. */ /**@{*/ /** - * Print a giant block of debugging information to stdout + * Set chip select pin * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data + * and best of all, we make use of the radio's FIFO buffers. A lower speed + * means we're less likely to effectively leverage our FIFOs and pay a higher + * AVR runtime cost as toll. + * + * @param mode HIGH to take this unit off the SPI bus, LOW to put it on */ - void printDetails(void); + void csn(int mode); /** - * Enter low-power mode + * Set chip enable * - * To return to normal power mode, either write() some data or - * startListening, or powerUp(). - * - * @note Optimization: The radio will never enter power down unless instructed - * by the MCU via this command. + * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet + * for a much more detailed description of this pin. */ - void powerDown(void); + void ce(int level); /** - * Leave low-power mode - making radio more responsive + * Read a chunk of data in from a register * - * To return to low power mode, call powerDown(). + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register */ - void powerUp(void) ; + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); /** - * Non-blocking write to the open writing pipe + * Read single byte from a register * - * Just like write(), but it returns immediately. To find out what happened - * to the send, catch the IRQ and then call whatHappened(). + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); + + /** + * Write a chunk of data to a register * - * @note Optimization: This function again behaves as it did previously for backwards-compatibility. - * with user code. The library uses startFastWrite() internally. - * This is mainly used for single-payload transactions. + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + + /** + * Write a single byte to a register * - * @see write() - * @see writeFast() - * @see startFastWrite() - * @see whatHappened() + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); + + /** + * Write the transmit payload * - * @param buf Pointer to the data to be sent + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data * @param len Number of bytes to be sent - * + * @return Current value of status register */ - void startWrite( const void* buf, uint8_t len ); + uint8_t write_payload(const void* buf, uint8_t len); /** - * Optimization: New Command + * Read the receive payload * - * This function is mainly used internally to take advantage of the auto payload - * re-use functionality of the chip, but can be beneficial to users as well. + * The size of data read is the fixed payload size, see getPayloadSize() * - * The function will instruct the radio to re-use the data in the FIFO buffers, - * and instructs the radio to re-send once the timeout limit has been reached. - * Used by writeFast and writeBlocking to initiate retries when a TX failure - * occurs. Retries are automatically initiated except with the standard write(). - * This way, data is not flushed from the buffer until switching between modes. - * - * @note This is to be used AFTER auto-retry fails if wanting to resend - * using the built-in payload reuse features. - * After issuing reUseTX(), it will keep reending the same payload forever or until - * a payload is written to the FIFO, or a flush_tx command is given. + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register */ - void reUseTX(); + uint8_t read_payload(void* buf, uint8_t len); /** - * Write an ack payload for the specified pipe - * - * The next time a message is received on @p pipe, the data in @p buf will - * be sent back in the acknowledgement. - * - * @warning According to the data sheet, only three of these can be pending - * at any time as there are only 3 FIFO buffers. - * - * @param pipe Which pipe# (typically 1-5) will get this response. - * @param buf Pointer to data that is sent - * @param len Length of the data to send, up to 32 bytes max. Not affected - * by the static payload set by setPayloadSize(). - */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); - - /** - * Determine if an ack payload was received in the most recent call to - * write(). - * - * Call read() to retrieve the ack payload. - * - * @note Optimization: Calling this function NO LONGER clears the interrupt - * flag. The new functionality checks the RX FIFO buffer for an ACK payload - * instead of relying on interrupt flags.Reading the payload will clear the flags. - * - * @return True if an ack payload is available. - */ - bool isAckPayloadAvailable(void); - - /** - * Test whether there are bytes available to be read in the - * FIFO buffers. This optimized version does not rely on interrupt - * flags, but checks the actual FIFO buffers. - * - * @note Optimization: Interrupt flags are no longer cleared when available is called, - * but will be reset only when the data is read from the FIFO buffers. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - bool available(uint8_t* pipe_num); - - /** - * Call this when you get an interrupt to find out why - * - * Tells you what caused the interrupt, and clears the state of - * interrupts. - * - * @param[out] tx_ok The send was successful (TX_DS) - * @param[out] tx_fail The send failed, too many retries (MAX_RT) - * @param[out] rx_ready There is a message waiting to be read (RX_DS) - */ - void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); - - /** - * Empty the transmit buffer + * Empty the receive buffer * * @return Current value of status register */ - uint8_t flush_tx(void); + uint8_t flush_rx(void); /** - * Test whether there was a carrier on the line for the - * previous listening period. + * Retrieve the current status of the chip * - * Useful to check for interference on the current channel. - * - * @return true if was carrier, false if not + * @return Current value of status register */ - bool testCarrier(void); + uint8_t get_status(void); /** - * Test whether a signal (carrier or otherwise) greater than - * or equal to -64dBm is present on the channel. Valid only - * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * Decode and print the given status to stdout * - * Useful to check for interference on the current channel and - * channel hopping strategies. + * @param status Status value to print * - * @return true if signal => -64dBm, false if not + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h */ - bool testRPD(void) ; + void print_status(uint8_t status); /** - * Test whether this is a real radio, or a mock shim for - * debugging. Setting either pin to 0xff is the way to - * indicate that this is not a real radio. + * Decode and print the given 'observe_tx' value to stdout * - * @return true if this is a legitimate radio + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h */ - bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + void print_observe_tx(uint8_t value); /** - * @see txStandBy() + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print */ - bool txStandBy(bool block); + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); /**@}*/ + }; /** * @example GettingStarted.ino - * Updated: TMRh20 - * This is an example which corresponds to my "Getting Started" blog post: - * Getting Started with nRF24L01+ on Arduino. + * Updated: TMRh20 2014
* - * It is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting - * with the serial monitor and sending a 'T'. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. + * This is an example of how to use the RF24 class to communicate on a basic level. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting with the serial monitor and
+ * sending a 'T'. The ping node sends the current time to the pong node, which responds by sending the value + * back. The ping node can then see how long the whole cycle took.
+ * @note For a more efficient call-response scenario see the GettingStarted_CallResponse.ino example. + * @note When switching between sketches, the radio may need to be powered down to clear settings that are not "un-set" otherwise + */ + +/** + * @example GettingStarted_CallResponse.ino + * New: TMRh20 2014
+ * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to
+ * primary transmitter if wanting to initiate communication instead of respond to a commmunication. */ /** * @example Transfer.ino - * Updated: TMRh20 - * This example demonstrates half-rate transfer using the FIFO buffers + * New: TMRh20
+ * This example demonstrates half-rate transfer using the FIFO buffers
* * It is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting + * different nodes. Put one of the nodes into 'transmit' mode by connecting
* with the serial monitor and sending a 'T'. The data transfer will begin, - * with the receiver displaying the payload count. (32Byte Payloads) + * with the receiver displaying the payload count. (32Byte Payloads)
*/ /** * @example TransferTimeouts.ino - * Updated: TMRh20 + * New: TMRh20
* This example demonstrates the use of and extended timeout period and - * auto-retries/auto-reUse to increase reliability in noisy or low signal scenarios. + * auto-retries/auto-reUse to increase reliability in noisy or low signal scenarios.
* * Write this sketch to two different nodes. Put one of the nodes into 'transmit' - * mode by connecting with the serial monitor and sending a 'T'. The data + * mode by connecting with the serial monitor and sending a 'T'. The data
* transfer will begin, with the receiver displaying the payload count and the * data transfer rate. */ -/** - * @example nordic_fob.pde - * - * This is an example of how to use the RF24 class to receive signals from the - * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . - * Thanks to Kirk Mower for providing test hardware. - */ - -/** - * @example led_remote.pde - * - * This is an example of how to use the RF24 class to control a remote - * bank of LED's using buttons on a remote control. - * - * Every time the buttons change on the remote, the entire state of - * buttons is send to the led board, which displays the state. - */ - -/** - * @example pingpair.ino - * - * This is an example of how to use the RF24 class. Write this sketch to two - * different nodes, connect the role_pin to ground on one. The ping node sends - * the current time to the pong node, which responds by sending the value back. - * The ping node can then see how long the whole cycle took. - */ - -/** - * @example pingpair_maple.pde - * - * This is an example of how to use the RF24 class on the Maple. For a more - * detailed explanation, see my blog post: - * nRF24L01+ Running on Maple - * - * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. - * - * Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - /** * @example starping.pde * @@ -924,28 +899,70 @@ public: */ /** - * @example pingpair_pl.pde - * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within - * the Ack packet of each transmission. Note that the payload is set BEFORE - * the sender's message arrives. + * @example pingpair_ack.ino + * Update: TMRh20
+ * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to if wanting
+ * to initiate communication instead of respond to a commmunication. */ /** - * @example pingpair_irq.pde - * - * This is an example of how to user interrupts to interact with the radio. - * It builds on the pingpair_pl example, and uses ack payloads. + * @example pingpair_irq.ino + * Update: TMRh20
+ * This is an example of how to user interrupts to interact with the radio, and a demonstration + * of how to use them to sleep when receiving, and not miss any payloads.
+ * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter. + * Sleep functionality is built directly into my fork of the RF24Network library
*/ /** - * @example pingpair_sleepy.pde - * + * @example pingpair_sleepy.ino + * Update: TMRh20
* This is an example of how to use the RF24 class to create a battery- - * efficient system. It is just like the pingpair.pde example, but the + * efficient system. It is just like the GettingStarted_CallResponse example, but the
* ping node powers down the radio and sleeps the MCU after every - * ping/pong cycle. + * ping/pong cycle, and the receiver sleeps between payloads.
+ */ + +/** + * @example pingpair_dyn.pde + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + +/** + * @example led_remote.pde + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. */ /** @@ -973,14 +990,21 @@ public: * * @section News News * - * March 2014: Fork currently being optimized for high speed and more responsive data transfers + * March 2014: Optimization begun
+ * April 2014: Optimization nearing completion

+ * - The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds + * - Changes to read() functionality have increased reliability and response + * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments + * - Delays have been removed where possible to ensure maximum efficiency + * - More! See the links below and class documentation for more info. + * * If issues are discovered with the documentation, please report them here: here * @section Useful Useful References * * Please refer to: * * @li Documentation Main Page - * @li RF24 Class Documentation + * @li RF24 Class Documentation * @li Source Code * @li Downloads Page * @li Chip Datasheet @@ -995,6 +1019,8 @@ public: * * @li Project blog: * @li TMRh20.blogspot.com + * @li RF24 Wireless Audio Library (Coming Soon) + * @li Optimized RF24 Network Layer * @li ManiacBug on GitHub (Original Library Author) */ diff --git a/RF24_config.h b/RF24_config.h index fc7397f..5d62655 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -5,61 +5,129 @@ 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. + + Added Arduino Due support from https://github.com/mcrosson/ */ #ifndef __RF24_CONFIG_H__ #define __RF24_CONFIG_H__ -#if ARDUINO < 100 -#include -#else -#include -#endif + #if ARDUINO < 100 + #include + #else + #include + #endif -#include + #include -// Stuff that is normally provided by Arduino -#ifdef ARDUINO -#include -#else -#include -#include -#include -extern HardwareSPI SPI; -#define _BV(x) (1<<(x)) -#endif + //TMRh20: + //#define MINIMAL -#undef SERIAL_DEBUG -#ifdef SERIAL_DEBUG -#define IF_SERIAL_DEBUG(x) ({x;}) -#else -#define IF_SERIAL_DEBUG(x) -#endif + // Define _BV for non-Arduino platforms and for Arduino DUE + #if ! defined(ARDUINO) || (defined(ARDUINO) && defined(__arm__)) + #define _BV(x) (1<<(x)) + #endif -// Avoid spurious warnings -#if 1 -#if ! defined( NATIVE ) && defined( ARDUINO ) -#undef PROGMEM -#define PROGMEM __attribute__(( section(".progmem.data") )) -#undef PSTR -#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) -#endif -#endif + #undef SERIAL_DEBUG + #ifdef SERIAL_DEBUG + #define IF_SERIAL_DEBUG(x) ({x;}) + #else + #define IF_SERIAL_DEBUG(x) + #endif -// Progmem is Arduino-specific -#ifdef ARDUINO -#include -#define PRIPSTR "%S" -#else -typedef char const char; -typedef uint16_t prog_uint16_t; -#define PSTR(x) (x) -#define printf_P printf -#define strlen_P strlen -#define PROGMEM -#define pgm_read_word(p) (*(p)) -#define PRIPSTR "%s" -#endif + // Avoid spurious warnings + #if 1 + #if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) + #undef PROGMEM + #define PROGMEM __attribute__(( section(".progmem.data") )) + #undef PSTR + #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) + #endif + #endif + + // Progmem is Arduino-specific + #if defined(ARDUINO) && ! defined(__arm__) + #include + #define PRIPSTR "%S" + #else + + #if ! defined(ARDUINO) + typedef char const char; + else // Fill in pgm_read_byte that is used, but missing from DUE + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + #endif + typedef uint16_t prog_uint16_t; + #define PSTR(x) (x) + #define printf_P printf + #define strlen_P strlen + #define PROGMEM + #define pgm_read_word(p) (*(p)) + #define PRIPSTR "%s" + #endif + + + + // Stuff that is normally provided by Arduino + #if !defined ARDUINO + + #include + #include + #include + extern HardwareSPI SPI; + #else + + #if !defined( __AVR_ATtiny85__ ) || defined( __AVR_ATtiny84__) + #include + #else + #define ATTINY + + #define SPI_CLOCK_DIV4 0x00 + #define SPI_CLOCK_DIV16 0x01 + #define SPI_CLOCK_DIV64 0x02 + #define SPI_CLOCK_DIV128 0x03 + #define SPI_CLOCK_DIV2 0x04 + #define SPI_CLOCK_DIV8 0x05 + #define SPI_CLOCK_DIV32 0x06 + //#define SPI_CLOCK_DIV64 0x07 + + #define SPI_MODE0 0x00 + #define SPI_MODE1 0x04 + #define SPI_MODE2 0x08 + #define SPI_MODE3 0x0C + + #define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR + #define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR + #define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + + + + class SPIClass { + public: + inline static byte transfer(byte _data); + + static void begin(); // Default + static void end(); + + static void setDataMode(byte); + static void setClockDivider(byte); + static void setBitOrder(byte); + }; + + extern SPIClass SPI; + + byte SPIClass::transfer(byte _data){ + + USIDR = _data; + USISR = _BV(USIOIF); + + while((USISR & _BV(USIOIF)) == 0){ + USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); + } + return USIDR; + } + #endif //ATTINY + + #endif //Defined Arduino #endif // __RF24_CONFIG_H__ -// vim:ai:cin:sts=2 sw=2 ft=cpp + diff --git a/examples/GettingStarted/GettingStarted.ino b/examples/GettingStarted/GettingStarted.ino index 1dfd8ab..8b0353c 100644 --- a/examples/GettingStarted/GettingStarted.ino +++ b/examples/GettingStarted/GettingStarted.ino @@ -4,156 +4,115 @@ 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. +//2014 - TMRh20 - Updated along with Optimized RF24 Library fork */ /** * Example for Getting Started with nRF24L01+ radios. * - * This is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting - * with the serial monitor and sending a 'T'. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. + * This is an example of how to use the RF24 class to communicate on a basic level. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting with the serial monitor and + * sending a 'T'. The ping node sends the current time to the pong node, which responds by sending the value + * back. The ping node can then see how long the whole cycle took. + * Note: For a more efficient call-response scenario see the GettingStarted_CallResponse.ino example. + * Note: When switching between sketches, the radio may need to be powered down to clear settings that are not "un-set" otherwise */ -//March 2014 - TMRh20 - Updated to utilize High Speed RF24 Library fork -//This - #include #include "nRF24L01.h" #include "RF24.h" #include "printf.h" -// Hardware configuration -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); -RF24 radio(48,49); +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. -// Topology -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; +// Set up roles to simplify testing +boolean role; // The main role variable, holds the current role identifier +boolean role_ping_out = 1, role_pong_back = 0; // The two different roles. -// -// Role management -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. +void setup() { -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role = role_pong_back; - -void setup(void) -{ - // - // Print preamble - // Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/GettingStarted/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - // // Setup and configure rf radio - - radio.begin(); + radio.begin(); // Start up the radio radio.setAutoAck(1); // Ensure autoACK is enabled - - radio.setRetries(15,15); // Optionally, increase the delay between retries & # of retries - - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); + radio.setRetries(15,15); // Max delay between retries & number of retries + + radio.openWritingPipe(addresses[1]); // Open a writing pipe on pipe 0, with address 1 + radio.openReadingPipe(1,addresses[0]); // Open a reading pipe on pipe 1, with address 0 radio.startListening(); // Start listening - radio.printDetails(); // Dump the configuration of the rf unit for debugging } -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // +void loop(void){ + - if (role == role_ping_out) - { +/****************** Ping Out Role ***************************/ + if (role == role_ping_out) { - radio.stopListening(); // First, stop listening so we can talk. + radio.stopListening(); // First, stop listening so we can talk. - unsigned long time = millis(); // Take the time, and send it. This will block until complete - printf("Now sending %lu...",time); - radio.writeFast( &time, sizeof(unsigned long) ); //New function for proper use of FIFO buffers - bool ok = radio.txStandBy(); //Called when STANDBY-I mode is engaged (User is finished sending) - if (!ok) - printf("failed.\n\r"); - - - radio.startListening(); // Now, continue listening - - unsigned long started_waiting_at = millis(); // Wait here until we get a response, or timeout (250ms) - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; + unsigned long time; + printf("Now sending ",time); + + time = micros(); // Take the time, and send it. This will block until complete + boolean ok = radio.write( &time, sizeof(unsigned long) ); // Send the current time + if (!ok){ printf("failed.\n\r"); } - if ( timeout ) // Describe the results - { - printf("Failed, response timed out.\n\r"); + radio.startListening(); // Now, continue listening + + unsigned long started_waiting_at = micros(); // Set up a timeout period, get the current microseconds + boolean timeout = false; // Set up a variable to indicate if a response was received or not + + while ( ! radio.available() ){ // While nothing is received + if (micros() - started_waiting_at > 200000 ){ // If waited longer than 200ms, indicate timeout and exit while loop + timeout = true; + break; + } } - else - { - unsigned long got_time; // Grab the response, compare, and send to debugging spew - radio.read( &got_time, sizeof(unsigned long) ); - unsigned long tim = millis(); - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,tim-got_time); + + if ( timeout ){ // Describe the results + printf("Failed, response timed out.\n\r"); + }else{ + unsigned long got_time; // Grab the response, compare, and send to debugging spew + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Sent %lu, Got response %lu, round-trip delay: %lu microseconds\n\r",time,got_time,micros()-got_time); } // Try again 1s later delay(1000); } - // - // Pong back role. Receive each packet, dump it out, and send it back - // +/****************** Pong Back Role ***************************/ if ( role == role_pong_back ) { if( radio.available()){ - unsigned long got_time; // Dump the payloads until we've gotten everything - bool done = false; + unsigned long got_time; // Variable for the received timestamp while (radio.available()) { // While there is data ready - // Fetch the payload, and see if this was the last one. - radio.read( &got_time, sizeof(unsigned long) ); + radio.read( &got_time, sizeof(unsigned long) ); // Get the payload } - // Spew it - //printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - //delay(2); - radio.stopListening(); // First, stop listening so we can talk - - radio.writeFast( &got_time, sizeof(unsigned long) ); // Send the final one back. - radio.txStandBy(); - radio.startListening(); // Now, resume listening so we catch the next packets. - - //printf("Sent response.\n\r"); - + radio.stopListening(); // First, stop listening so we can talk + radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. + radio.startListening(); // Now, resume listening so we catch the next packets. + printf("Sent response %lu \n\r", got_time); } } - // - // Change roles - // + +/****************** Change Roles via Serial Commands ***************************/ if ( Serial.available() ) { @@ -163,16 +122,16 @@ void loop(void) printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); role = role_ping_out; // Become the primary transmitter (ping out) - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); } else if ( c == 'R' && role == role_ping_out ) { printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); role = role_pong_back; // Become the primary receiver (pong back) - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); } } -} +} \ No newline at end of file diff --git a/examples/GettingStarted/Jamfile b/examples/GettingStarted/Jamfile deleted file mode 100644 index 9a5f2c4..0000000 --- a/examples/GettingStarted/Jamfile +++ /dev/null @@ -1,210 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = SPI RF24 ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 57600 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN = /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN ?= /usr/bin ; - AVR_INCLUDE ?= /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE = $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -# -# Targets -# - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Grab everything from the current dir -PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; - -# Main output executable -MAIN = $(PWD:B).elf ; - -Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -Hex $(MAIN:B).hex : $(MAIN) ; - -# Upload targets -for _p in $(PORTS) -{ - Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; -} diff --git a/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino b/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino new file mode 100644 index 0000000..d491a83 --- /dev/null +++ b/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino @@ -0,0 +1,124 @@ +/* + // March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork + // Parts derived from examples by J. Coliz + +/** + * Example for efficient call-response using ack-payloads + * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. + * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to + * primary transmitter if wanting to initiate communication instead of respond to a commmunication. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); + // Topology +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management: Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. +typedef enum { role_ping_out = 1, role_pong_back } role_e; // The various roles supported by this sketch +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The debug-friendly names of those roles +role_e role = role_pong_back; // The role of the current running sketch + +byte counter = 1; // A single byte to keep track of the data being sent back and forth + + +void setup(){ + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // Setup and configure radio + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.enableAckPayload(); // Allow optional ack payloads + radio.setRetries(0,15); // Smallest time between retries, max no. of retries + radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed + radio.openWritingPipe(addresses[1]); // Both radios listen on the same pipes by default, and switch when writing + radio.openReadingPipe(1,addresses[0]); // Open a reading pipe on address 0, pipe 1 + radio.startListening(); // Start listening + radio.powerUp(); + radio.printDetails(); // Dump the configuration of the rf unit for debugging +} + +void loop(void) { + + +/****************** Ping Out Role ***************************/ + + if (role == role_ping_out){ // Radio is in ping mode + + byte gotByte; // Initialize a variable for the incoming response + unsigned long time = micros(); // Record the current microsecond count + + radio.stopListening(); // First, stop listening so we can talk. + printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload + + if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio + if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank + printf("Got blank response. round-trip delay: %lu microseconds\n\r",micros()-time); + }else{ + while(radio.available() ){ // If an ack with payload was received + radio.read( &gotByte, 1 ); // Read it, and display the response time + printf("Got response %d, round-trip delay: %lu microseconds\n\r",gotByte,micros()-time); + counter++; // Increment the counter variable + } + } + + }else{ printf("Sending failed.\n\r"); } // If no ack response, sending failed + + delay(1000); // Try again later + } + + +/****************** Pong Back Role ***************************/ + + if ( role == role_pong_back ) { + byte pipeNo, gotByte; // Declare variables for the pipe and the byte received + while( radio.available(&pipeNo)){ // Read all available payloads + radio.read( &gotByte, 1 ); + // Since this is a call-response. Respond directly with an ack payload. + // Ack payloads are much more efficient than switching to transmit mode to respond to a call + radio.writeAckPayload(pipeNo,&gotByte, 1 ); // This can be commented out to send empty payloads. + printf("Sent response %d \n\r", gotByte); + } + } + + + +/****************** Change Roles via Serial Commands ***************************/ + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + role = role_ping_out; // Change roles (ping out) + radio.openWritingPipe(addresses[0]); // Open different pipes when writing. Write on pipe 0, address 0 + radio.openReadingPipe(1,addresses[1]); // Read on pipe 1, as address 1 + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + role = role_pong_back; // Become the primary receiver (pong back) + radio.openWritingPipe(addresses[1]); // Since only two radios involved, both listen on the same addresses and pipe numbers in RX mode + radio.openReadingPipe(1,addresses[0]); // then switch pipes & addresses to transmit. + radio.startListening(); // Need to start listening after opening new reading pipes + } + } +} diff --git a/examples/led_remote/printf.h b/examples/GettingStarted_CallResponse/printf.h similarity index 100% rename from examples/led_remote/printf.h rename to examples/GettingStarted_CallResponse/printf.h diff --git a/examples/Transfer/Transfer.ino b/examples/Transfer/Transfer.ino index 080c208..b9b1362 100644 --- a/examples/Transfer/Transfer.ino +++ b/examples/Transfer/Transfer.ino @@ -21,7 +21,7 @@ TMRh20 2014 /************* USER Configuration *****************************/ // Hardware configuration -RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 7 & 8 boolean RADIO_NO = 0; //SET THIS TO 0 or 1 for 1st or 2nd radio /***************************************************************/ @@ -31,7 +31,7 @@ const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe ad byte data[32]; //Data buffer for testing data transfer speeds unsigned long counter, rxTimer; //Counter and timer for keeping track transfer info -float startTime, stopTime; +unsigned long startTime, stopTime; bool TX=1,RX=0,role=0; void setup(void) { @@ -42,17 +42,13 @@ void setup(void) { radio.begin(); // Setup and configure rf radio radio.setChannel(1); radio.setPALevel(RF24_PA_MAX); + radio.setDataRate(RF24_1MBPS); radio.setAutoAck(1); // Ensure autoACK is enabled - radio.setRetries(1,1); // Optionally, increase the delay between retries & # of retries + radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries radio.setCRCLength(RF24_CRC_8); - - if(RADIO_NO){ //Lets the two radios choose the correct pipes depending on user selection - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - }else{ - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } + //radio.disableCRC(); + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); radio.startListening(); // Start listening radio.printDetails(); // Dump the configuration of the rf unit for debugging @@ -73,28 +69,29 @@ void loop(void){ if(role == TX){ - delay(1500); + delay(2000); printf("Initiating Basic Data Transfer\n\r"); - float cycles = 1000; //Change this to a higher or lower number. + unsigned long cycles = 10000; //Change this to a higher or lower number. startTime = millis(); for(int i=0; i 1000){ rxTimer = millis(); -// printf("Rx Rate: "); -// Serial.print(counter*32/1000); -// printf(" KB/s\n\r"); + printf("Rate: "); + float numBytes = counter*32; + Serial.print(numBytes/1000); + printf(" KB/s\n\r"); printf("Payload Count: %d \n\r", counter); counter = 0; } @@ -128,15 +126,19 @@ void loop(void){ if ( c == 'T' && role == RX ) { printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); radio.stopListening(); role = TX; // Become the primary transmitter (ping out) } else if ( c == 'R' && role == TX ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); radio.startListening(); printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); role = RX; // Become the primary receiver (pong back) } } } -// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/examples/led_remote/Jamfile b/examples/Usage/led_remote/Jamfile similarity index 100% rename from examples/led_remote/Jamfile rename to examples/Usage/led_remote/Jamfile diff --git a/examples/led_remote/led_remote.pde b/examples/Usage/led_remote/led_remote.pde similarity index 100% rename from examples/led_remote/led_remote.pde rename to examples/Usage/led_remote/led_remote.pde diff --git a/examples/nordic_fob/printf.h b/examples/Usage/led_remote/printf.h similarity index 100% rename from examples/nordic_fob/printf.h rename to examples/Usage/led_remote/printf.h diff --git a/examples/nordic_fob/Jamfile b/examples/Usage/nordic_fob/Jamfile similarity index 100% rename from examples/nordic_fob/Jamfile rename to examples/Usage/nordic_fob/Jamfile diff --git a/examples/nordic_fob/nordic_fob.pde b/examples/Usage/nordic_fob/nordic_fob.pde similarity index 100% rename from examples/nordic_fob/nordic_fob.pde rename to examples/Usage/nordic_fob/nordic_fob.pde diff --git a/examples/pingpair/printf.h b/examples/Usage/nordic_fob/printf.h similarity index 100% rename from examples/pingpair/printf.h rename to examples/Usage/nordic_fob/printf.h diff --git a/examples/pingpair_maple/Jamfile b/examples/Usage/pingpair_maple/Jamfile similarity index 100% rename from examples/pingpair_maple/Jamfile rename to examples/Usage/pingpair_maple/Jamfile diff --git a/examples/pingpair_maple/main.cpp b/examples/Usage/pingpair_maple/main.cpp similarity index 100% rename from examples/pingpair_maple/main.cpp rename to examples/Usage/pingpair_maple/main.cpp diff --git a/examples/pingpair_maple/pingpair_maple.pde b/examples/Usage/pingpair_maple/pingpair_maple.pde similarity index 100% rename from examples/pingpair_maple/pingpair_maple.pde rename to examples/Usage/pingpair_maple/pingpair_maple.pde diff --git a/examples/pingpair/Jamfile b/examples/pingpair/Jamfile deleted file mode 100644 index 18244ec..0000000 --- a/examples/pingpair/Jamfile +++ /dev/null @@ -1,219 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = SPI RF24 ; -PROJECT_DIRS = $(PWD) ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 115200 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN ?= /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN ?= /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -AR = $(AVR_BIN)/avr-ar rcs ; -RANLIB = ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE ?= $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Library -{ - LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -rule Arduino -{ - LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; - Main $(<) : $(>) ; - LinkLibraries $(<) : core libs ; - Hex $(<:B).hex : $(<) ; - for _p in $(PORTS) - { - Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; - } -} - -# -# Targets -# - -# Grab everything from the core directory -Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Main output executable -Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/examples/pingpair/pingpair.ino b/examples/pingpair/pingpair.ino deleted file mode 100644 index 80baba5..0000000 --- a/examples/pingpair/pingpair.ino +++ /dev/null @@ -1,186 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example RF Radio Ping Pair - * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -//March 2014 TMRh20 - Updated to utilize High Speed RF24 Library fork - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(9,10); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 5; - -// Topology -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -void setup(void) -{ - // - // Role - // - pinMode(role_pin, INPUT); // set up the role pin - digitalWrite(role_pin,HIGH); //Change this to HIGH/LOW to pre-choose a role - delay(20); // Just to get a solid reading on the role pin - - if ( ! digitalRead(role_pin) ) // read the address pin, establish our role - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - radio.setRetries(15,15); // optionally, increase the delay between retries & # of retries - - radio.setPayloadSize(8); // optionally, reduce the payload size. seems to improve reliability - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - radio.startListening(); // Start listening - - radio.printDetails(); // Dump the configuration of the rf unit for debugging -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - radio.stopListening(); // First, stop listening so we can talk. - - unsigned long time = millis(); // Take the time, and send it. This will block until complete - printf("Now sending %lu...",time); - bool ok = radio.writeFast( &time, sizeof(unsigned long) ); // NEW command to buffer data more efficiently - - if (!ok) - printf("failed.\n\r"); - - while(!radio.txStandBy()){} //NEW command needs to be called after done sending. Allows FIFO buffers to be filled - - radio.startListening(); // Now, continue listening - - unsigned long started_waiting_at = millis(); // Wait here until we get a response, or timeout (250ms) - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; - - if ( timeout ) // Describe the results - { - printf("Failed, response timed out.\n\r"); - } - else - { - unsigned long got_time; // Grab the response, compare, and send to debugging spew - radio.read( &got_time, sizeof(unsigned long) ); - - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); // Spew it - } - - delay(1000); // Try again 1s later - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - if ( radio.available() ) // if there is data ready - { - unsigned long got_time; // Dump the payloads until we've gotten everything - bool done = false; - while (radio.available()) - { - radio.read( &got_time, sizeof(unsigned long) ); // Fetch the payload, and see if this was the last one. - } - printf("Got payload %lu...",got_time); // Spew it - - // Delay just a little bit to let the other unit - // make the transition to receiver - //delay(20); - - radio.stopListening(); // First, stop listening so we can talk - - radio.write( &got_time, sizeof(unsigned long) );// Send the final one back. - printf("Sent response.\n\r"); - - radio.startListening(); // Now, resume listening so we catch the next packets. - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/examples/pingpair_ack/pingpair_ack.ino b/examples/pingpair_ack/pingpair_ack.ino new file mode 100644 index 0000000..2d3ecf7 --- /dev/null +++ b/examples/pingpair_ack/pingpair_ack.ino @@ -0,0 +1,123 @@ +/* + // March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork + // Parts derived from examples by J. Coliz + +/** + * Example for efficient call-response using ack-payloads + * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. + * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to if wanting + * to initiate communication instead of respond to a commmunication. + */ + + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); + +// Topology +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management: Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. + +typedef enum { role_ping_out = 1, role_pong_back } role_e; // The various roles supported by this sketch +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The debug-friendly names of those roles +role_e role = role_pong_back; // The role of the current running sketch + +// A single byte to keep track of the data being sent back and forth +byte counter = 1; + +void setup(){ + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // Setup and configure rf radio + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.enableAckPayload(); // Allow optional ack payloads + radio.setRetries(0,15); // Smallest time between retries, max no. of retries + radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed + radio.openWritingPipe(pipes[1]); // Both radios listen on the same pipes by default, and switch when writing + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); // Start listening + radio.printDetails(); // Dump the configuration of the rf unit for debugging +} + +void loop(void) { + + if (role == role_ping_out){ + + radio.stopListening(); // First, stop listening so we can talk. + + printf("Now sending %d as payload. ",counter); + byte gotByte; + unsigned long time = micros(); // Take the time, and send it. This will block until complete + //Called when STANDBY-I mode is engaged (User is finished sending) + if (!radio.write( &counter, 1 )){ + printf("failed.\n\r"); + }else{ + + if(!radio.available()){ + printf("Blank Payload Received\n\r"); + }else{ + while(radio.available() ){ + unsigned long tim = micros(); + radio.read( &gotByte, 1 ); + printf("Got response %d, round-trip delay: %lu microseconds\n\r",gotByte,tim-time); + counter++; + } + } + + } + // Try again later + delay(1000); + } + + // Pong back role. Receive each packet, dump it out, and send it back + + if ( role == role_pong_back ) { + byte pipeNo; + byte gotByte; // Dump the payloads until we've gotten everything + while( radio.available(&pipeNo)){ + radio.read( &gotByte, 1 ); + radio.writeAckPayload(pipeNo,&gotByte, 1 ); + } + } + + // Change roles + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + role = role_ping_out; // Become the primary transmitter (ping out) + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + role = role_pong_back; // Become the primary receiver (pong back) + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + } + } +} diff --git a/examples/pingpair_pl/printf.h b/examples/pingpair_ack/printf.h similarity index 100% rename from examples/pingpair_pl/printf.h rename to examples/pingpair_ack/printf.h diff --git a/examples/pingpair_irq/Jamfile b/examples/pingpair_irq/Jamfile deleted file mode 100644 index 97237bc..0000000 --- a/examples/pingpair_irq/Jamfile +++ /dev/null @@ -1,219 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = SPI RF24 ; -PROJECT_DIRS = $(PWD) ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 115200 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN ?= /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN ?= /usr/bin ; - AVR_INCLUDE ?= /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -AR = $(AVR_BIN)/avr-ar rcs ; -RANLIB = ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE ?= $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Library -{ - LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -rule Arduino -{ - LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; - Main $(<) : $(>) ; - LinkLibraries $(<) : core libs ; - Hex $(<:B).hex : $(<) ; - for _p in $(PORTS) - { - Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; - } -} - -# -# Targets -# - -# Grab everything from the core directory -Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Main output executable -Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/examples/pingpair_irq/pingpair_irq.ino b/examples/pingpair_irq/pingpair_irq.ino new file mode 100644 index 0000000..de00b62 --- /dev/null +++ b/examples/pingpair_irq/pingpair_irq.ino @@ -0,0 +1,144 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + Update 2014 - TMRh20 + */ + +/** + * Example of using interrupts + * + * This is an example of how to user interrupts to interact with the radio, and a demonstration + * of how to use them to sleep when receiving, and not miss any payloads. + * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter. + * Sleep functionality is built directly into my fork of the RF24Network library + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" +#include +#include + +// Hardware configuration +RF24 radio(7,8); // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +const short role_pin = 5; // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver + // Leave open to be the 'ping' transmitter + + const uint64_t address[2] = {0xABCDABCD71LL, 0x544d52687CLL}; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management + +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// This is done through the role_pin +typedef enum { role_sender = 1, role_receiver } role_e; // The various roles supported by this sketch +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; // The debug-friendly names of those roles +role_e role; // The role of the current running sketch + +boolean gotMsg = 0; // So we know when to go to sleep + +/********************** Setup *********************/ +void setup(){ + + pinMode(role_pin, INPUT); // set up the role pin + digitalWrite(role_pin,HIGH); // Change this to LOW/HIGH instead of using an external pin + delay(20); // Just to get a solid reading on the role pin + + if ( digitalRead(role_pin) ) // read the address pin, establish our role + role = role_sender; + else + role = role_receiver; + + + Serial.begin(115200); + printf_begin(); + printf("\n\rRF24/examples/pingpair_irq/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // Setup and configure rf radio + radio.begin(); + radio.enableAckPayload(); // We will be using the Ack Payload feature, so please enable it + // Open pipes to other node for communication + if ( role == role_sender ) { // This simple sketch opens a pipe on a single address for these two nodes to + radio.openWritingPipe(address[0]); // communicate back and forth. One listens on it, the other talks to it. + }else{ + radio.openReadingPipe(1,address[0]); + radio.startListening(); + } + radio.printDetails(); // Dump the configuration of the rf unit for debugging + radio.powerUp(); // Power up the radio + attachInterrupt(0, check_radio, FALLING); // Attach interrupt handler to interrupt #0 (using pin 2) on BOTH the sender and receiver +} + +static uint32_t message_count = 0; + + +/********************** Main Loop *********************/ +void loop() { + + + if (role == role_sender) { // Sender role. Repeatedly send the current time + unsigned long time = millis(); // Take the time, and send it. + printf("Now sending %lu\n\r",time); + radio.startWrite( &time, sizeof(unsigned long) ); + delay(2000); // Try again soon + } + + + if(role == role_receiver){ // Receiver does nothing but sleep between payloads + if(!radio.available() && gotMsg == 1 ){ // Display the uptime in millis(). (Counter does not increment during sleep) + gotMsg = 0; + set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Put the Arduino to sleep between payloads + sleep_enable(); + sleep_mode(); // Arduino goes to sleep here + //ZZZ... sleeping + sleep_disable(); // This happens when the interrupt wakes Arduino up + } + } +} + + +/********************** Interrupt *********************/ + +void check_radio(void) // Receiver role: Does nothing! All the work is in IRQ +{ + + bool tx,fail,rx; + radio.whatHappened(tx,fail,rx); // What happened? + + if ( tx ) { // Have we successfully transmitted? + if ( role == role_sender ){ printf("Send:OK\n\r"); } + if ( role == role_receiver ){ printf("Ack Payload:Sent\n\r"); } + } + + if ( fail ) { // Have we failed to transmit? + if ( role == role_sender ){ printf("Send:Failed\n\r"); } + if ( role == role_receiver ){ printf("Ack Payload:Failed\n\r"); } + } + + if ( rx || radio.available()){ // Did we receive a message? + + if ( role == role_sender ) { // If we're the sender, we've received an ack payload + radio.read(&message_count,sizeof(message_count)); + printf("Ack:%lu\n\r",message_count); + } + + + if ( role == role_receiver ) { // If we're the receiver, we've received a time message + static unsigned long got_time; // Get this payload and dump it + radio.read( &got_time, sizeof(got_time) ); + printf("Got payload %lu\n\r",got_time); + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); // Add an ack packet for the next time around. This is a simple + ++message_count; // packet counter + + } + } +} + diff --git a/examples/pingpair_irq/pingpair_irq.pde b/examples/pingpair_irq/pingpair_irq.pde deleted file mode 100644 index 47084a1..0000000 --- a/examples/pingpair_irq/pingpair_irq.pde +++ /dev/null @@ -1,216 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example of using interrupts - * - * This is an example of how to user interrupts to interact with the radio. - * It builds on the pingpair_pl example, and uses ack payloads. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 7; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -// Interrupt handler, check the radio because we got an IRQ -void check_radio(void); - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_irq/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipe for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Attach interrupt handler to interrupt #0 (using pin 2) - // on BOTH the sender and receiver - // - - attachInterrupt(0, check_radio, FALLING); -} - -static uint32_t message_count = 0; - -void loop(void) -{ - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender) - { - // Take the time, and send it. - unsigned long time = millis(); - printf("Now sending %lu\n\r",time); - radio.startWrite( &time, sizeof(unsigned long) ); - - // Try again soon - delay(2000); - } - - // - // Receiver role: Does nothing! All the work is in IRQ - // - -} - -void check_radio(void) -{ - // What happened? - bool tx,fail,rx; - radio.whatHappened(tx,fail,rx); - - // Have we successfully transmitted? - if ( tx ) - { - if ( role == role_sender ) - printf("Send:OK\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Sent\n\r"); - } - - // Have we failed to transmit? - if ( fail ) - { - if ( role == role_sender ) - printf("Send:Failed\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Failed\n\r"); - } - - // Transmitter can power down for now, because - // the transmission is done. - if ( ( tx || fail ) && ( role == role_sender ) ) - radio.powerDown(); - - // Did we receive a message? - if ( rx ) - { - // If we're the sender, we've received an ack payload - if ( role == role_sender ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu\n\r",message_count); - } - - // If we're the receiver, we've received a time message - if ( role == role_receiver ) - { - // Get this payload and dump it - static unsigned long got_time; - radio.read( &got_time, sizeof(got_time) ); - printf("Got payload %lu\n\r",got_time); - - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); - ++message_count; - } - } -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_pl/Jamfile b/examples/pingpair_pl/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/examples/pingpair_pl/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde deleted file mode 100644 index 70aed6e..0000000 --- a/examples/pingpair_pl/pingpair_pl.pde +++ /dev/null @@ -1,180 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example of using Ack Payloads - * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within - * the Ack packet of each transmission. Note that the payload is set BEFORE - * the sender's message arrives. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 7; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_pl/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipes for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - static uint32_t message_count = 0; - - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender) - { - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - if ( radio.isAckPayloadAvailable() ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack: [%lu] ",message_count); - } - printf("OK\n\r"); - - // Try again soon - delay(2000); - } - - // - // Receiver role. Receive each packet, dump it out, add ack payload for next time - // - - if ( role == role_receiver ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - static unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu\n",got_time); - } - - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); - ++message_count; - } - } -} -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_sleepy/Jamfile b/examples/pingpair_sleepy/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/examples/pingpair_sleepy/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/examples/pingpair_sleepy/pingpair_sleepy.ino b/examples/pingpair_sleepy/pingpair_sleepy.ino new file mode 100644 index 0000000..ebc024e --- /dev/null +++ b/examples/pingpair_sleepy/pingpair_sleepy.ino @@ -0,0 +1,226 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + TMRh20 2014 - Updates to the library allow sleeping both in TX and RX modes: + TX Mode: The radio can be powered down (.9uA current) and the Arduino slept using the watchdog timer + RX Mode: The radio can be left in standby mode (22uA current) and the Arduino slept using an interrupt pin + */ + +/** + * Example RF Radio Ping Pair which Sleeps between Sends + * + * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the GettingStarted_CallResponse example, but the + * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle, and the receiver sleeps between payloads. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +#include +#include +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 5; + +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + + +// Sleep declarations +typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e; + +void setup_watchdog(uint8_t prescalar); +void do_sleep(void); + +const short sleep_cycles_per_transmission = 4; +volatile short sleep_cycles_remaining = sleep_cycles_per_transmission; + + + +void setup(){ + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,LOW); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // Prepare sleep parameters + // Only the ping out role uses WDT. Wake up every 4s to send a ping + //if ( role == role_ping_out ) + setup_watchdog(wdt_4s); + + // Setup and configure rf radio + + radio.begin(); + + // Open pipes to other nodes for communication + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // Start listening + radio.startListening(); + + // Dump the configuration of the rf unit for debugging + radio.printDetails(); +} + +void loop(){ + + + if (role == role_ping_out) { // Ping out role. Repeatedly send the current time + radio.powerUp(); // Power up the radio after sleeping + radio.stopListening(); // First, stop listening so we can talk. + + printf("Now sending... "); + unsigned long time = millis(); // Take the time, and send it. + radio.write( &time, sizeof(unsigned long) ); + + radio.startListening(); // Now, continue listening + + unsigned long started_waiting_at = millis(); // Wait here until we get a response, or timeout (250ms) + bool timeout = false; + while ( ! radio.available() ){ + if (millis() - started_waiting_at > 250 ){ // Break out of the while loop if nothing available + timeout = true; + break; + } + } + + if ( timeout ) { // Describe the results + printf("Failed, response timed out.\n\r"); + }else{ + unsigned long got_time; // Grab the response, compare, and send to debugging spew + radio.read( &got_time, sizeof(unsigned long) ); + + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Shut down the system + delay(500); // Experiment with some delay here to see if it has an effect + // Power down the radio. + radio.powerDown(); // NOTE: The radio MUST be powered back up again manually + + // Sleep the MCU. + do_sleep(); + + + } + + + // Pong back role. Receive each packet, dump it out, and send it back + if ( role == role_pong_back ) { + + if ( radio.available() ) { // if there is data ready + + unsigned long got_time; + while (radio.available()) { // Dump the payloads until we've gotten everything + radio.read( &got_time, sizeof(unsigned long) ); // Get the payload, and see if this was the last one. + // Spew it. Include our time, because the ping_out millis counter is unreliable + printf("Got payload %lu @ %lu...",got_time,millis()); // due to it sleeping + } + + radio.stopListening(); // First, stop listening so we can talk + radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. + printf("Sent response.\n\r"); + radio.startListening(); // Now, resume listening so we catch the next packets. + }else{ + Serial.println("Sleeping"); + delay(200); // Delay so the serial data can print out + do_sleep(); + + } + } +} + +void wakeUp(){ + sleep_disable(); +} + +// Sleep helpers + +//Prescaler values +// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms +// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec + +void setup_watchdog(uint8_t prescalar){ + + uint8_t wdtcsr = prescalar & 7; + if ( prescalar & 8 ) + wdtcsr |= _BV(WDP3); + Serial.println(wdtcsr); + MCUSR &= ~_BV(WDRF); // Clear the WD System Reset Flag + WDTCSR = _BV(WDCE) | _BV(WDE); // Write the WD Change enable bit to enable changing the prescaler and enable system reset + WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); // Write the prescalar bits (how long to sleep, enable the interrupt to wake the MCU +} + +ISR(WDT_vect) +{ + //--sleep_cycles_remaining; + Serial.println("WDT"); +} + +void do_sleep(void) +{ + set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here + sleep_enable(); + attachInterrupt(0,wakeUp,LOW); + WDTCSR |= _BV(WDIE); + sleep_mode(); // System sleeps here + // The WDT_vect interrupt wakes the MCU from here + sleep_disable(); // System continues execution here when watchdog timed out + detachInterrupt(0); + WDTCSR &= ~_BV(WDIE); +} + diff --git a/examples/pingpair_sleepy/pingpair_sleepy.pde b/examples/pingpair_sleepy/pingpair_sleepy.pde deleted file mode 100644 index 49daa69..0000000 --- a/examples/pingpair_sleepy/pingpair_sleepy.pde +++ /dev/null @@ -1,288 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example RF Radio Ping Pair which Sleeps between Sends - * - * This is an example of how to use the RF24 class to create a battery- - * efficient system. It is just like the pingpair.pde example, but the - * ping node powers down the radio and sleeps the MCU after every - * ping/pong cycle. - * - * As with the pingpair.pde example, write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. - */ - -#include -#include -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Sleep declarations -// - -typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e; - -void setup_watchdog(uint8_t prescalar); -void do_sleep(void); - -const short sleep_cycles_per_transmission = 4; -volatile short sleep_cycles_remaining = sleep_cycles_per_transmission; - -// -// Normal operation -// - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Prepare sleep parameters - // - - // Only the ping out role sleeps. Wake up every 4s to send a ping - if ( role == role_ping_out ) - setup_watchdog(wdt_1s); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // - // Shut down the system - // - - // Experiment with some delay here to see if it has an effect - delay(500); - - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - radio.powerDown(); - - // Sleep the MCU. The watchdog timer will awaken in a short while, and - // continue execution here. - while( sleep_cycles_remaining ) - do_sleep(); - - sleep_cycles_remaining = sleep_cycles_per_transmission; - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - // This is untouched from the pingpair example. - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it. Include our time, because the ping_out millis counter is unreliable - // due to it sleeping - printf("Got payload %lu @ %lu...",got_time,millis()); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} - -// -// Sleep helpers -// - -// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms -// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec - -void setup_watchdog(uint8_t prescalar) -{ - prescalar = min(9,prescalar); - uint8_t wdtcsr = prescalar & 7; - if ( prescalar & 8 ) - wdtcsr |= _BV(WDP3); - - MCUSR &= ~_BV(WDRF); - WDTCSR = _BV(WDCE) | _BV(WDE); - WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); -} - -ISR(WDT_vect) -{ - --sleep_cycles_remaining; -} - -void do_sleep(void) -{ - set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here - sleep_enable(); - - sleep_mode(); // System sleeps here - - sleep_disable(); // System continues execution here when watchdog timed out -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/keywords.txt b/keywords.txt index d0bd557..d829ee9 100644 --- a/keywords.txt +++ b/keywords.txt @@ -7,6 +7,9 @@ startListening KEYWORD2 stopListening KEYWORD2 write KEYWORD2 + writeFast KEYWORD2 + writeBlocking KEYWORD2 + txStandBy KEYWORD2 available KEYWORD2 read KEYWORD2 openWritingPipe KEYWORD2 From 3455e489f4c88504bd65b6b5edf10282d5a47ab3 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 2 Apr 2014 22:08:29 -0600 Subject: [PATCH 216/326] Add powerUp to begin(), increase powerUp delay - Found I could create a hang condition on startup with some code. a: Now power up the radio on begin() to simplify things b: Increase delay to 5ms for powerUp, although it should only take 2ms tops, this prevents the hanging condition. --- RF24.cpp | 4 +++- RF24.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index f534b7d..e171883 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -399,6 +399,8 @@ void RF24::begin(void) flush_rx(); flush_tx(); + powerUp(); + delay(5); //Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); @@ -449,7 +451,7 @@ void RF24::powerDown(void) void RF24::powerUp(void) { write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); - delay(2); + delay(5); } /******************************************************************/ diff --git a/RF24.h b/RF24.h index fd81d87..dfb0ff9 100644 --- a/RF24.h +++ b/RF24.h @@ -996,6 +996,7 @@ private: * - Changes to read() functionality have increased reliability and response * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments * - Delays have been removed where possible to ensure maximum efficiency + * - Untested: Arduino Due and ATTiny 84/85 support: Do NOT #include with ATTiny. * - More! See the links below and class documentation for more info. * * If issues are discovered with the documentation, please report them here: here @@ -1025,5 +1026,5 @@ private: */ #endif // __RF24_H__ -// vim:ai:cin:sts=2 sw=2 ft=cpp + From e1aaa571721d48584c43cfb287f55efc17ed97dd Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sat, 5 Apr 2014 05:34:52 -0600 Subject: [PATCH 217/326] Full Due support, maskIRQ function - Tested and now fully functional on Arduino Due - Added support for the extended SPI functions: brings Due performance up to par with Due managing the CSN pin and settings - Corrected issues with printDetails() on Due - Changed added variables to staticly defined types: ie: uint16_t instead of unsigned int - I think this will work on all ARM based boards that use the Arduino libs - Added maskIRQ function - to be used in RF24Network sleep mode - Changed csn(int mode) to csn(bool mode) - Updated GettingStarted_CallResponse.ino example: Capture start time AFTER printing to serial so measurement is more accurate --- README.md | 3 +- RF24.cpp | 211 +++++++++++++++--- RF24.h | 7 +- RF24_config.h | 54 +++-- .../GettingStarted_CallResponse.ino | 2 +- 5 files changed, 218 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index a71fb6b..cc7b97f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Design Goals: This library is designed to be... -* More complianct with the manufacturer specified operation of the chip +* More compliant with the manufacturer specified operation of the chip * More reliable and feature rich * Easy for beginners to use * Consumed with a public interface that's similiar to other Arduino standard libraries @@ -14,6 +14,7 @@ April 2014: Optimization nearing completion * Changes to read() functionality have increased reliability and response * Extended timeout periods have been added to aid in noisy or otherwise unreliable environments * Delays have been removed where possible to ensure maximum efficiency +* Arduino Due fully supported with extended SPI functions * More! See the links below and class documentation for more info. Please refer to: diff --git a/RF24.cpp b/RF24.cpp index e171883..5297c63 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -12,29 +12,29 @@ /****************************************************************************/ -void RF24::csn(int mode) +void RF24::csn(bool mode) { // Minimum ideal SPI bus speed is 2x data rate // If we assume 2Mbs data rate and 16Mhz clock, a // divider of 4 is the minimum we want. // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz #ifdef ARDUINO - #if !defined( __AVR_ATtiny85__ ) && !defined( __AVR_ATtiny84__) - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - #ifdef __arm__ // Shouldn't need to set the clock divider on DUE - SPI.setClockDivider(21); - #else - SPI.setClockDivider(SPI_CLOCK_DIV4); - #endif + #if !defined( __AVR_ATtiny85__ ) && !defined( __AVR_ATtiny84__) && !defined (__arm__) + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV2); + #endif #endif + +#ifndef __arm__ + digitalWrite(csn_pin,mode); #endif - digitalWrite(csn_pin,mode); + } /****************************************************************************/ -void RF24::ce(int level) +void RF24::ce(bool level) { digitalWrite(ce_pin,level); } @@ -45,13 +45,23 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) { uint8_t status; +#if defined (__arm__) + status = SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); + while ( len-- > 1 ){ + *buf++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); + } + *buf++ = SPI.transfer(csn_pin,0xff); + +#else csn(LOW); status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); - while ( len-- ) + while ( len-- ){ *buf++ = SPI.transfer(0xff); - + } csn(HIGH); +#endif + return status; } @@ -59,11 +69,18 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) uint8_t RF24::read_register(uint8_t reg) { + + #if defined (__arm__) + SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ) , SPI_CONTINUE); + uint8_t result = SPI.transfer(csn_pin,0xff); + #else csn(LOW); SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); uint8_t result = SPI.transfer(0xff); csn(HIGH); + #endif + return result; } @@ -73,6 +90,14 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) { uint8_t status; + #if defined (__arm__) + status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); + while ( len-- > 1){ + SPI.transfer(csn_pin,*buf++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*buf++); + #else + csn(LOW); status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); while ( len-- ) @@ -80,6 +105,8 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) csn(HIGH); + #endif + return status; } @@ -91,11 +118,18 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); + #if defined (__arm__) + status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE); + SPI.transfer(csn_pin,value); + #else + csn(LOW); status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); SPI.transfer(value); csn(HIGH); + #endif + return status; } @@ -112,6 +146,31 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) //printf("[Writing %u bytes %u blanks]",data_len,blank_len); + #if defined (__arm__) + + status = SPI.transfer(csn_pin, W_TX_PAYLOAD , SPI_CONTINUE); + + if(data_len == 32 || dynamic_payloads_enabled){ + while ( data_len-- > 1){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*current++); + }else{ + while ( data_len-- ){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + } + + if(blank_len){ + while ( blank_len-- > 1){ + SPI.transfer(csn_pin,0, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0); + } + + + #else + csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); while ( data_len-- ) @@ -120,6 +179,8 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) SPI.transfer(0); csn(HIGH); + #endif + return status; } @@ -135,6 +196,30 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) //printf("[Reading %u bytes %u blanks]",data_len,blank_len); + #if defined (__arm__) + + status = SPI.transfer(csn_pin, R_RX_PAYLOAD, SPI_CONTINUE ); + + if(data_len == 32 || dynamic_payloads_enabled){ + while ( data_len-- > 1 ){ + *current++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); + } + *current++ = SPI.transfer(csn_pin,0xff); + }else{ + while ( data_len-- > 1 ){ + *current++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); + } + + } + if(blank_len){ + while ( blank_len-- ){ + SPI.transfer(csn_pin,0xff, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0xff); + } + + #else + csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); while ( data_len-- ) @@ -143,6 +228,8 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) SPI.transfer(0xff); csn(HIGH); + #endif + return status; } @@ -152,10 +239,15 @@ uint8_t RF24::flush_rx(void) { uint8_t status; + #if defined (__arm__) + status = SPI.transfer(csn_pin, FLUSH_RX ); + #else csn(LOW); status = SPI.transfer( FLUSH_RX ); csn(HIGH); + #endif + return status; } @@ -165,10 +257,16 @@ uint8_t RF24::flush_tx(void) { uint8_t status; + #if defined (__arm__) + status = SPI.transfer(csn_pin, FLUSH_TX ); + #else + csn(LOW); status = SPI.transfer( FLUSH_TX ); csn(HIGH); + #endif + return status; } @@ -178,9 +276,14 @@ uint8_t RF24::get_status(void) { uint8_t status; + #if defined (__arm__) + status = SPI.transfer(csn_pin, NOP ); + #else + csn(LOW); status = SPI.transfer( NOP ); csn(HIGH); + #endif return status; } @@ -305,8 +408,8 @@ static const char * const rf24_crclength_e_str_P[] PROGMEM = { }; static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; -static const char rf24_pa_dbm_e_str_2[] PROGMEM = "LA_MED"; -static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_2[] PROGMEM = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_MAX"; static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { rf24_pa_dbm_e_str_0, rf24_pa_dbm_e_str_1, @@ -330,10 +433,17 @@ void RF24::printDetails(void) print_byte_register(PSTR("CONFIG"),CONFIG); print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); +#if defined(__arm__) + printf_P(PSTR("Data Rate\t = %s\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); + printf_P(PSTR("Model\t\t = %s\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); + printf_P(PSTR("CRC Length\t = %s\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); + printf_P(PSTR("PA Power\t = %s\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); +#else printf_P(PSTR("Data Rate\t = %S\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); printf_P(PSTR("Model\t\t = %S\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); printf_P(PSTR("CRC Length\t = %S\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); +#endif } #endif @@ -343,13 +453,22 @@ void RF24::begin(void) { // Initialize pins pinMode(ce_pin,OUTPUT); - pinMode(csn_pin,OUTPUT); - // Initialize SPI bus - SPI.begin(); + #if defined(__arm__) + SPI.begin(csn_pin); // Using the extended SPI features of the DUE + SPI.setClockDivider(csn_pin, 9); // Set the bus speed to 8.4mhz on Due + SPI.setBitOrder(csn_pin,MSBFIRST); // Set the bit order and mode specific to this device + SPI.setDataMode(csn_pin,SPI_MODE0); + ce(LOW); + //csn(HIGH); + #else + pinMode(csn_pin,OUTPUT); + SPI.begin(); + ce(LOW); + csn(HIGH); + #endif + - ce(LOW); - csn(HIGH); // Must allow the radio time to settle else configuration bits will not necessarily stick. // This is actually only required following power up but some settling time also appears to @@ -483,14 +602,14 @@ bool RF24::write( const void* buf, uint8_t len ) /****************************************************************************/ //For general use, the interrupt flags are not important to clear -bool RF24::writeBlocking( const void* buf, uint8_t len, unsigned long timeout ) +bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) { //Block until the FIFO is NOT full. //Keep track of the MAX retries and set auto-retry if seeing failures //This way the FIFO will fill up and allow blocking until packets go through //The radio will auto-clear everything in the FIFO as long as CE remains high - unsigned long timer = millis(); //Get the time that the payload transmission started + uint32_t timer = millis(); //Get the time that the payload transmission started while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful @@ -510,9 +629,13 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, unsigned long timeout ) void RF24::reUseTX(){ write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag - csn(LOW); - SPI.transfer( REUSE_TX_PL ); - csn(HIGH); + #if defined (__arm__) + SPI.transfer(csn_pin, REUSE_TX_PL ); + #else + csn(LOW); + SPI.transfer( REUSE_TX_PL ); + csn(HIGH); + #endif ce(LOW); //Re-Transfer packet ce(HIGH); } @@ -587,9 +710,9 @@ bool RF24::txStandBy(){ return 1; } -bool RF24::txStandBy(unsigned long timeout){ +bool RF24::txStandBy(uint32_t timeout){ - unsigned long start = millis(); + uint32_t start = millis(); while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ if( get_status() & _BV(MAX_RT)){ @@ -605,6 +728,12 @@ bool RF24::txStandBy(unsigned long timeout){ return 1; } +/****************************************************************************/ + +void RF24::maskIRQ(bool tx, bool fail, bool rx){ + + write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); +} /****************************************************************************/ @@ -612,11 +741,17 @@ uint8_t RF24::getDynamicPayloadSize(void) { uint8_t result = 0; + #if defined (__arm__) + SPI.transfer(csn_pin, R_RX_PL_WID, SPI_CONTINUE ); + result = SPI.transfer(csn_pin,0xff); + #else csn(LOW); SPI.transfer( R_RX_PL_WID ); result = SPI.transfer(0xff); csn(HIGH); + #endif + if(result > 32) { flush_rx(); return 0; } return result; } @@ -735,10 +870,16 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) void RF24::toggle_features(void) { + + #if defined (__arm__) + SPI.transfer(csn_pin, ACTIVATE, SPI_CONTINUE ); + SPI.transfer(csn_pin, 0x73 ); + #else csn(LOW); SPI.transfer( ACTIVATE ); SPI.transfer( 0x73 ); csn(HIGH); + #endif } /****************************************************************************/ @@ -800,14 +941,26 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) { const uint8_t* current = reinterpret_cast(buf); - csn(LOW); - SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); const uint8_t max_payload_size = 32; uint8_t data_len = min(len,max_payload_size); + + #if defined (__arm__) + SPI.transfer(csn_pin, W_ACK_PAYLOAD | ( pipe & B111 ), SPI_CONTINUE); + while ( data_len-- > 1 ){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*current++); + + #else + csn(LOW); + SPI.transfer(W_ACK_PAYLOAD | ( pipe & B111 ) ); + while ( data_len-- ) SPI.transfer(*current++); csn(HIGH); + + #endif } /****************************************************************************/ diff --git a/RF24.h b/RF24.h index dfb0ff9..609124d 100644 --- a/RF24.h +++ b/RF24.h @@ -54,7 +54,6 @@ private: bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ - bool avail; public: @@ -360,7 +359,7 @@ public: * @return True if transmission is successful * */ - bool txStandBy(unsigned long timeout); + bool txStandBy(uint32_t timeout); /** * Write an ack payload for the specified pipe @@ -699,7 +698,7 @@ private: * * @param mode HIGH to take this unit off the SPI bus, LOW to put it on */ - void csn(int mode); + void csn(bool mode); /** * Set chip enable @@ -707,7 +706,7 @@ private: * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet * for a much more detailed description of this pin. */ - void ce(int level); + void ce(bool level); /** * Read a chunk of data in from a register diff --git a/RF24_config.h b/RF24_config.h index 5d62655..946586b 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -24,9 +24,9 @@ //#define MINIMAL // Define _BV for non-Arduino platforms and for Arduino DUE - #if ! defined(ARDUINO) || (defined(ARDUINO) && defined(__arm__)) - #define _BV(x) (1<<(x)) - #endif + + + #undef SERIAL_DEBUG #ifdef SERIAL_DEBUG @@ -35,27 +35,29 @@ #define IF_SERIAL_DEBUG(x) #endif - // Avoid spurious warnings - #if 1 - #if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) - #undef PROGMEM - #define PROGMEM __attribute__(( section(".progmem.data") )) - #undef PSTR - #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) - #endif - #endif +// Avoid spurious warnings +// Arduino DUE is arm and uses traditional PROGMEM constructs +#if 1 +#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) +#undef PROGMEM +#define PROGMEM __attribute__(( section(".progmem.data") )) +#undef PSTR +#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) +#endif +#endif - // Progmem is Arduino-specific - #if defined(ARDUINO) && ! defined(__arm__) - #include - #define PRIPSTR "%S" - #else +// Progmem is Arduino-specific +// Arduino DUE is arm and does not include avr/pgmspace +#if defined(ARDUINO) && ! defined(__arm__) +#include +#define PRIPSTR "%S" +#else +#if ! defined(ARDUINO) // This doesn't work on Arduino DUE +typedef char const char; +#else // Fill in pgm_read_byte that is used, but missing from DUE +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif - #if ! defined(ARDUINO) - typedef char const char; - else // Fill in pgm_read_byte that is used, but missing from DUE - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) - #endif typedef uint16_t prog_uint16_t; #define PSTR(x) (x) #define printf_P printf @@ -66,14 +68,18 @@ #endif - +// Define _BV for non-Arduino platforms and for Arduino DUE +#if ! defined(ARDUINO) || (defined(ARDUINO) && defined(__arm__)) +#define _BV(x) (1<<(x)) +#endif // Stuff that is normally provided by Arduino - #if !defined ARDUINO + #if !defined (ARDUINO) #include #include #include extern HardwareSPI SPI; + #define _BV(x) (1<<(x)) #else #if !defined( __AVR_ATtiny85__ ) || defined( __AVR_ATtiny84__) diff --git a/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino b/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino index d491a83..5f09ad2 100644 --- a/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino +++ b/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino @@ -61,10 +61,10 @@ void loop(void) { if (role == role_ping_out){ // Radio is in ping mode byte gotByte; // Initialize a variable for the incoming response - unsigned long time = micros(); // Record the current microsecond count radio.stopListening(); // First, stop listening so we can talk. printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload + unsigned long time = micros(); // Record the current microsecond count if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank From d28323aadca8fa626315a9748c370d7e5a05dc64 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Sun, 6 Apr 2014 08:49:35 -0600 Subject: [PATCH 218/326] Fix: Missing byte with due RX - SPI transfer fix to read every available byte in the payload with payloads less than 32 bytes with dynamic payload size disabled. --- RF24.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 5297c63..a2a501f 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -206,7 +206,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) } *current++ = SPI.transfer(csn_pin,0xff); }else{ - while ( data_len-- > 1 ){ + while ( data_len-- ){ *current++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); } From afdb084daf9fce9c8792281c7fd17683ce2ce1e2 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Mon, 7 Apr 2014 20:41:38 -0600 Subject: [PATCH 219/326] Fix: Due write() issues. Found that Due would send multiple payloads or fail in writing in some cases. Change ensures single-payload transmission using write(). - Updated pingpair_dyn.ino example sketch to conform with available() changes - Slight doc update --- RF24.cpp | 4 +-- RF24.h | 34 ++++++++++++++++++- .../{pingpair_dyn.pde => pingpair_dyn.ino} | 20 +++++------ 3 files changed, 44 insertions(+), 14 deletions(-) rename examples/pingpair_dyn/{pingpair_dyn.pde => pingpair_dyn.ino} (94%) diff --git a/RF24.cpp b/RF24.cpp index a2a501f..f6c63d0 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -579,15 +579,13 @@ void RF24::powerUp(void) bool RF24::write( const void* buf, uint8_t len ) { //Start Writing - startFastWrite(buf,len); + startWrite(buf,len); //Wait until complete or failed //ACK payloads that are handled improperly will cause this to hang //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } - ce(LOW); //Set the radio back to STANDBY-I mode since we can only fill one buffer at a time using this method, and tx is complete - uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); //Max retries exceeded diff --git a/RF24.h b/RF24.h index 609124d..ccf7417 100644 --- a/RF24.h +++ b/RF24.h @@ -263,6 +263,9 @@ public: * timeout, and return 1 or 0 respectively. From a user perspective, just * keep trying to send the same data. The library will keep auto retrying * the current payload using the built in functionality. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. * * ONLY max retry interrupt flags will be cleared when writeFast is called * @@ -292,6 +295,9 @@ public: * It will not block until the 3 FIFO buffers are filled with data. * If so the library will auto retry until a new payload is written * or the user specified timeout period is reached. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. * * ONLY max retry interrupt flags will be cleared when writeBlocking is called * @code @@ -409,6 +415,9 @@ public: * @note Optimization: This function now leaves the CE pin high, so the radio * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. * This allows the chip to be used to its full potential in TX mode. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. * * @see write() * @see writeFast() @@ -500,7 +509,20 @@ public: bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } /** + * The radio will generate interrupt signals when a transmission is complete, + * a transmission fails, or a payload is received. This allows users to mask + * those interrupts to prevent them from generating a signal on the interrupt + * pin. * + * @code + * Mask all interrupts except the receive interrupt: + * + * radio.maskIRQ(1,1,0); + * @endcode + * + * @param tx_ok Mask transmission complete interrupts + * @param tx_fail Mask transmit failure interrupts + * @param rx_ready Mask payload received interrupts */ void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); @@ -995,7 +1017,8 @@ private: * - Changes to read() functionality have increased reliability and response * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments * - Delays have been removed where possible to ensure maximum efficiency - * - Untested: Arduino Due and ATTiny 84/85 support: Do NOT #include with ATTiny. + * - Full Due support with extended SPI functions + * - Initial ATTiny support added (Untested) * - More! See the links below and class documentation for more info. * * If issues are discovered with the documentation, please report them here: here @@ -1013,6 +1036,15 @@ private: * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or * the SPI hardware will go into 'slave' mode. * + * @section BoardSupport Board Support + * + * Most standard Arduino based boards are supported: + * - ATMega 328 based boards (Uno, Nano, etc) + * - Mega Boards (1280, 2560, etc) + * - ARM based boards (Arduino Due) Note: Do not include printf.h or use printf begin. This functionality is already present. Must use one of the + * hardware SS/CSN pins. + * - ATTiny boards (Not fully tested) Note: Do not include SPI.h. The SPI functions for ATTiny are already included. + * * @section More More Information * * @section Info and Projects diff --git a/examples/pingpair_dyn/pingpair_dyn.pde b/examples/pingpair_dyn/pingpair_dyn.ino similarity index 94% rename from examples/pingpair_dyn/pingpair_dyn.pde rename to examples/pingpair_dyn/pingpair_dyn.ino index 7108f3b..16fc01b 100644 --- a/examples/pingpair_dyn/pingpair_dyn.pde +++ b/examples/pingpair_dyn/pingpair_dyn.ino @@ -15,7 +15,7 @@ #include #include "nRF24L01.h" #include "RF24.h" -#include "printf.h" +//#include "printf.h" // // Hardware configuration @@ -27,7 +27,7 @@ RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter -const int role_pin = 7; +const int role_pin = 5; // // Topology @@ -61,7 +61,7 @@ role_e role; const int min_payload_size = 4; const int max_payload_size = 32; -const int payload_size_increments_by = 2; +const int payload_size_increments_by = 1; int next_payload_size = min_payload_size; char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char @@ -74,7 +74,7 @@ void setup(void) // set up the role pin pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); + digitalWrite(role_pin,LOW); delay(20); // Just to get a solid reading on the role pin // read the address pin, establish our role @@ -88,7 +88,7 @@ void setup(void) // Serial.begin(57600); - printf_begin(); + //printf_begin(); printf("\n\rRF24/examples/pingpair_dyn/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); @@ -102,7 +102,7 @@ void setup(void) radio.enableDynamicPayloads(); // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); + radio.setRetries(5,15); // // Open pipes to other nodes for communication @@ -189,7 +189,7 @@ void loop(void) next_payload_size = min_payload_size; // Try again 1s later - delay(1000); + delay(100); } // @@ -204,11 +204,11 @@ void loop(void) // Dump the payloads until we've gotten everything uint8_t len; bool done = false; - while (!done) + while (radio.available()) { // Fetch the payload, and see if this was the last one. len = radio.getDynamicPayloadSize(); - done = radio.read( receive_payload, len ); + radio.read( receive_payload, len ); // Put a zero at the end for easy printing receive_payload[len] = 0; @@ -229,4 +229,4 @@ void loop(void) } } } -// vim:cin:ai:sts=2 sw=2 ft=cpp +// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file From 84146a17f301d4e341f12db9613bff44cc285ae0 Mon Sep 17 00:00:00 2001 From: zephyrr Date: Mon, 7 Apr 2014 23:31:52 -0700 Subject: [PATCH 220/326] Revamped write_payload() and read_payload() Restructured based on needing or not needing blank padding --- RF24.cpp | 92 +++++++++++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index f6c63d0..a7869d5 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -135,48 +135,45 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) /****************************************************************************/ -uint8_t RF24::write_payload(const void* buf, uint8_t len) +uint8_t RF24::write_payload(const void* buf, uint8_t data_len) { uint8_t status; - const uint8_t* current = reinterpret_cast(buf); - uint8_t data_len = min(len,payload_size); + if(data_len > payload_size) data_len = payload_size; uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; //printf("[Writing %u bytes %u blanks]",data_len,blank_len); - #if defined (__arm__) + #if defined (__arm__) - status = SPI.transfer(csn_pin, W_TX_PAYLOAD , SPI_CONTINUE); - - if(data_len == 32 || dynamic_payloads_enabled){ - while ( data_len-- > 1){ - SPI.transfer(csn_pin,*current++, SPI_CONTINUE); - } - SPI.transfer(csn_pin,*current++); - }else{ - while ( data_len-- ){ - SPI.transfer(csn_pin,*current++, SPI_CONTINUE); - } - } - - if(blank_len){ - while ( blank_len-- > 1){ - SPI.transfer(csn_pin,0, SPI_CONTINUE); - } - SPI.transfer(csn_pin,0); - } + status = SPI.transfer(csn_pin, W_TX_PAYLOAD , SPI_CONTINUE); + if(blank_len){ + while ( data_len--){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + while ( --blank_len ){ + SPI.transfer(csn_pin,0, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0); + }else{ + while( --data_len ){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*current); + } #else csn(LOW); status = SPI.transfer( W_TX_PAYLOAD ); - while ( data_len-- ) + while ( data_len-- ) { SPI.transfer(*current++); - while ( blank_len-- ) + } + while ( blank_len-- ) { SPI.transfer(0); + } csn(HIGH); #endif @@ -186,46 +183,47 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len) /****************************************************************************/ -uint8_t RF24::read_payload(void* buf, uint8_t len) +uint8_t RF24::read_payload(void* buf, uint8_t data_len) { uint8_t status; uint8_t* current = reinterpret_cast(buf); - uint8_t data_len = min(len,payload_size); + if(data_len > payload_size) data_len = payload_size; uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; //printf("[Reading %u bytes %u blanks]",data_len,blank_len); + #if defined (__arm__) - status = SPI.transfer(csn_pin, R_RX_PAYLOAD, SPI_CONTINUE ); + status = SPI.transfer(csn_pin, R_RX_PAYLOAD, SPI_CONTINUE ); - if(data_len == 32 || dynamic_payloads_enabled){ - while ( data_len-- > 1 ){ - *current++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); - } - *current++ = SPI.transfer(csn_pin,0xff); - }else{ - while ( data_len-- ){ - *current++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); - } + if( blank_len ){ - } - if(blank_len){ - while ( blank_len-- ){ - SPI.transfer(csn_pin,0xff, SPI_CONTINUE); - } - SPI.transfer(csn_pin,0xff); - } + while ( data_len-- ){ + *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + while ( --blank_len ){ + SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0xFF); + }else{ + while ( --data_len ){ + *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + *current = SPI.transfer(csn_pin,0xFF); + } #else csn(LOW); status = SPI.transfer( R_RX_PAYLOAD ); - while ( data_len-- ) - *current++ = SPI.transfer(0xff); - while ( blank_len-- ) + while ( data_len-- ) { + *current++ = SPI.transfer(0xFF); + } + while ( blank_len-- ) { SPI.transfer(0xff); + } csn(HIGH); #endif From 07eb9f874999c2d862a99d452a29d08a0798760d Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 9 Apr 2014 10:53:47 -0600 Subject: [PATCH 221/326] ATTiny support fr jscrane fork tested and working ATTiny support pulled in from https://github.com/jscrane/RF24 - SPI support for ATTiny is included with the library. Do NOT include SPI.h with tiny. Pin info etc can be found at: http://programmablehardware.blogspot.ca/2013/09/rf24-with-attiny84.html ATTiny 85 Example: RF24 radio(4,3); // CE: 4 CS: 3 --- RF24.cpp | 71 +++++++++++++++--------------- RF24.h | 11 +++-- RF24_config.h | 118 +++++++++++++++++++++++--------------------------- 3 files changed, 97 insertions(+), 103 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index a7869d5..4fe07bb 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -380,7 +380,7 @@ uint8_t RF24::getPayloadSize(void) /****************************************************************************/ -#if !defined (MINIMAL) + static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; @@ -414,7 +414,7 @@ static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { rf24_pa_dbm_e_str_2, rf24_pa_dbm_e_str_3, }; - +#if !defined (MINIMAL) void RF24::printDetails(void) { print_status(get_status()); @@ -1210,50 +1210,51 @@ void RF24::setRetries(uint8_t delay, uint8_t count) } +//ATTiny support code pulled in from https://github.com/jscrane/RF24 - - - -#if defined (ATTINY) - -#include "pins_arduino.h" - -#if defined( __AVR_ATtiny85__ ) -const static uint8_t _CS = PB4; -const static uint8_t _MOSI = PB1; -const static uint8_t _MISO = PB0; -const static uint8_t _SCK = PB2; +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) +// see http://gammon.com.au/spi +# define DI 0 // D0, pin 5 Data In +# define DO 1 // D1, pin 6 Data Out (this is *not* MOSI) +# define USCK 2 // D2, pin 7 Universal Serial Interface clock +# define SS 3 // D3, pin 2 Slave Select +#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +// these depend on the core used (check pins_arduino.h) +// this is for jeelabs' one (based on google-code core) +# define DI 4 // PA6 +# define DO 5 // PA5 +# define USCK 6 // PA4 +# define SS 3 // PA7 #endif -#if defined( __AVR_ATtiny84__ ) -const static uint8_t _CS = 3; -const static uint8_t _MOSI = 5; -const static uint8_t _MISO = 4; -const static uint8_t _SCK = 6; -#endif - -SPIClass SPI; +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) void SPIClass::begin() { - pinMode(_SCK, OUTPUT); - pinMode(_MOSI, OUTPUT); - pinMode(_MISO,INPUT); + digitalWrite(SS, HIGH); + pinMode(USCK, OUTPUT); + pinMode(DO, OUTPUT); + pinMode(SS, OUTPUT); + pinMode(DI, INPUT); + USICR = _BV(USIWM0); - digitalWrite(_MISO,HIGH); - digitalWrite(_SCK, LOW); - digitalWrite(_MOSI, LOW); } -void SPIClass::end() { +byte SPIClass::transfer(byte b) { + + USIDR = b; + USISR = _BV(USIOIF); + do + USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); + while ((USISR & _BV(USIOIF)) == 0); + return USIDR; - pinMode(_SCK, INPUT); - pinMode(_MOSI, INPUT); - pinMode(_MISO,INPUT); } -void SPIClass::setDataMode(byte mode){} -void SPIClass::setClockDivider(byte rate){} +void SPIClass::end() {} +void SPIClass::setDataMode(uint8_t mode){} +void SPIClass::setBitOrder(uint8_t bitOrder){} +void SPIClass::setClockDivider(uint8_t rate){} + #endif - diff --git a/RF24.h b/RF24.h index ccf7417..e92d7c4 100644 --- a/RF24.h +++ b/RF24.h @@ -948,7 +948,7 @@ private: */ /** - * @example pingpair_dyn.pde + * @example pingpair_dyn.ino * * This is an example of how to use payloads of a varying (dynamic) size. */ @@ -1018,7 +1018,7 @@ private: * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments * - Delays have been removed where possible to ensure maximum efficiency * - Full Due support with extended SPI functions - * - Initial ATTiny support added (Untested) + * - ATTiny 24/44/84 25/45/85 now supported. * - More! See the links below and class documentation for more info. * * If issues are discovered with the documentation, please report them here: here @@ -1042,8 +1042,11 @@ private: * - ATMega 328 based boards (Uno, Nano, etc) * - Mega Boards (1280, 2560, etc) * - ARM based boards (Arduino Due) Note: Do not include printf.h or use printf begin. This functionality is already present. Must use one of the - * hardware SS/CSN pins. - * - ATTiny boards (Not fully tested) Note: Do not include SPI.h. The SPI functions for ATTiny are already included. + * hardware SS/CSN pins as extended SPI methods are used. + * Initial Due support taken from https://github.com/mcrosson/RF24/tree/due + * - ATTiny board support added from https://github.com/jscrane/RF24 + * Note: ATTiny support is built into the library. Do not include SPI.h. + * See here for wiring/pin info. * * @section More More Information * diff --git a/RF24_config.h b/RF24_config.h index 946586b..b7154a5 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -24,8 +24,15 @@ //#define MINIMAL // Define _BV for non-Arduino platforms and for Arduino DUE - - +#if defined (ARDUINO) + #include +#else + #include + #include + #include + extern HardwareSPI SPI; + #define _BV(x) (1<<(x)) + #endif #undef SERIAL_DEBUG @@ -33,6 +40,9 @@ #define IF_SERIAL_DEBUG(x) ({x;}) #else #define IF_SERIAL_DEBUG(x) + #if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) + #define printf_P(...) + #endif #endif // Avoid spurious warnings @@ -49,13 +59,13 @@ // Progmem is Arduino-specific // Arduino DUE is arm and does not include avr/pgmspace #if defined(ARDUINO) && ! defined(__arm__) -#include -#define PRIPSTR "%S" + #include + #define PRIPSTR "%S" #else #if ! defined(ARDUINO) // This doesn't work on Arduino DUE -typedef char const char; + typedef char const char; #else // Fill in pgm_read_byte that is used, but missing from DUE -#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) #endif typedef uint16_t prog_uint16_t; @@ -65,75 +75,55 @@ typedef char const char; #define PROGMEM #define pgm_read_word(p) (*(p)) #define PRIPSTR "%s" - #endif - - -// Define _BV for non-Arduino platforms and for Arduino DUE -#if ! defined(ARDUINO) || (defined(ARDUINO) && defined(__arm__)) -#define _BV(x) (1<<(x)) #endif - // Stuff that is normally provided by Arduino - #if !defined (ARDUINO) - - #include - #include - #include - extern HardwareSPI SPI; - #define _BV(x) (1<<(x)) - #else - - #if !defined( __AVR_ATtiny85__ ) || defined( __AVR_ATtiny84__) - #include - #else - #define ATTINY - - #define SPI_CLOCK_DIV4 0x00 - #define SPI_CLOCK_DIV16 0x01 - #define SPI_CLOCK_DIV64 0x02 - #define SPI_CLOCK_DIV128 0x03 - #define SPI_CLOCK_DIV2 0x04 - #define SPI_CLOCK_DIV8 0x05 - #define SPI_CLOCK_DIV32 0x06 - //#define SPI_CLOCK_DIV64 0x07 - - #define SPI_MODE0 0x00 - #define SPI_MODE1 0x04 - #define SPI_MODE2 0x08 - #define SPI_MODE3 0x0C - - #define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR - #define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR - #define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR - class SPIClass { - public: - inline static byte transfer(byte _data); +// ATTiny support code is from https://github.com/jscrane/RF24 - static void begin(); // Default - static void end(); +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +#include +#include +#include - static void setDataMode(byte); - static void setClockDivider(byte); - static void setBitOrder(byte); - }; +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 +//#define SPI_CLOCK_DIV64 0x07 - extern SPIClass SPI; +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C - byte SPIClass::transfer(byte _data){ +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR - USIDR = _data; - USISR = _BV(USIOIF); - while((USISR & _BV(USIOIF)) == 0){ - USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); - } - return USIDR; - } - #endif //ATTINY - #endif //Defined Arduino +class SPIClass { +public: + static byte transfer(byte _data); + // SPI Configuration methods + + inline static void attachInterrupt(); + inline static void detachInterrupt(); // Default + + static void begin(); // Default + static void end(); + + static void setBitOrder(uint8_t); + static void setDataMode(uint8_t); + static void setClockDivider(uint8_t); +}; +extern SPIClass SPI; + +#endif //ATTiny #endif // __RF24_CONFIG_H__ From 92a19af8f808967aa03cf4e18d8e4c9fd3cc27fc Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 10 Apr 2014 09:49:23 -0600 Subject: [PATCH 222/326] enableDynamicAck(), Single NOACK, default RT, - Added new function: enableDynamicAck() to enable single-write NOACK payloads (merge closer with gcopeland fork) - Added single NOACK writes for write(), writeFast(), startWrite() and startFastWrite() - powerUp() now checks to see if radio is powered on. No delay if powered up. - radio must still be powered up manually if powered down manually - use setRetries function in begin() - set CE low on powerDown - updated readme --- README.md | 32 +++++++++++++------- RF24.cpp | 85 ++++++++++++++++++++++++++++++++++++++---------------- RF24.h | 69 +++++++++++++++++++++++++++++++++++++++----- nRF24L01.h | 30 ++++++++++--------- 4 files changed, 158 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index cc7b97f..054d80d 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,23 @@ Design Goals: This library is designed to be... -* More compliant with the manufacturer specified operation of the chip +* More compliant with the manufacturer specified operation of the chip, while allowing advanced users +* to work outside the reccommended operation. +* Utilize the capabilities of the radio to their full potential via Arduino * More reliable and feature rich -* Easy for beginners to use +* Easy for beginners to use, with well documented examples and features * Consumed with a public interface that's similiar to other Arduino standard libraries * Built against the standard SPI library. -March 2014: Optimization begun -April 2014: Optimization nearing completion -* The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds -* Changes to read() functionality have increased reliability and response -* Extended timeout periods have been added to aid in noisy or otherwise unreliable environments -* Delays have been removed where possible to ensure maximum efficiency -* Arduino Due fully supported with extended SPI functions -* More! See the links below and class documentation for more info. +April 2014: Official Release: Still some work to do, but most benefits have been realized + +* The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds +* Changes to read() and available () functionality have increased reliability and response +* Extended timeout periods have been added to aid in noisy or otherwise unreliable environments +* Delays have been removed where possible to ensure maximum efficiency +* Full Due support with extended SPI functions +* ATTiny 24/44/84 25/45/85 now supported. +* More! See the links below and class documentation for more info. Please refer to: @@ -27,4 +30,11 @@ Please refer to: This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or the SPI hardware will go into 'slave' mode. - + +Supported Boards: + +* Uno, Nano, etc (328 based boards) +* Mega Types (2560, 1280, etc) +* ARM (Arduino Due) via extended SPI methods +* ATTiny 24/44/84 25/45/85 +* See the [documentation](http://tmrh20.github.io/) for more info \ No newline at end of file diff --git a/RF24.cpp b/RF24.cpp index 4fe07bb..36d8b3f 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -135,7 +135,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) /****************************************************************************/ -uint8_t RF24::write_payload(const void* buf, uint8_t data_len) +uint8_t RF24::write_payload(const void* buf, uint8_t data_len, const uint8_t writeType) { uint8_t status; const uint8_t* current = reinterpret_cast(buf); @@ -147,7 +147,7 @@ uint8_t RF24::write_payload(const void* buf, uint8_t data_len) #if defined (__arm__) - status = SPI.transfer(csn_pin, W_TX_PAYLOAD , SPI_CONTINUE); + status = SPI.transfer(csn_pin, writeType , SPI_CONTINUE); if(blank_len){ while ( data_len--){ @@ -160,14 +160,14 @@ uint8_t RF24::write_payload(const void* buf, uint8_t data_len) }else{ while( --data_len ){ SPI.transfer(csn_pin,*current++, SPI_CONTINUE); - } + } SPI.transfer(csn_pin,*current); } #else csn(LOW); - status = SPI.transfer( W_TX_PAYLOAD ); + status = SPI.transfer( writeType ); while ( data_len-- ) { SPI.transfer(*current++); } @@ -205,7 +205,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t data_len) } while ( --blank_len ){ SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); - } + } SPI.transfer(csn_pin,0xFF); }else{ while ( --data_len ){ @@ -346,7 +346,7 @@ void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) /****************************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), + ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false), payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), pipe0_reading_address(0) { @@ -466,8 +466,6 @@ void RF24::begin(void) csn(HIGH); #endif - - // Must allow the radio time to settle else configuration bits will not necessarily stick. // This is actually only required following power up but some settling time also appears to // be required after resets too. For full coverage, we'll always assume the worst. @@ -479,7 +477,7 @@ void RF24::begin(void) // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet // sizes must never be used. See documentation for a more complete explanation. - write_register(SETUP_RETR,(B0100 << ARD) | (B1111 << ARC)); + setRetries(5,15); // Restore our default PA level setPALevel( RF24_PA_MAX ) ; @@ -516,9 +514,10 @@ void RF24::begin(void) flush_rx(); flush_tx(); - powerUp(); - delay(5); - //Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + powerUp(); //Power up by default when begin() is called + + // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + // PTX should use only 22uA of power write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); @@ -528,7 +527,8 @@ void RF24::begin(void) void RF24::startListening(void) { - write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); + powerUp(); + write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Restore the pipe0 adddress, if exists @@ -559,6 +559,7 @@ void RF24::stopListening(void) void RF24::powerDown(void) { + ce(LOW); // Guarantee CE is low on powerDown write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); } @@ -567,17 +568,23 @@ void RF24::powerDown(void) //Power up now. Radio will not power down unless instructed by MCU for config changes etc. void RF24::powerUp(void) { - write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); - delay(5); + uint8_t cfg = read_register(CONFIG); + + // if not powered up then power up and wait for the radio to initialize + if (!(cfg & _BV(PWR_UP))){ + write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); + delayMicroseconds(1500); + + } } /******************************************************************/ //Similar to the previous write, clears the interrupt flags -bool RF24::write( const void* buf, uint8_t len ) +bool RF24::write( const void* buf, uint8_t len, const bool multicast ) { //Start Writing - startWrite(buf,len); + startWrite(buf,len,multicast); //Wait until complete or failed //ACK payloads that are handled improperly will cause this to hang @@ -595,6 +602,9 @@ bool RF24::write( const void* buf, uint8_t len ) return 1; } +bool RF24::write( const void* buf, uint8_t len ){ + write(buf,len,0); +} /****************************************************************************/ //For general use, the interrupt flags are not important to clear @@ -617,7 +627,7 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) } //Start Writing - startFastWrite(buf,len); //Write the payload if a buffer is clear + startFastWrite(buf,len,0); //Write the payload if a buffer is clear return 1; //Return 1 to indicate successful transmission } @@ -638,7 +648,7 @@ void RF24::reUseTX(){ /****************************************************************************/ -bool RF24::writeFast( const void* buf, uint8_t len ) +bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) { //Block until the FIFO is NOT full. //Keep track of the MAX retries and set auto-retry if seeing failures @@ -656,12 +666,14 @@ bool RF24::writeFast( const void* buf, uint8_t len ) } //Start Writing - startFastWrite(buf,len); + startFastWrite(buf,len,multicast); return 1; } - +bool RF24::writeFast( const void* buf, uint8_t len ){ + writeFast(buf,len,0); +} /****************************************************************************/ @@ -670,9 +682,10 @@ bool RF24::writeFast( const void* buf, uint8_t len ) //Otherwise we enter Standby-II mode, which is still faster than standby mode //Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data -void RF24::startFastWrite( const void* buf, uint8_t len ){ //TMRh20 +void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 - write_payload( buf,len); + //write_payload( buf,len); + write_payload( buf, len,multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ) ; ce(HIGH); } @@ -680,11 +693,12 @@ void RF24::startFastWrite( const void* buf, uint8_t len ){ //TMRh20 //Added the original startWrite back in so users can still use interrupts, ack payloads, etc //Allows the library to pass all tests -void RF24::startWrite( const void* buf, uint8_t len ){ +void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){ // Send the payload - write_payload( buf, len ); + //write_payload( buf, len ); + write_payload( buf, len,multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ) ; ce(HIGH); ce(LOW); @@ -933,6 +947,27 @@ void RF24::enableAckPayload(void) /****************************************************************************/ +void RF24::enableDynamicAck(void){ + // + // enable dynamic ack features + // + + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); + + // If it didn't work, the features are not enabled + if ( ! read_register(FEATURE) & _BV(EN_DYN_ACK) ){ + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); + } + + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + + +} + +/****************************************************************************/ + void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) { const uint8_t* current = reinterpret_cast(buf); diff --git a/RF24.h b/RF24.h index e92d7c4..e2e0b77 100644 --- a/RF24.h +++ b/RF24.h @@ -256,6 +256,22 @@ public: */ void powerUp(void) ; + /** + * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * + * Can be used with enableAckPayload() to request a response + * @see enableDynamicAck() + * @see setAutoAck() + * @see write() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0), NOACK (1) + */ + bool write( const void* buf, uint8_t len, const bool multicast ); + /** * @note Optimization: New Command * * This will not block until the 3 FIFO buffers are filled with data. @@ -289,6 +305,19 @@ public: */ bool writeFast( const void* buf, uint8_t len ); + /** + * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + */ + bool writeFast( const void* buf, uint8_t len, const bool multicast ); + /** * @note Optimization: New Command * This function extends the auto-retry mechanism to any specified duration. @@ -383,6 +412,20 @@ public: */ void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + /** + * Enable dynamic ACKs (single write multicasting) for chosen messages + * + * @note To enable full multicasting or per-pipe multicast, use setAutoAck() + * + * @warning This MUST be called prior to attempting single write NOACK calls + * @code + * radio.enableDynamicAck(); + * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested + * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK + * @endcode + */ + void enableDynamicAck(); + /** * Determine if an ack payload was received in the most recent call to * write(). @@ -424,11 +467,16 @@ public: * @see startWrite() * @see writeBlocking() * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) * @return True if the payload was delivered successfully false if not */ - void startFastWrite( const void* buf, uint8_t len ); + void startFastWrite( const void* buf, uint8_t len, const bool multicast ); /** * Non-blocking write to the open writing pipe @@ -445,11 +493,16 @@ public: * @see startFastWrite() * @see whatHappened() * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * * @param buf Pointer to the data to be sent * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) * */ - void startWrite( const void* buf, uint8_t len ); + void startWrite( const void* buf, uint8_t len, const bool multicast ); /** * Optimization: New Command @@ -776,7 +829,7 @@ private: * @param len Number of bytes to be sent * @return Current value of status register */ - uint8_t write_payload(const void* buf, uint8_t len); + uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); /** * Read the receive payload @@ -1003,18 +1056,18 @@ private: * @section Goals Design Goals * * This library fork is designed to be... - * @li More compliant with the manufacturer specified operation of the chip + * @li More compliant with the manufacturer specified operation of the chip, while allowing advanced users + * to work outside the reccommended operation. * @li Utilize the capabilities of the radio to their full potential via Arduino * @li More reliable, responsive and feature rich - * @li Easy for beginners to use + * @li Easy for beginners to use, with well documented examples and features * @li Consumed with a public interface that's similiar to other Arduino standard libraries * * @section News News * - * March 2014: Optimization begun
- * April 2014: Optimization nearing completion

+ * April 2014: Official Release: Still some work to do, but most benefits have been realized
* - The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds - * - Changes to read() functionality have increased reliability and response + * - Changes to read() and available () functionality have increased reliability and response * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments * - Delays have been removed where possible to ensure maximum efficiency * - Full Due support with extended SPI functions diff --git a/nRF24L01.h b/nRF24L01.h index 2012ce6..132de94 100644 --- a/nRF24L01.h +++ b/nRF24L01.h @@ -1,24 +1,25 @@ /* Copyright (c) 2007 Stefan Engelke + Portions Copyright (C) 2011 Greg Copeland - 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 + 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 + 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 + 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. */ @@ -117,6 +118,7 @@ /* P model memory Map */ #define RPD 0x09 +#define W_TX_PAYLOAD_NO_ACK 0xB0 /* P model bit Mnemonics */ #define RF_DR_LOW 5 From 185831233868b07310b69daefa63b169f1bf8ed4 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 10 Apr 2014 13:01:51 -0600 Subject: [PATCH 223/326] Revert powerUp() delay to 5ms delay For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode. There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode before the CEis set high. 5ms is the maximum defined in the v1.0 datasheet --- RF24.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 36d8b3f..92659d8 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -573,7 +573,11 @@ void RF24::powerUp(void) // if not powered up then power up and wait for the radio to initialize if (!(cfg & _BV(PWR_UP))){ write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); - delayMicroseconds(1500); + + // For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode. + // There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode before + // the CEis set high. - Tpd2stby can be up to 5ms per the 1.0 datasheet + delay(5); } } From 5fabf2608a64572876e5b7b94a0e7e66b392cd8c Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 11 Apr 2014 02:17:42 -0600 Subject: [PATCH 224/326] Fix: Return value of write, writefast --- RF24.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index 92659d8..6b14a16 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -607,7 +607,7 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast ) } bool RF24::write( const void* buf, uint8_t len ){ - write(buf,len,0); + return write(buf,len,0); } /****************************************************************************/ @@ -676,7 +676,7 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) } bool RF24::writeFast( const void* buf, uint8_t len ){ - writeFast(buf,len,0); + return writeFast(buf,len,0); } /****************************************************************************/ From e83a107bdf8e7430457975b1d9251cc5c6ba7107 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 15 Apr 2014 09:40:36 -0600 Subject: [PATCH 225/326] Update examples, minimize code size - Updated some example files and tests - Small changes to minimize code size --- RF24.cpp | 112 ++++++------------ RF24.h | 6 +- examples/GettingStarted/GettingStarted.ino | 9 +- .../GettingStarted_CallResponse.ino | 6 +- examples/Transfer/printf.h | 8 +- examples/pingpair_ack/pingpair_ack.ino | 2 +- examples/pingpair_dyn/pingpair_dyn.ino | 8 +- examples/pingpair_irq/pingpair_irq.ino | 19 +-- examples/pingpair_sleepy/pingpair_sleepy.ino | 13 +- examples/scanner/{scanner.pde => scanner.ino} | 10 +- tests/pingpair_blocking/pingpair_blocking.pde | 24 ++-- tests/pingpair_test/pingpair_test.pde | 26 ++-- 12 files changed, 97 insertions(+), 146 deletions(-) rename examples/scanner/{scanner.pde => scanner.ino} (94%) diff --git a/RF24.cpp b/RF24.cpp index 6b14a16..bb16c95 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -235,55 +235,39 @@ uint8_t RF24::read_payload(void* buf, uint8_t data_len) uint8_t RF24::flush_rx(void) { - uint8_t status; - #if defined (__arm__) - status = SPI.transfer(csn_pin, FLUSH_RX ); - #else - csn(LOW); - status = SPI.transfer( FLUSH_RX ); - csn(HIGH); - - #endif - - return status; + return spiTrans( FLUSH_TX ); } /****************************************************************************/ uint8_t RF24::flush_tx(void) { - uint8_t status; + return spiTrans( FLUSH_TX ); +} +/****************************************************************************/ + +uint8_t RF24::spiTrans(uint8_t cmd){ + + uint8_t status; #if defined (__arm__) - status = SPI.transfer(csn_pin, FLUSH_TX ); + status = SPI.transfer(csn_pin, cmd ); #else csn(LOW); - status = SPI.transfer( FLUSH_TX ); + status = SPI.transfer( cmd ); csn(HIGH); - #endif - return status; + } /****************************************************************************/ uint8_t RF24::get_status(void) { - uint8_t status; - - #if defined (__arm__) - status = SPI.transfer(csn_pin, NOP ); - #else - - csn(LOW); - status = SPI.transfer( NOP ); - csn(HIGH); - #endif - - return status; + return spiTrans(NOP); } /****************************************************************************/ @@ -347,7 +331,7 @@ void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) RF24::RF24(uint8_t _cepin, uint8_t _cspin): ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false), - payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), + payload_size(32), dynamic_payloads_enabled(false), pipe0_reading_address(0) { } @@ -367,8 +351,7 @@ void RF24::setChannel(uint8_t channel) void RF24::setPayloadSize(uint8_t size) { - const uint8_t max_payload_size = 32; - payload_size = min(size,max_payload_size); + payload_size = min(size,32); } /****************************************************************************/ @@ -380,7 +363,7 @@ uint8_t RF24::getPayloadSize(void) /****************************************************************************/ - +#if !defined (MINIMAL) static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; @@ -414,7 +397,7 @@ static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { rf24_pa_dbm_e_str_2, rf24_pa_dbm_e_str_3, }; -#if !defined (MINIMAL) + void RF24::printDetails(void) { print_status(get_status()); @@ -479,8 +462,8 @@ void RF24::begin(void) // sizes must never be used. See documentation for a more complete explanation. setRetries(5,15); - // Restore our default PA level - setPALevel( RF24_PA_MAX ) ; + // Reset value is MAX + //setPALevel( RF24_PA_MAX ) ; // Determine if this is a p or non-p RF24 module and then // reset our data rate back to default value. This works @@ -498,8 +481,8 @@ void RF24::begin(void) // Initialize CRC and request 2-byte (16bit) CRC setCRCLength( RF24_CRC_16 ) ; - // Disable dynamic payloads, to match dynamic_payloads_enabled setting - write_register(DYNPD,0); + // Disable dynamic payloads, to match dynamic_payloads_enabled setting - Reset value is 0 + //write_register(DYNPD,0); // Reset current status // Notice reset and flush is the last thing we do @@ -532,8 +515,9 @@ void RF24::startListening(void) write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); // Restore the pipe0 adddress, if exists - if (pipe0_reading_address) + if (pipe0_reading_address){ write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + } // Flush buffers flush_rx(); @@ -636,16 +620,11 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) return 1; //Return 1 to indicate successful transmission } +/****************************************************************************/ void RF24::reUseTX(){ write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag - #if defined (__arm__) - SPI.transfer(csn_pin, REUSE_TX_PL ); - #else - csn(LOW); - SPI.transfer( REUSE_TX_PL ); - csn(HIGH); - #endif + spiTrans( REUSE_TX_PL ); ce(LOW); //Re-Transfer packet ce(HIGH); } @@ -659,11 +638,11 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) //Return 0 so the user can control the retrys and set a timer or failure counter if required //The radio will auto-clear everything in the FIFO as long as CE remains high - while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful if( get_status() & _BV(MAX_RT)){ - reUseTX(); //Set re-transmit + //reUseTX(); //Set re-transmit + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag return 0; //Return 0. The previous payload has been retransmitted //From the user perspective, if you get a 0, just keep trying to send the same payload } @@ -689,7 +668,7 @@ bool RF24::writeFast( const void* buf, uint8_t len ){ void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 //write_payload( buf,len); - write_payload( buf, len,multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ) ; + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; ce(HIGH); } @@ -702,7 +681,8 @@ void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){ // Send the payload //write_payload( buf, len ); - write_payload( buf, len,multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ) ; + write_payload( buf, len,multicast? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + ce(HIGH); ce(LOW); @@ -836,8 +816,9 @@ void RF24::openWritingPipe(uint64_t value) write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); write_register(TX_ADDR, reinterpret_cast(&value), 5); - const uint8_t max_payload_size = 32; - write_register(RX_PW_P0,min(payload_size,max_payload_size)); + //const uint8_t max_payload_size = 32; + //write_register(RX_PW_P0,min(payload_size,max_payload_size)); + write_register(RX_PW_P0,payload_size); } /****************************************************************************/ @@ -901,15 +882,10 @@ void RF24::toggle_features(void) void RF24::enableDynamicPayloads(void) { // Enable dynamic payload throughout the system - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); - // If it didn't work, the features are not enabled - if ( ! read_register(FEATURE) ) - { - // So enable them and try again toggle_features(); write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); - } + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); @@ -930,15 +906,8 @@ void RF24::enableAckPayload(void) // enable ack payload and dynamic payload features // - write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); - - // If it didn't work, the features are not enabled - if ( ! read_register(FEATURE) ) - { - // So enable them and try again toggle_features(); write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); - } IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); @@ -955,15 +924,8 @@ void RF24::enableDynamicAck(void){ // // enable dynamic ack features // - - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); - - // If it didn't work, the features are not enabled - if ( ! read_register(FEATURE) & _BV(EN_DYN_ACK) ){ - // So enable them and try again toggle_features(); write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); - } IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); @@ -976,8 +938,7 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) { const uint8_t* current = reinterpret_cast(buf); - const uint8_t max_payload_size = 32; - uint8_t data_len = min(len,max_payload_size); + uint8_t data_len = min(len,32); #if defined (__arm__) SPI.transfer(csn_pin, W_ACK_PAYLOAD | ( pipe & B111 ), SPI_CONTINUE); @@ -1002,10 +963,7 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) bool RF24::isAckPayloadAvailable(void) { - if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ - return 1; - } - return 0; + return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); } /****************************************************************************/ diff --git a/RF24.h b/RF24.h index e2e0b77..b1f8a37 100644 --- a/RF24.h +++ b/RF24.h @@ -50,9 +50,7 @@ private: bool wide_band; /* 2Mbs data rate in use? */ bool p_variant; /* False for RF24L01 and true for RF24L01P */ uint8_t payload_size; /**< Fixed size of payloads */ - bool ack_payload_available; /**< Whether there is an ack payload waiting */ bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ - uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ public: @@ -907,6 +905,8 @@ private: * are enabled. See the datasheet for details. */ void toggle_features(void); + + uint8_t spiTrans(uint8_t cmd); /**@}*/ }; @@ -1040,7 +1040,7 @@ private: */ /** - * @example scanner.pde + * @example scanner.ino * * Example to detect interference on the various channels available. * This is a good diagnostic tool to check whether you're picking a diff --git a/examples/GettingStarted/GettingStarted.ino b/examples/GettingStarted/GettingStarted.ino index 8b0353c..ff871be 100644 --- a/examples/GettingStarted/GettingStarted.ino +++ b/examples/GettingStarted/GettingStarted.ino @@ -61,12 +61,11 @@ void loop(void){ radio.stopListening(); // First, stop listening so we can talk. - unsigned long time; - printf("Now sending ",time); + + printf("Now sending \n\r"); - time = micros(); // Take the time, and send it. This will block until complete - boolean ok = radio.write( &time, sizeof(unsigned long) ); // Send the current time - if (!ok){ printf("failed.\n\r"); } + unsigned long time = micros(); // Take the time, and send it. This will block until complete + if (!radio.write( &time, sizeof(unsigned long) )){ printf("failed.\n\r"); } radio.startListening(); // Now, continue listening diff --git a/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino b/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino index 5f09ad2..8b2d0d5 100644 --- a/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino +++ b/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino @@ -1,7 +1,7 @@ /* - // March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork - // Parts derived from examples by J. Coliz - + March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork + Parts derived from examples by J. Coliz +*/ /** * Example for efficient call-response using ack-payloads * diff --git a/examples/Transfer/printf.h b/examples/Transfer/printf.h index b2efd56..b40dd38 100644 --- a/examples/Transfer/printf.h +++ b/examples/Transfer/printf.h @@ -1,11 +1,11 @@ /* Copyright (C) 2011 J. Coliz - + 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. */ - + /** * @file printf.h * @@ -18,12 +18,12 @@ #ifdef ARDUINO -int serial_putc( char c, FILE * ) +int serial_putc( char c, FILE * ) { Serial.write( c ); return c; -} +} void printf_begin(void) { diff --git a/examples/pingpair_ack/pingpair_ack.ino b/examples/pingpair_ack/pingpair_ack.ino index 2d3ecf7..4cd3fe3 100644 --- a/examples/pingpair_ack/pingpair_ack.ino +++ b/examples/pingpair_ack/pingpair_ack.ino @@ -1,7 +1,7 @@ /* // March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork // Parts derived from examples by J. Coliz - +*/ /** * Example for efficient call-response using ack-payloads * diff --git a/examples/pingpair_dyn/pingpair_dyn.ino b/examples/pingpair_dyn/pingpair_dyn.ino index 16fc01b..e1b1319 100644 --- a/examples/pingpair_dyn/pingpair_dyn.ino +++ b/examples/pingpair_dyn/pingpair_dyn.ino @@ -15,7 +15,7 @@ #include #include "nRF24L01.h" #include "RF24.h" -//#include "printf.h" +#include "printf.h" // // Hardware configuration @@ -23,7 +23,7 @@ // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(9,10); +RF24 radio(7,8); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter @@ -74,7 +74,7 @@ void setup(void) // set up the role pin pinMode(role_pin, INPUT); - digitalWrite(role_pin,LOW); + digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin // read the address pin, establish our role @@ -88,7 +88,7 @@ void setup(void) // Serial.begin(57600); - //printf_begin(); + printf_begin(); printf("\n\rRF24/examples/pingpair_dyn/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); diff --git a/examples/pingpair_irq/pingpair_irq.ino b/examples/pingpair_irq/pingpair_irq.ino index de00b62..fb3f429 100644 --- a/examples/pingpair_irq/pingpair_irq.ino +++ b/examples/pingpair_irq/pingpair_irq.ino @@ -67,13 +67,15 @@ void setup(){ radio.enableAckPayload(); // We will be using the Ack Payload feature, so please enable it // Open pipes to other node for communication if ( role == role_sender ) { // This simple sketch opens a pipe on a single address for these two nodes to - radio.openWritingPipe(address[0]); // communicate back and forth. One listens on it, the other talks to it. + radio.openWritingPipe(address[0]); // communicate back and forth. One listens on it, the other talks to it. + radio.openReadingPipe(1,address[1]); }else{ + radio.openWritingPipe(address[1]); radio.openReadingPipe(1,address[0]); radio.startListening(); } radio.printDetails(); // Dump the configuration of the rf unit for debugging - radio.powerUp(); // Power up the radio + delay(50); attachInterrupt(0, check_radio, FALLING); // Attach interrupt handler to interrupt #0 (using pin 2) on BOTH the sender and receiver } @@ -87,20 +89,12 @@ void loop() { if (role == role_sender) { // Sender role. Repeatedly send the current time unsigned long time = millis(); // Take the time, and send it. printf("Now sending %lu\n\r",time); - radio.startWrite( &time, sizeof(unsigned long) ); + radio.startWrite( &time, sizeof(unsigned long) ,0); delay(2000); // Try again soon } - if(role == role_receiver){ // Receiver does nothing but sleep between payloads - if(!radio.available() && gotMsg == 1 ){ // Display the uptime in millis(). (Counter does not increment during sleep) - gotMsg = 0; - set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Put the Arduino to sleep between payloads - sleep_enable(); - sleep_mode(); // Arduino goes to sleep here - //ZZZ... sleeping - sleep_disable(); // This happens when the interrupt wakes Arduino up - } + if(role == role_receiver){ // Receiver does nothing except in IRQ } } @@ -141,4 +135,3 @@ void check_radio(void) // Receiver role: Does not } } } - diff --git a/examples/pingpair_sleepy/pingpair_sleepy.ino b/examples/pingpair_sleepy/pingpair_sleepy.ino index ebc024e..8a8bc4e 100644 --- a/examples/pingpair_sleepy/pingpair_sleepy.ino +++ b/examples/pingpair_sleepy/pingpair_sleepy.ino @@ -71,7 +71,7 @@ void setup(){ // set up the role pin pinMode(role_pin, INPUT); - digitalWrite(role_pin,LOW); + digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin // read the address pin, establish our role @@ -113,7 +113,7 @@ void setup(){ radio.startListening(); // Dump the configuration of the rf unit for debugging - radio.printDetails(); + //radio.printDetails(); } void loop(){ @@ -123,8 +123,9 @@ void loop(){ radio.powerUp(); // Power up the radio after sleeping radio.stopListening(); // First, stop listening so we can talk. - printf("Now sending... "); - unsigned long time = millis(); // Take the time, and send it. + unsigned long time = millis(); // Take the time, and send it. + printf("Now sending... %lu \n\r",time); + radio.write( &time, sizeof(unsigned long) ); radio.startListening(); // Now, continue listening @@ -177,7 +178,7 @@ void loop(){ radio.startListening(); // Now, resume listening so we catch the next packets. }else{ Serial.println("Sleeping"); - delay(200); // Delay so the serial data can print out + delay(50); // Delay so the serial data can print out do_sleep(); } @@ -199,7 +200,6 @@ void setup_watchdog(uint8_t prescalar){ uint8_t wdtcsr = prescalar & 7; if ( prescalar & 8 ) wdtcsr |= _BV(WDP3); - Serial.println(wdtcsr); MCUSR &= ~_BV(WDRF); // Clear the WD System Reset Flag WDTCSR = _BV(WDCE) | _BV(WDE); // Write the WD Change enable bit to enable changing the prescaler and enable system reset WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); // Write the prescalar bits (how long to sleep, enable the interrupt to wake the MCU @@ -223,4 +223,3 @@ void do_sleep(void) detachInterrupt(0); WDTCSR &= ~_BV(WDIE); } - diff --git a/examples/scanner/scanner.pde b/examples/scanner/scanner.ino similarity index 94% rename from examples/scanner/scanner.pde rename to examples/scanner/scanner.ino index 1a43d72..e4aca24 100644 --- a/examples/scanner/scanner.pde +++ b/examples/scanner/scanner.ino @@ -102,12 +102,14 @@ void loop(void) // Listen for a little radio.startListening(); - delayMicroseconds(128); - radio.stopListening(); + delayMicroseconds(225); + // Did we get a carrier? - if ( radio.testCarrier() ) + if ( radio.testCarrier() ){ ++values[i]; + } + radio.stopListening(); } } @@ -121,4 +123,4 @@ void loop(void) printf("\n\r"); } -// vim:ai:cin:sts=2 sw=2 ft=cpp +// vim:ai:cin:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/tests/pingpair_blocking/pingpair_blocking.pde b/tests/pingpair_blocking/pingpair_blocking.pde index 1501d37..004b421 100644 --- a/tests/pingpair_blocking/pingpair_blocking.pde +++ b/tests/pingpair_blocking/pingpair_blocking.pde @@ -27,11 +27,11 @@ public: RF24Test(int a, int b): RF24(a,b) {} // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24Test radio(8,9); +RF24Test radio(48,49); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter -const int role_pin = 7; +const int role_pin = 5; // // Topology @@ -230,28 +230,28 @@ void loop(void) // Dump the payloads until we've gotten everything unsigned long got_time; bool done = false; - while (!done) + while (radio.available()) { // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - + radio.read( &got_time, sizeof(unsigned long) ); + } // Delay just a little bit to let the other unit // make the transition to receiver - delay(20); - } + //delay(20); + //} // First, stop listening so we can talk radio.stopListening(); + + // Spew it + printf("Got payload %lu...",got_time); // Send the final one back. radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); // Now, resume listening so we catch the next packets. radio.startListening(); + printf("Sent response.\n\r"); } } @@ -270,4 +270,4 @@ void loop(void) printf("FAIL\n\r\n\r"); } } -// vim:cin:ai:sts=2 sw=2 ft=cpp +// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/tests/pingpair_test/pingpair_test.pde b/tests/pingpair_test/pingpair_test.pde index 6acbf51..7685ca8 100644 --- a/tests/pingpair_test/pingpair_test.pde +++ b/tests/pingpair_test/pingpair_test.pde @@ -29,11 +29,11 @@ // Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); +RF24 radio(7,8); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'ping' transmitter -const short role_pin = 7; +const short role_pin = 5; // // Topology @@ -254,7 +254,7 @@ void setup(void) // attachInterrupt(0, check_radio, FALLING); - + delay(50); if ( role == role_receiver ) printf("\n\r+OK "); } @@ -294,7 +294,7 @@ void loop(void) // Send it. This will block until complete printf("\n\rNow sending length %i...",next_payload_size); - radio.startWrite( send_payload, next_payload_size ); + radio.startWrite( send_payload, next_payload_size,0 ); // Update size for next time. next_payload_size += payload_size_increments_by; @@ -374,10 +374,9 @@ void check_radio(void) prbuf_in += sprintf(prbuf_in,"Ack Payload:Failed\n\r"); } - // Transmitter can power down for now, because - // the transmission is done. - if ( ( tx || fail ) && ( role == role_sender ) ) - radio.powerDown(); + // Not powering down since radio is in standby mode + //if ( ( tx || fail ) && ( role == role_sender ) ) + //radio.powerDown(); // Did we receive a message? if ( rx ) @@ -387,7 +386,7 @@ void check_radio(void) { radio.read(&message_count,sizeof(message_count)); prbuf_in += sprintf(prbuf_in,"Ack:%lu ",message_count); - + // is this ack what we were expecting? to account // for failures, we simply want to make sure we get a // DIFFERENT ack every time. @@ -411,10 +410,11 @@ void check_radio(void) size_t len = max_payload_size; memset(receive_payload,0,max_payload_size); - if ( configuration == '3' ) + if ( configuration == '3' ){ len = next_payload_size; - else + }else{ len = radio.getDynamicPayloadSize(); + } radio.read( receive_payload, len ); @@ -427,9 +427,9 @@ void check_radio(void) // Add an ack packet for the next time around. // Here we will report back how many bytes we got this time. radio.writeAckPayload( pipe_number, &len, sizeof(len) ); + ++message_count; } + } } - -// vim:ai:cin:sts=2 sw=2 ft=cpp From c965f73838383f7d11d15670ddc371f98b5be8b3 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 22 Apr 2014 10:15:55 -0600 Subject: [PATCH 226/326] FIFO rd change (Thanks Zephyrr), Initial SI24R1 support - Using status register is faster to read TX FIFO status - Zephyrr (GitHub) - Fixed typo on FLUSH_RX cmd - Added support for pwr levels on SI24R1 chips with smaller code --- RF24.cpp | 64 ++++++++++---------------------------------------------- RF24.h | 35 ++++++++++++++++++------------- 2 files changed, 32 insertions(+), 67 deletions(-) diff --git a/RF24.cpp b/RF24.cpp index bb16c95..831ecdf 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -235,8 +235,7 @@ uint8_t RF24::read_payload(void* buf, uint8_t data_len) uint8_t RF24::flush_rx(void) { - - return spiTrans( FLUSH_TX ); + return spiTrans( FLUSH_RX ); } /****************************************************************************/ @@ -605,7 +604,7 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) uint32_t timer = millis(); //Get the time that the payload transmission started - while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag @@ -638,7 +637,7 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) //Return 0 so the user can control the retrys and set a timer or failure counter if required //The radio will auto-clear everything in the FIFO as long as CE remains high - while ( (read_register(FIFO_STATUS) & _BV(FIFO_FULL))){ //Blocking only if FIFO is full. This will loop and block until TX is successful + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail if( get_status() & _BV(MAX_RT)){ //reUseTX(); //Set re-transmit @@ -1018,63 +1017,22 @@ bool RF24::testRPD(void) /****************************************************************************/ -void RF24::setPALevel(rf24_pa_dbm_e level) +void RF24::setPALevel(uint8_t level) { - uint8_t setup = read_register(RF_SETUP) ; - setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - - // switch uses RAM (evil!) - if ( level == RF24_PA_MAX ) - { - setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; + if(level > 3){ // If invalid level, go to max PA + level = RF24_PA_MAX << 1 + 1; // +1 to support the SI24R1 chip extra bit + }else{ + level = (level << 1) + 1; // Else set level as requested } - else if ( level == RF24_PA_HIGH ) - { - setup |= _BV(RF_PWR_HIGH) ; - } - else if ( level == RF24_PA_LOW ) - { - setup |= _BV(RF_PWR_LOW); - } - else if ( level == RF24_PA_MIN ) - { - // nothing - } - else if ( level == RF24_PA_ERROR ) - { - // On error, go to maximum PA - setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - } - - write_register( RF_SETUP, setup ) ; + write_register( RF_SETUP, level ) ; // Write it to the chip } /****************************************************************************/ -rf24_pa_dbm_e RF24::getPALevel(void) +uint8_t RF24::getPALevel(void) { - rf24_pa_dbm_e result = RF24_PA_ERROR ; - uint8_t power = read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - // switch uses RAM (evil!) - if ( power == (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ) - { - result = RF24_PA_MAX ; - } - else if ( power == _BV(RF_PWR_HIGH) ) - { - result = RF24_PA_HIGH ; - } - else if ( power == _BV(RF_PWR_LOW) ) - { - result = RF24_PA_LOW ; - } - else - { - result = RF24_PA_MIN ; - } - - return result ; + return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; } /****************************************************************************/ diff --git a/RF24.h b/RF24.h index b1f8a37..1571113 100644 --- a/RF24.h +++ b/RF24.h @@ -691,25 +691,27 @@ public: void setAutoAck( uint8_t pipe, bool enable ) ; /** - * Set Power Amplifier (PA) level to one of four levels. - * Relative mnemonics have been used to allow for future PA level - * changes. According to 6.5 of the nRF24L01+ specification sheet, - * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, - * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. + * Set Power Amplifier (PA) level to one of four levels: + * RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX + * + * The power levels correspond to the following output levels respectively: + * NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm + * + * SI24R1: -6dBm, 0dBm, 3dBM, and 7dBm. * * @param level Desired PA level. */ - void setPALevel( rf24_pa_dbm_e level ) ; + void setPALevel ( uint8_t level ); /** * Fetches the current PA level. * - * @return Returns a value from the rf24_pa_dbm_e enum describing - * the current PA setting. Please remember, all values represented - * by the enum mnemonics are negative dBm. See setPALevel for - * return value descriptions. + * NRF24L01: -18dBm, -12dBm, -6dBm and 0dBm + * SI24R1: -6dBm, 0dBm, 3dBm, 7dBm + * + * @return Returns values 0 to 3 representing the PA Level. */ - rf24_pa_dbm_e getPALevel( void ) ; + uint8_t getPALevel( void ); /** * Set the transmission data rate @@ -906,6 +908,10 @@ private: */ void toggle_features(void); + /** + * Built in spi transfer function to simplify repeating code repeating code + */ + uint8_t spiTrans(uint8_t cmd); /**@}*/ @@ -1089,7 +1095,7 @@ private: * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or * the SPI hardware will go into 'slave' mode. * - * @section BoardSupport Board Support + * @section Board_Support Board Support * * Most standard Arduino based boards are supported: * - ATMega 328 based boards (Uno, Nano, etc) @@ -1098,8 +1104,9 @@ private: * hardware SS/CSN pins as extended SPI methods are used. * Initial Due support taken from https://github.com/mcrosson/RF24/tree/due * - ATTiny board support added from https://github.com/jscrane/RF24 - * Note: ATTiny support is built into the library. Do not include SPI.h. - * See here for wiring/pin info. + * Note: ATTiny support is built into the library. Do not include SPI.h.
+ * ATTiny 85: D0(pin 5): MISO, D1(pin6) MOSI, D2(pin7) SCK, D3(pin2):CSN/SS, D4(pin3): CE
+ * ATTiny 84: PA6:MISO, PA5:MOSI, PA4:SCK, PA7:CSN/SS, CE as desired
* * @section More More Information * From 8ff9fb6f155d263adc5671e7b1c28f6265a73f15 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Wed, 23 Apr 2014 13:27:46 -0600 Subject: [PATCH 227/326] Small fix before pushing last commit - fixed mistake with changes to setPALevel prior to pushing --- RF24.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/RF24.cpp b/RF24.cpp index 831ecdf..ebfbf81 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -1019,12 +1019,17 @@ bool RF24::testRPD(void) void RF24::setPALevel(uint8_t level) { + + uint8_t setup = read_register(RF_SETUP) & 0b11111000; + if(level > 3){ // If invalid level, go to max PA level = RF24_PA_MAX << 1 + 1; // +1 to support the SI24R1 chip extra bit }else{ level = (level << 1) + 1; // Else set level as requested } - write_register( RF_SETUP, level ) ; // Write it to the chip + + + write_register( RF_SETUP, setup |= level ) ; // Write it to the chip } /****************************************************************************/ From 2406e06a777d1ff0e2feb4c0b62b42f27ee21a59 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 24 Apr 2014 14:47:39 -0600 Subject: [PATCH 228/326] Raspberry Pi support Added support for hardware SPI on RPI per jscrane fork: - Fixed a few bugs with single SPI transfers - Aligned with optimized RF24 library fork - Updated bmc2835 files per http://www.airspayce.com/mikem/bcm2835/index.html - Merged bcm files with changes from https://github.com/farconada/RF24Network - Added example files - Added setup info to readme --- RPi/RF24/Makefile | 55 + RPi/RF24/RF24.cpp | 1132 +++++++++++++ RPi/RF24/RF24.h | 1041 ++++++++++++ RPi/RF24/RF24_config.h | 34 + RPi/RF24/bcm2835.c | 1451 +++++++++++++++++ RPi/RF24/bcm2835.h | 1353 +++++++++++++++ RPi/RF24/examples/Makefile | 40 + RPi/RF24/examples/extra/Makefile | 40 + RPi/RF24/examples/extra/rpi-hub.cpp | 134 ++ RPi/RF24/examples/extra/scanner.cpp | 147 ++ RPi/RF24/examples/gettingstarted.cpp | 188 +++ .../examples/gettingstarted_call_response.cpp | 138 ++ RPi/RF24/examples/pingpair_dyn.cpp | 181 ++ RPi/RF24/examples/transfer.cpp | 148 ++ RPi/RF24/nRF24L01.h | 127 ++ RPi/RF24/readme.md | 108 ++ RPi/readme.md | 108 ++ 17 files changed, 6425 insertions(+) create mode 100644 RPi/RF24/Makefile create mode 100644 RPi/RF24/RF24.cpp create mode 100644 RPi/RF24/RF24.h create mode 100644 RPi/RF24/RF24_config.h create mode 100644 RPi/RF24/bcm2835.c create mode 100644 RPi/RF24/bcm2835.h create mode 100644 RPi/RF24/examples/Makefile create mode 100644 RPi/RF24/examples/extra/Makefile create mode 100644 RPi/RF24/examples/extra/rpi-hub.cpp create mode 100644 RPi/RF24/examples/extra/scanner.cpp create mode 100644 RPi/RF24/examples/gettingstarted.cpp create mode 100644 RPi/RF24/examples/gettingstarted_call_response.cpp create mode 100644 RPi/RF24/examples/pingpair_dyn.cpp create mode 100644 RPi/RF24/examples/transfer.cpp create mode 100644 RPi/RF24/nRF24L01.h create mode 100644 RPi/RF24/readme.md create mode 100644 RPi/readme.md diff --git a/RPi/RF24/Makefile b/RPi/RF24/Makefile new file mode 100644 index 0000000..c2ae37d --- /dev/null +++ b/RPi/RF24/Makefile @@ -0,0 +1,55 @@ +############################################################################# +# +# Makefile for librf24-bcm on Raspberry Pi +# +# License: GPL (General Public License) +# Author: Charles-Henri Hallard +# Date: 2013/03/13 +# +# Description: +# ------------ +# use make all and mak install to install the library +# You can change the install directory by editing the LIBDIR line +# +PREFIX=/usr/local + +# Library parameters +# where to put the lib +LIBDIR=$(PREFIX)/lib +# lib name +LIB=librf24-bcm +# shared library name +LIBNAME=$(LIB).so.1.0 + + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s + +# make all +# reinstall the library after each recompilation +all: librf24-bcm install + +# Make the library +librf24-bcm: RF24.o bcm2835.o + g++ -shared -Wl,-soname,$@.so.1 ${CCFLAGS} -o ${LIBNAME} $^ + +# Library parts +RF24.o: RF24.cpp + g++ -Wall -fPIC ${CCFLAGS} -c $^ + +bcm2835.o: bcm2835.c + gcc -Wall -fPIC ${CCFLAGS} -c $^ + +# clear build files +clean: + rm -rf *.o ${LIB}.* + +# Install the library to LIBPATH +install: + @echo "[Install]" + @if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi + #@install -m 0755 ${LIB}.a ${LIBDIR} + @install -m 0755 ${LIBNAME} ${LIBDIR} + @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so.1 + @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so + @ldconfig diff --git a/RPi/RF24/RF24.cpp b/RPi/RF24/RF24.cpp new file mode 100644 index 0000000..303fde0 --- /dev/null +++ b/RPi/RF24/RF24.cpp @@ -0,0 +1,1132 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + + +03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 library + +TMRh20 2014: Updated to work with optimized RF24 and RF24 Network Arduino libs. +*/ + +#include "./RF24_config.h" +#include "./RF24.h" +#include "./nRF24L01.h" + + +/****************************************************************************/ +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size = len + 1; // Add register value to transmit buffer + + *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); + while (len--){ + *ptx++ = NOP ; // Dummy operation, just for reading + } + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + //bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) buf, size); + + status = *prx++; // status is 1st byte of receive buffer + + // decrement before to skip status byte + while ( --size ){ + *buf++ = *prx++; + } + + return status; +} + +/****************************************************************************/ +uint8_t RF24::read_register(uint8_t reg) +{ + uint8_t result; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + + *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); + *ptx++ = NOP ; // Dummy operation, just for reading + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + result = *++prx; // result is 2nd byte of receive buffer + + return result; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, uint8_t value) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + + *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); + *ptx = value ; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + status = *prx++; // status is 1st byte of receive buffer + + if (debug) + printf("write_register(%02x,%02x)\r\n",reg,value); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size = len + 1; // Add register value to transmit buffer + + *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + *ptx++ = *buf++; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + status = *prx; // status is 1st byte of receive buffer + + return status; +} + + +/****************************************************************************/ + +uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeType) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + size = data_len + blank_len + 1 ; // Add register value to transmit buffer + + if (debug) + printf("[Writing %u bytes %u blanks]",data_len,blank_len); + + *ptx++ = W_TX_PAYLOAD; + while ( data_len-- ) + *ptx++ = *current++; + while ( blank_len-- ) + *ptx++ = 0; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + status = *prx; // status is 1st byte of receive buffer + + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::read_payload(void* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + size = data_len + blank_len + 1; // Add register value to transmit buffer + + if (debug) + printf("[Reading %u bytes %u blanks]",data_len,blank_len); + + *ptx++ = R_RX_PAYLOAD; + while(size--) + *ptx++ = NOP; + + // Size has been lost during while, re affect + size = data_len + blank_len + 1; // Add register value to transmit buffer + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + // 1st byte is status + status = *prx++; + + // Decrement before to skip 1st status byte + while ( --size ) + *current++ = *prx++; + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_rx(void) +{ + uint8_t status; + + status = bcm2835_spi_transfer( FLUSH_RX ); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_tx(void) +{ + uint8_t status; + + status = bcm2835_spi_transfer( FLUSH_TX ); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::get_status(void) +{ + return bcm2835_spi_transfer( NOP ); +} + +/****************************************************************************/ + +void RF24::print_status(uint8_t status) +{ + printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n", + status, + (status & _BV(RX_DR))?1:0, + (status & _BV(TX_DS))?1:0, + (status & _BV(MAX_RT))?1:0, + ((status >> RX_P_NO) & 0b111), + (status & _BV(TX_FULL))?1:0 + ); +} + +/****************************************************************************/ + +void RF24::print_observe_tx(uint8_t value) +{ + printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n", + value, + (value >> PLOS_CNT) & 0b1111, + (value >> ARC_CNT) & 0b1111 + ); +} + +/****************************************************************************/ + +void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen(name) < 8 ? '\t' : 0; + printf("%s\t%c =", name, extra_tab); + while (qty--) + printf(" 0x%02x",read_register(reg++)); + printf("\n"); +} + +/****************************************************************************/ + +void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen(name) < 8 ? '\t' : 0; + printf("%s\t%c =",name,extra_tab); + + while (qty--) + { + uint8_t buffer[5]; + read_register(reg++,buffer,sizeof buffer); + + printf(" 0x"); + uint8_t* bufptr = buffer + sizeof buffer; + while( --bufptr >= buffer ) + printf("%02x",*bufptr); + } + + printf("\r\n"); +} + + +/****************************************************************************/ + +RF24::RF24(uint8_t _cepin, uint8_t _cspin, uint32_t _spi_speed): + ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), wide_band(true), p_variant(false), + payload_size(32), dynamic_payloads_enabled(false), + pipe0_reading_address(0) +{ +} + +/****************************************************************************/ + +void RF24::setChannel(uint8_t channel) +{ + // TODO: This method could take advantage of the 'wide_band' calculation + // done in setChannel() to require certain channel spacing. + + const uint8_t max_channel = 127; + write_register(RF_CH,min(channel,max_channel)); +} + +/****************************************************************************/ + +void RF24::setPayloadSize(uint8_t size) +{ + const uint8_t max_payload_size = 32; + payload_size = min(size,max_payload_size); +} + +/****************************************************************************/ + +uint8_t RF24::getPayloadSize(void) +{ + return payload_size; +} + +/****************************************************************************/ + +static const char rf24_datarate_e_str_0[] = "1MBPS"; +static const char rf24_datarate_e_str_1[] = "2MBPS"; +static const char rf24_datarate_e_str_2[] = "250KBPS"; +static const char * const rf24_datarate_e_str_P[] = { + rf24_datarate_e_str_0, + rf24_datarate_e_str_1, + rf24_datarate_e_str_2, +}; +static const char rf24_model_e_str_0[] = "nRF24L01"; +static const char rf24_model_e_str_1[] = "nRF24L01+"; +static const char * const rf24_model_e_str_P[] = { + rf24_model_e_str_0, + rf24_model_e_str_1, +}; +static const char rf24_crclength_e_str_0[] = "Disabled"; +static const char rf24_crclength_e_str_1[] = "8 bits"; +static const char rf24_crclength_e_str_2[] = "16 bits" ; +static const char * const rf24_crclength_e_str_P[] = { + rf24_crclength_e_str_0, + rf24_crclength_e_str_1, + rf24_crclength_e_str_2, +}; +static const char rf24_pa_dbm_e_str_0[] = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_3[] = "PA_MAX"; +static const char * const rf24_pa_dbm_e_str_P[] = { + rf24_pa_dbm_e_str_0, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + +static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)"; +static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)"; +static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)"; +static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven"; +static const char * const rf24_csn_e_str_P[] = { + rf24_csn_e_str_0, + rf24_csn_e_str_1, + rf24_csn_e_str_2, + rf24_csn_e_str_3, +}; + +// Display NRF24L01 details +void RF24::printDetails(void) +{ + printf("================ SPI Configuration ================\n" ); + + if (csn_pin < BCM2835_SPI_CS_NONE ) + { + printf("CSN Pin \t = %s\n",rf24_csn_e_str_P[csn_pin]); + } + else + { + printf("CSN Pin \t = Custom GPIO%d%s\n", csn_pin, + csn_pin==RPI_V2_GPIO_P1_26 ? " (CE1) Software Driven" : "" ); + } + + printf("CE Pin \t = Custom GPIO%d\n", ce_pin ); + + // SPI Bus Speed + printf("Clock Speed\t = " ); + switch (spi_speed) + { + case BCM2835_SPI_SPEED_64MHZ : printf("64 Mhz"); break ; + case BCM2835_SPI_SPEED_32MHZ : printf("32 Mhz"); break ; + case BCM2835_SPI_SPEED_16MHZ : printf("16 Mhz"); break ; + case BCM2835_SPI_SPEED_8MHZ : printf("8 Mhz"); break ; + case BCM2835_SPI_SPEED_4MHZ : printf("4 Mhz"); break ; + case BCM2835_SPI_SPEED_2MHZ : printf("2 Mhz"); break ; + case BCM2835_SPI_SPEED_1MHZ : printf("1 Mhz"); break ; + case BCM2835_SPI_SPEED_512KHZ: printf("512 KHz"); break ; + case BCM2835_SPI_SPEED_256KHZ: printf("256 KHz"); break ; + case BCM2835_SPI_SPEED_128KHZ: printf("128 KHz"); break ; + case BCM2835_SPI_SPEED_64KHZ : printf("64 KHz"); break ; + case BCM2835_SPI_SPEED_32KHZ : printf("32 KHz"); break ; + case BCM2835_SPI_SPEED_16KHZ : printf("16 KHz"); break ; + case BCM2835_SPI_SPEED_8KHZ : printf("8 KHz"); break ; + default : printf("Probably Bad !!!"); break ; + } + printf("\n"); + + printf("================ NRF Configuration ================\n" ); + + print_status(get_status()); + + print_address_register("RX_ADDR_P0-1",RX_ADDR_P0,2); + print_byte_register("RX_ADDR_P2-5",RX_ADDR_P2,4); + print_address_register("TX_ADDR",TX_ADDR); + + print_byte_register("RX_PW_P0-6",RX_PW_P0,6); + print_byte_register("EN_AA",EN_AA); + print_byte_register("EN_RXADDR",EN_RXADDR); + print_byte_register("RF_CH",RF_CH); + print_byte_register("RF_SETUP",RF_SETUP); + print_byte_register("CONFIG",CONFIG); + print_byte_register("DYNPD/FEATURE",DYNPD,2); + + printf("Data Rate\t = %s\r\n",rf24_datarate_e_str_P[getDataRate()]); + printf("Model\t\t = %s\r\n",rf24_model_e_str_P[isPVariant()]); + printf("CRC Length\t = %s\r\n",rf24_crclength_e_str_P[getCRCLength()]); + printf("PA Power\t = %s\r\n",rf24_pa_dbm_e_str_P[getPALevel()]); +} + +/****************************************************************************/ + +bool RF24::begin(void) +{ + debug = false; + //debug = true; + + // Init BCM2835 chipset for talking with us + if (!bcm2835_init()){ + return false; + } + // Initialise the CE pin of NRF24 (chip enable) + bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_write(ce_pin, LOW); + + // used to drive custom I/O to trigger my logic analyser + // bcm2835_gpio_fsel(GPIO_CTRL_PIN , BCM2835_GPIO_FSEL_OUTP); + + // start the SPI library: + // Note the NRF24 wants mode 0, MSB first and default to 1 Mbps + bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); + bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); + + // Set SPI bus Speed + bcm2835_spi_setClockDivider(spi_speed); + + if(csn_pin <= BCM2835_SPI_CS_NONE){ + bcm2835_spi_chipSelect(csn_pin); + } + // This initialize the SPI bus with + // csn pin as chip select (custom or not) + bcm2835_spi_begin(csn_pin); + + // wait 100ms + delay(100); + + // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier + // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet + // sizes must never be used. See documentation for a more complete explanation. + //printf("write_register(%02X, %02X)\n", SETUP_RETR, (0b0100 << ARD) | (0b1111 << ARC)); + setRetries(5,15); + + // Determine if this is a p or non-p RF24 module and then + // reset our data rate back to default value. This works + // because a non-P variant won't allow the data rate to + // be set to 250Kbps. + if( setDataRate( RF24_250KBPS ) ) + { + p_variant = true ; + } + + // Then set the data rate to the slowest (and most reliable) speed supported by all + // hardware. + setDataRate( RF24_1MBPS ) ; + + // Initialize CRC and request 2-byte (16bit) CRC + setCRCLength( RF24_CRC_16 ) ; + + // Reset current status + // Notice reset and flush is the last thing we do + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Set up default configuration. Callers can always change it later. + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); + + // Flush buffers + flush_rx(); + flush_tx(); + + powerUp(); + + // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + // PTX should use only 22uA of power + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + + return true; +} + +/****************************************************************************/ + +void RF24::startListening(void) +{ + powerUp(); + write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address) + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + + // Flush buffers + flush_rx(); + flush_tx(); + + // Go! + bcm2835_gpio_write(ce_pin, HIGH); + + // wait for the radio to come up (130us actually only needed) + //delayMicroseconds(130); +} + +/****************************************************************************/ + +void RF24::stopListening(void) +{ + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); + flush_rx(); + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + +} + +/****************************************************************************/ + +void RF24::powerDown(void) +{ + bcm2835_gpio_write(ce_pin, LOW); + write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); +} + +/****************************************************************************/ + +void RF24::powerUp(void) +{ + bool up = read_register(CONFIG) & _BV(PWR_UP); + + if(! up ){ + write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) )); + delay(5); + } + + +} + +/******************************************************************/ + +bool RF24::write( const void* buf, uint8_t len, const bool multicast ){ + + // Begin the write + startWrite(buf,len, multicast); + + + //Wait until complete or failed + // If this hangs, it ain't coming back, no sense in timing out + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + + + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); + + //Max retries exceeded + if( status & _BV(MAX_RT)){ + flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush + return 0; + } + //TX OK 1 or 0 + return 1; +} + +bool RF24::write( const void* buf, uint8_t len ){ + return write(buf,len,0); +} + +/****************************************************************************/ + +//For general use, the interrupt flags are not important to clear +bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //This way the FIFO will fill up and allow blocking until packets go through + //The radio will auto-clear everything in the FIFO as long as CE remains high + + uint32_t timer = millis(); //Get the time that the payload transmission started + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout + + if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached + reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag + if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 + } + + } + + //Start Writing + startFastWrite(buf,len,0); //Write the payload if a buffer is clear + + return 1; //Return 1 to indicate successful transmission +} + +/****************************************************************************/ + +void RF24::reUseTX(){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + //spiTrans( REUSE_TX_PL ); + bcm2835_spi_transfer( REUSE_TX_PL); + bcm2835_gpio_write(ce_pin, LOW); //Re-Transfer packet + bcm2835_gpio_write(ce_pin, HIGH); +} + +/****************************************************************************/ + +bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //Return 0 so the user can control the retrys and set a timer or failure counter if required + //The radio will auto-clear everything in the FIFO as long as CE remains high + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail + + if( get_status() & _BV(MAX_RT)){ + //reUseTX(); //Set re-transmit + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + return 0; //Return 0. The previous payload has been retransmitted + //From the user perspective, if you get a 0, just keep trying to send the same payload + } + + } + //Start Writing + startFastWrite(buf,len,multicast); + + return 1; +} + +bool RF24::writeFast( const void* buf, uint8_t len ){ + return writeFast(buf,len,0); +} + +/****************************************************************************/ + +//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high +//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) +//Otherwise we enter Standby-II mode, which is still faster than standby mode +//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data + +void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 + + //write_payload( buf,len); + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + bcm2835_gpio_write(ce_pin, HIGH); + +} + +/****************************************************************************/ + +void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ) +{ + + // Send the payload + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + + bcm2835_gpio_write(ce_pin, HIGH); + //bcm2835_gpio_write(ce_pin, LOW); + +} + +/****************************************************************************/ + +bool RF24::txStandBy(){ + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); //Non blocking, flush the data + return 0; + } + } + + bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode + return 1; +} + +/****************************************************************************/ + +bool RF24::txStandBy(uint32_t timeout){ + + uint32_t start = millis(); + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); //Set re-transmit + bcm2835_gpio_write(ce_pin, HIGH); + if(millis() - start >= timeout){ + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); + return 0; + } + } + } + bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode + return 1; + +} +/****************************************************************************/ + +void RF24::maskIRQ(bool tx, bool fail, bool rx){ + + write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); +} + +/****************************************************************************/ + +uint8_t RF24::getDynamicPayloadSize(void) +{ + spi_txbuff[0] = R_RX_PL_WID; + spi_rxbuff[1] = 0xff; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + if(spi_rxbuff[1] > 32) { flush_rx(); return 0; } + + return spi_rxbuff[1]; +} + +/****************************************************************************/ + +bool RF24::available(void) +{ + return available(NULL); +} + +/****************************************************************************/ + +bool RF24::available(uint8_t* pipe_num) +{ + //Check the FIFO buffer to see if data is waitng to be read + + if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ + + + // If the caller wants the pipe number, include that + if ( pipe_num ){ + uint8_t status = get_status(); + *pipe_num = ( status >> RX_P_NO ) & 0b111; + } + return 1; + } + + return 0; +} + +/****************************************************************************/ + +void RF24::read( void* buf, uint8_t len ) +{ + // Fetch the payload + read_payload( buf, len ); + + //Clear the two possible interrupt flags with one command + write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); +} + +/****************************************************************************/ + +void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) +{ + // Read the status & reset the status in one easy call + // Or is that such a good idea? + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Report to the user what happened + tx_ok = status & _BV(TX_DS); + tx_fail = status & _BV(MAX_RT); + rx_ready = status & _BV(RX_DR); +} + +/****************************************************************************/ + +void RF24::openWritingPipe(uint64_t value) +{ + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) + // expects it LSB first too, so we're good. + + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); + write_register(TX_ADDR, reinterpret_cast(&value), 5); + + + write_register(RX_PW_P0,payload_size); +} + +/****************************************************************************/ + +static const uint8_t child_pipe[] = +{ + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 +}; +static const uint8_t child_payload_size[] = +{ + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 +}; +static const uint8_t child_pipe_enable[] = +{ + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 +}; + +void RF24::openReadingPipe(uint8_t child, uint64_t address) +{ + // If this is pipe 0, cache the address. This is needed because + // openWritingPipe() will overwrite the pipe 0 address, so + // startListening() will have to restore it. + if (child == 0) + pipe0_reading_address = address; + + if (child <= 6) + { + // For pipes 2-5, only write the LSB + if ( child < 2 ) + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); + else + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); + + write_register(pgm_read_byte(&child_payload_size[child]),payload_size); + + // Note it would be more efficient to set all of the bits for all open + // pipes at once. However, I thought it would make the calling code + // more simple to do it this way. + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); + } +} + +/****************************************************************************/ + +void RF24::toggle_features(void) +{ + bcm2835_spi_transfer( ACTIVATE ); + bcm2835_spi_transfer( 0x73 ); +} + +/****************************************************************************/ + +void RF24::enableDynamicPayloads(void) +{ + + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); + + if (debug) + printf("FEATURE=%i\r\n",read_register(FEATURE)); + + // Enable dynamic payload on all pipes + // + // Not sure the use case of only having dynamic payload on certain + // pipes, so the library does not support it. + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + + dynamic_payloads_enabled = true; +} + +/****************************************************************************/ + +void RF24::enableAckPayload(void) +{ + // + // enable ack payload and dynamic payload features + // + + + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + + if (debug) + printf("FEATURE=%i\r\n",read_register(FEATURE)); + + // + // Enable dynamic payload on pipes 0 & 1 + // + + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/****************************************************************************/ + +void RF24::enableDynamicAck(void){ + // + // enable dynamic ack features + // + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); + + if(debug){printf("FEATURE=%i\r\n",read_register(FEATURE));} + + +} + +/****************************************************************************/ + +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + + size = data_len + 1 ; // Add register value to transmit buffer + + if (debug){ + printf("[Writing %u bytes]",data_len); + } + *ptx++ = W_ACK_PAYLOAD | ( pipe & 0b111 ); + while ( data_len-- ){ + *ptx++ = *current++; + } + bcm2835_spi_transfern( (char *) spi_txbuff, size); + +} + + +/****************************************************************************/ + +bool RF24::isAckPayloadAvailable(void) +{ + return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); +} + +/****************************************************************************/ + +bool RF24::isPVariant(void) +{ + return p_variant ; +} + +/****************************************************************************/ + +void RF24::setAutoAck(bool enable) +{ + if ( enable ) + write_register(EN_AA, 0b111111); + else + write_register(EN_AA, 0); +} + +/****************************************************************************/ + +void RF24::setAutoAck( uint8_t pipe, bool enable ) +{ + if ( pipe <= 6 ) + { + uint8_t en_aa = read_register( EN_AA ) ; + if( enable ) + { + en_aa |= _BV(pipe) ; + } + else + { + en_aa &= ~_BV(pipe) ; + } + write_register( EN_AA, en_aa ) ; + } +} + +/****************************************************************************/ + +bool RF24::testCarrier(void) +{ + return ( read_register(CD) & 1 ); +} + +/****************************************************************************/ + +bool RF24::testRPD(void) +{ + return ( read_register(RPD) & 1 ) ; +} + +/****************************************************************************/ + +void RF24::setPALevel(uint8_t level) +{ + uint8_t setup = read_register(RF_SETUP) & 0b11111000; + + if(level > 3){ // If invalid level, go to max PA + level = (RF24_PA_MAX << 1) + 1; // +1 to support the SI24R1 chip extra bit + }else{ + level = (level << 1) + 1; // Else set level as requested + } + + + write_register( RF_SETUP, setup |= level ) ; // Write it to the chip +} + +/****************************************************************************/ + +uint8_t RF24::getPALevel(void) +{ + return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; +} + +/****************************************************************************/ + +bool RF24::setDataRate(rf24_datarate_e speed) +{ + bool result = false; + uint8_t setup = read_register(RF_SETUP) ; + + // HIGH and LOW '00' is 1Mbs - our default + wide_band = false ; + setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; + if( speed == RF24_250KBPS ) + { + // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 + // Making it '10'. + wide_band = false ; + setup |= _BV( RF_DR_LOW ) ; + } + else + { + // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 + // Making it '01' + if ( speed == RF24_2MBPS ) + { + wide_band = true ; + setup |= _BV(RF_DR_HIGH); + } + else + { + // 1Mbs + wide_band = false ; + } + } + write_register(RF_SETUP,setup); + + // Verify our result + if ( read_register(RF_SETUP) == setup ) + { + result = true; + } + else + { + wide_band = false; + } + + return result; +} + +/****************************************************************************/ + +rf24_datarate_e RF24::getDataRate( void ) +{ + rf24_datarate_e result ; + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + // switch uses RAM (evil!) + // Order matters in our case below + if ( dr == _BV(RF_DR_LOW) ) + { + // '10' = 250KBPS + result = RF24_250KBPS ; + } + else if ( dr == _BV(RF_DR_HIGH) ) + { + // '01' = 2MBPS + result = RF24_2MBPS ; + } + else + { + // '00' = 1MBPS + result = RF24_1MBPS ; + } + return result ; +} + +/****************************************************************************/ + +void RF24::setCRCLength(rf24_crclength_e length) +{ + uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; + + // switch uses RAM (evil!) + if ( length == RF24_CRC_DISABLED ) + { + // Do nothing, we turned it off above. + } + else if ( length == RF24_CRC_8 ) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV( CRCO ); + } + write_register( CONFIG, config ) ; +} + +/****************************************************************************/ + +rf24_crclength_e RF24::getCRCLength(void) +{ + rf24_crclength_e result = RF24_CRC_DISABLED; + uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; + + if ( config & _BV(EN_CRC ) ) + { + if ( config & _BV(CRCO) ) + result = RF24_CRC_16; + else + result = RF24_CRC_8; + } + + return result; +} + +/****************************************************************************/ + +void RF24::disableCRC( void ) +{ + uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; + write_register( CONFIG, disable ) ; +} + +/****************************************************************************/ +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR,(delay&0xf)< + + 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. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Modified to use the great bcm2835 library for I/O and SPI + + */ + +/** + * @file RF24.h + * + * Class declaration for RF24 and helper enums + */ + +#ifndef __RF24_H__ +#define __RF24_H__ + +#include "RF24_config.h" +#include "./bcm2835.h" + + +/** + * Power Amplifier level. + * + * For use with setPALevel() + */ +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; + +/** + * Data rate. How fast data moves through the air. + * + * For use with setDataRate() + */ +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +/** + * CRC Length. How big (if any) of a CRC is included. + * + * For use with setCRCLength() + */ +typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; + +/** + * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + */ + +class RF24 +{ +private: + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + uint16_t spi_speed; /**< SPI Bus Speed */ + bool wide_band; /* 2Mbs data rate in use? */ + bool p_variant; /* False for RF24L01 and true for RF24L01P */ + uint8_t payload_size; /**< Fixed size of payloads */ + //bool ack_payload_available; /**< Whether there is an ack payload waiting */ + bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ + //uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + //uint32_t spispeed; + uint8_t debug ; /* Debug flag */ + uint8_t spi_rxbuff[32+1] ; //SPI receive buffer (payload max 32 bytes) + uint8_t spi_txbuff[32+1] ; //SPI transmit buffer (payload max 32 bytes + 1 byte for the command) + +protected: + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. + */ + /**@{*/ + + + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); + + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); + + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); + + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @param len Number of bytes to be sent + * @return Current value of status register + */ + uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); + + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register + */ + uint8_t read_payload(void* buf, uint8_t len); + + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); + + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); + + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void); + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status); + + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value); + + /** + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); + /**@}*/ + +public: + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ + + /** + * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(uint8_t _cepin, uint8_t _cspin); + RF24(uint8_t _cepin, uint8_t _cspin, uint32_t spispeed ); + + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + */ + bool begin(void); + + /** + * Start listening on the pipes opened for reading. + * + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. + */ + void startListening(void); + + /** + * Stop listening for incoming messages + * + * Do this before calling write(). + */ + void stopListening(void); + + /** + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool write( const void* buf, uint8_t len ); + + /** + * Test whether there are bytes available to be read + * + * @return True if there is a payload available, false if none is + */ + bool available(void); + + /** + * Read the payload + * + * Return the last payload received + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer + * @return True if the payload was delivered successfully false if not + */ + void read( void* buf, uint8_t len ); + + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param address The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); + + /** + * Open a pipe for reading + * + * Up to 6 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning Pipes 1-5 should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * @code + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); + * @endcode + * + * @warning Pipe 0 is also used by the writing pipe. So if you open + * pipe 0 for reading, and then startListening(), it will overwrite the + * writing pipe. Ergo, do an openWritingPipe() again before write(). + * + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits + * + * @param number Which pipe# to open, 0-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); + + /**@}*/ + /** + * @name Optional Configurators + * + * Methods you can use to get or set the configuration of the chip. + * None are required. Calling begin() sets up a reasonable set of + * defaults. + */ + /**@{*/ + /** + * Set the number and delay of retries upon failed submit + * + * @param delay How long to wait between each retry, in multiples of 250us, + * max is 15. 0 means 250us, 15 means 4000us. + * @param count How many retries before giving up, max 15 + */ + void setRetries(uint8_t delay, uint8_t count); + + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); + + /** + * Set Static Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); + + /** + * Get Static Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void); + + /** + * Get Dynamic Payload Size + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @return Payload length of last-received dynamic payload + */ + uint8_t getDynamicPayloadSize(void); + + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde + */ + void enableAckPayload(void); + + /** + * Enable dynamically-sized payloads + * + * This way you don't always have to send large packets just to send them + * once in a while. This enables dynamic payloads on ALL pipes. + * + * @see examples/pingpair_pl/pingpair_dyn.pde + */ + void enableDynamicPayloads(void); + + /** + * Determine whether the hardware is an nRF24L01+ or not. + * + * @return true if the hardware is nRF24L01+ (or compatible) and false + * if its not. + */ + bool isPVariant(void) ; + + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); + + /** + * Enable or disable auto-acknowlede packets on a per pipeline basis. + * + * AA is enabled by default, so it's only needed if you want to turn + * it off/on for some reason on a per pipeline basis. + * + * @param pipe Which pipeline to modify + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck( uint8_t pipe, bool enable ) ; + + /** + * Set Power Amplifier (PA) level to one of four levels. + * Relative mnemonics have been used to allow for future PA level + * changes. According to 6.5 of the nRF24L01+ specification sheet, + * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, + * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. + * + * @param level Desired PA level. + */ + void setPALevel( uint8_t level ) ; + + /** + * Fetches the current PA level. + * + * @return Returns a value from the rf24_pa_dbm_e enum describing + * the current PA setting. Please remember, all values represented + * by the enum mnemonics are negative dBm. See setPALevel for + * return value descriptions. + */ + uint8_t getPALevel( void ) ; + + /** + * Set the transmission data rate + * + * @warning setting RF24_250KBPS will fail for non-plus units + * + * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps + * @return true if the change was successful + */ + bool setDataRate(rf24_datarate_e speed); + + /** + * Fetches the transmission data rate + * + * @return Returns the hardware's currently configured datarate. The value + * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the + * rf24_datarate_e enum. + */ + rf24_datarate_e getDataRate( void ) ; + + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); + + /** + * Get the CRC length + * + * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + rf24_crclength_e getCRCLength(void); + + /** + * Disable CRC validation + * + */ + void disableCRC( void ) ; + + /**@}*/ + /** + * @name Advanced Operation + * + * Methods you can use to drive the chip in more advanced ways + */ + /**@{*/ + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); + + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening, or powerUp(). + */ + void powerDown(void); + + /** + * Leave low-power mode - making radio more responsive + * + * To return to low power mode, call powerDown(). + */ + void powerUp(void) ; + +/** + * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * + * Can be used with enableAckPayload() to request a response + * @see enableDynamicAck() + * @see setAutoAck() + * @see write() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0), NOACK (1) + */ + bool write( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command * + * This will not block until the 3 FIFO buffers are filled with data. + * Once the FIFOs are full, writeFast will simply wait for success or + * timeout, and return 1 or 0 respectively. From a user perspective, just + * keep trying to send the same data. The library will keep auto retrying + * the current payload using the built in functionality. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeFast is called + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(1000); // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0. + * @endcode + * + * @see txStandBy() + * @see write() + * @see writeBlocking() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool writeFast( const void* buf, uint8_t len ); + + /** + * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + */ + bool writeFast( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command + * This function extends the auto-retry mechanism to any specified duration. + * It will not block until the 3 FIFO buffers are filled with data. + * If so the library will auto retry until a new payload is written + * or the user specified timeout period is reached. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeBlocking is called + * @code + * Example (Full blocking): + * + * radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers + * txStandBy(1000); //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed. + * //Blocks only until user timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @see txStandBy() + * @see write() + * @see writeFast() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param timeout User defined timeout in milliseconds. + * @return True if the payload was loaded into the buffer successfully false if not + */ + bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); + + /** + * @note Optimization: New Command + * This function should be called as soon as transmission is finished to + * drop the radio back to STANDBY-I mode. If not issued, the radio will + * remain in STANDBY-II mode which, per the data sheet, is not a recommended + * operating mode. + * + * @note When transmitting data in rapid succession, it is still recommended by + * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is + * time enough between sends for the FIFOs to empty. + * + * Relies on built-in auto retry functionality. + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. + * //Blocks only until MAX_RT timeout or success. Data flushed on fail. + * @endcode + * @see txStandBy(unsigned long timeout) + * @return True if transmission is successful + * + */ + bool txStandBy(); + + /** + * @note Optimization: New Command + * + * This function allows extended blocking and auto-retries per a user defined timeout + * @code + * Fully Blocking Example: + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. + * //Blocks only until user defined timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @param timeout Number of milliseconds to retry failed payloads + * @return True if transmission is successful + * + */ + bool txStandBy(uint32_t timeout); + + /** + * Test whether there are bytes available to be read + * + * Use this version to discover on which pipe the message + * arrived. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + bool available(uint8_t* pipe_num); + +/** + * Non-blocking write to the open writing pipe used for buffered writes + * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * @see write() + * @see writeFast() + * @see startWrite() + * @see writeBlocking() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * @return True if the payload was delivered successfully false if not + */ + void startFastWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @note Optimization: This function again behaves as it did previously for backwards-compatibility. + * with user code. The library uses startFastWrite() internally. + * This is mainly used for single-payload transactions. + * + * @see write() + * @see writeFast() + * @see startFastWrite() + * @see whatHappened() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * + */ + void startWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Optimization: New Command + * + * This function is mainly used internally to take advantage of the auto payload + * re-use functionality of the chip, but can be beneficial to users as well. + * + * The function will instruct the radio to re-use the data in the FIFO buffers, + * and instructs the radio to re-send once the timeout limit has been reached. + * Used by writeFast and writeBlocking to initiate retries when a TX failure + * occurs. Retries are automatically initiated except with the standard write(). + * This way, data is not flushed from the buffer until switching between modes. + * + * @note This is to be used AFTER auto-retry fails if wanting to resend + * using the built-in payload reuse features. + * After issuing reUseTX(), it will keep reending the same payload forever or until + * a payload is written to the FIFO, or a flush_tx command is given. + */ + void reUseTX(); + + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time. I have not tested this. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + /** + * Enable dynamic ACKs (single write multicasting) for chosen messages + * + * @note To enable full multicasting or per-pipe multicast, use setAutoAck() + * + * @warning This MUST be called prior to attempting single write NOACK calls + * @code + * radio.enableDynamicAck(); + * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested + * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK + * @endcode + */ + void enableDynamicAck(); + + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @warning Calling this function clears the internal flag which indicates + * a payload is available. If it returns true, you must read the packet + * out as the very next interaction with the radio, or the results are + * undefined. + * + * @return True if an ack payload is available. + */ + bool isAckPayloadAvailable(void); + + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + bool testCarrier(void); + + /** + * Test whether a signal (carrier or otherwise) greater than + * or equal to -64dBm is present on the channel. Valid only + * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * + * Useful to check for interference on the current channel and + * channel hopping strategies. + * + * @return true if signal => -64dBm, false if not + */ + bool testRPD(void) ; + + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + + /** + * The radio will generate interrupt signals when a transmission is complete, + * a transmission fails, or a payload is received. This allows users to mask + * those interrupts to prevent them from generating a signal on the interrupt + * pin. + * + * @code + * Mask all interrupts except the receive interrupt: + * + * radio.maskIRQ(1,1,0); + * @endcode + * + * @param tx_ok Mask transmission complete interrupts + * @param tx_fail Mask transmit failure interrupts + * @param rx_ready Mask payload received interrupts + */ + void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); + + /**@}*/ +}; + +/** + * @example GettingStarted.pde + * + * This is an example which corresponds to my "Getting Started" blog post: + * Getting Started with nRF24L01+ on Arduino. + * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + +/** + * @example led_remote.pde + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + +/** + * @example pingpair.pde + * + * This is an example of how to use the RF24 class. Write this sketch to two + * different nodes, connect the role_pin to ground on one. The ping node sends + * the current time to the pong node, which responds by sending the value back. + * The ping node can then see how long the whole cycle took. + */ + +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +/** + * @example starping.pde + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +/** + * @example pingpair_pl.pde + * + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within + * the Ack packet of each transmission. Note that the payload is set BEFORE + * the sender's message arrives. + */ + +/** + * @example pingpair_irq.pde + * + * This is an example of how to user interrupts to interact with the radio. + * It builds on the pingpair_pl example, and uses ack payloads. + */ + +/** + * @example pingpair_sleepy.pde + * + * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the pingpair.pde example, but the + * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle. + */ + +/** + * @example scanner.pde + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +/** + * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + * + * @section Goals Design Goals + * + * This library is designed to be... + * @li Maximally compliant with the intended operation of the chip + * @li Easy for beginners to use + * @li Consumed with a public interface that's similiar to other Arduino standard libraries + * + * @section News News + * + * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. + * Please open an issue if you find any problems using it with any version of Arduino. + * + * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the + * Maple Native, + * and should work with any Maple board. See the pingpair_maple example. + * Note that only the pingpair_maple example has been tested on Maple, although + * the others can certainly be adapted. + * + * @section Useful Useful References + * + * Please refer to: + * + * @li Documentation Main Page + * @li RF24 Class Documentation + * @li Source Code + * @li Downloads Page + * @li Chip Datasheet + * + * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or + * the SPI hardware will go into 'slave' mode. + * + * @section More More Information + * + * @subpage FAQ + * + * @section Projects Projects + * + * Stuff I have built with RF24 + * + * RF24 Getting Started - Finished Product + * + * Getting Started with nRF24L01+ on Arduino + * + * Nordic FOB and nRF24L01+ + * + * Using the Sparkfun Nordic FOB + * + * RF Duinode V3 (2V4) + * + * Low-Power Wireless Sensor Node + * + * nRF24L01+ connected to Leaf Labs Maple Native + * + * nRF24L01+ Running on Maple + */ + +#endif // __RF24_H__ +// vim:ai:cin:sts=2 sw=2 ft=cpp + diff --git a/RPi/RF24/RF24_config.h b/RPi/RF24/RF24_config.h new file mode 100644 index 0000000..eef0704 --- /dev/null +++ b/RPi/RF24/RF24_config.h @@ -0,0 +1,34 @@ + +/* + Copyright (C) 2011 J. Coliz + + 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. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Modified to use the great bcm2835 library for I/O and SPI + + */ + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ + +#include +#include +#include +#include +#include +#include +#include "bcm2835.h" + +// GCC a Arduino Missing +#define max(a,b) (a>b?a:b) +#define min(a,b) (a +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "bcm2835.h" + +// This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11) +// You can do some safe, non-destructive testing on any platform with: +// gcc bcm2835.c -D BCM2835_TEST +// ./a.out +//#define BCM2835_TEST + +// Uncommenting this define compiles alternative I2C code for the version 1 RPi +// The P1 header I2C pins are connected to SDA0 and SCL0 on V1. +// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. +// #define I2C_V1 + +// Pointers to the hardware register bases +volatile uint32_t *bcm2835_gpio = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pwm = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_clk = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pads = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_spi0 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc0 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc1 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_st = (volatile 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; + +// I2C The time needed to transmit one byte. In microseconds. +static int i2c_byte_wait_us = 0; + +// SPI Custom Chip Select Pin +static int spi_custom_cs = 0; + +// Time for millis() +static unsigned long long epoch ; + +// +// Low level register access functions +// + +void bcm2835_set_debug(uint8_t d) +{ + debug = d; +} + +// safe read from peripheral +uint32_t bcm2835_peri_read(volatile uint32_t* paddr) +{ + if (debug) + { + printf("bcm2835_peri_read paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + // Make sure we dont return the _last_ read which might get lost + // if subsequent code changes to a different peripheral + uint32_t ret = *paddr; + *paddr; // Read without assigneing to an unused variable + return ret; + } +} + +// read from peripheral without the read barrier +uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) +{ + if (debug) + { + printf("bcm2835_peri_read_nb paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + return *paddr; + } +} + +// safe write to peripheral +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); + } + else + { + // Make sure we don't rely on the first write, which may get + // lost if the previous access was to a different peripheral. + *paddr = value; + *paddr = value; + } +} + +// write to peripheral without the write barrier +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); + } + else + { + *paddr = value; + } +} + +// Set/clear only the bits in value covered by the mask +void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask) +{ + uint32_t v = bcm2835_peri_read(paddr); + v = (v & ~mask) | (value & mask); + bcm2835_peri_write(paddr, v); +} + +// +// Low level convenience functions +// + +// Function select +// pin is a BCM2835 GPIO pin number NOT RPi pin number +// There are 6 control registers, each control the functions of a block +// of 10 pins. +// Each control register has 10 sets of 3 bits per GPIO pin: +// +// 000 = GPIO Pin X is an input +// 001 = GPIO Pin X is an output +// 100 = GPIO Pin X takes alternate function 0 +// 101 = GPIO Pin X takes alternate function 1 +// 110 = GPIO Pin X takes alternate function 2 +// 111 = GPIO Pin X takes alternate function 3 +// 011 = GPIO Pin X takes alternate function 4 +// 010 = GPIO Pin X takes alternate function 5 +// +// So the 3 bits for port X are: +// X / 10 + ((X % 10) * 3) +void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode) +{ + // Function selects are 10 pins per 32 bit word, 3 bits per pin + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10); + uint8_t shift = (pin % 10) * 3; + uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift; + uint32_t value = mode << shift; + bcm2835_peri_set_bits(paddr, value, mask); +} + +// Set output pin +void bcm2835_gpio_set(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +// Clear output pin +void bcm2835_gpio_clr(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +// Set all output pins in the mask +void bcm2835_gpio_set_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4; + bcm2835_peri_write(paddr, mask); +} + +// Clear all output pins in the mask +void bcm2835_gpio_clr_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4; + bcm2835_peri_write(paddr, mask); +} + +// Read input pin +uint8_t bcm2835_gpio_lev(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEV0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +// See if an event detection bit is set +// Sigh cant support interrupts yet +uint8_t bcm2835_gpio_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +// Write a 1 to clear the bit in EDS +void bcm2835_gpio_set_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_write(paddr, value); +} + +// Rising edge detect enable +void bcm2835_gpio_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Falling edge detect enable +void bcm2835_gpio_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// High detect enable +void bcm2835_gpio_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Low detect enable +void bcm2835_gpio_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Async rising edge detect enable +void bcm2835_gpio_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Async falling edge detect enable +void bcm2835_gpio_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Set pullup/down +void bcm2835_gpio_pud(uint8_t pud) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; + bcm2835_peri_write(paddr, pud); +} + +// Pullup/down clock +// Clocks the value of pud into the GPIO pin +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); +} + +// Read GPIO pad behaviour for groups of GPIOs +uint32_t bcm2835_gpio_pad(uint8_t group) +{ + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; + return bcm2835_peri_read(paddr); +} + +// Set GPIO pad behaviour for groups of GPIOs +// powerup value for al pads is +// BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA +void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) +{ + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; + bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD); +} + +// Some convenient arduino-like functions +// milliseconds +void bcm2835_delay(unsigned int millis) +{ + struct timespec sleeper; + + sleeper.tv_sec = (time_t)(millis / 1000); + sleeper.tv_nsec = (long)(millis % 1000) * 1000000; + nanosleep(&sleeper, NULL); +} + +// microseconds +void bcm2835_delayMicroseconds(uint64_t micros) +{ + struct timespec t1; + uint64_t start; + + // Calling nanosleep() takes at least 100-200 us, so use it for + // long waits and use a busy wait on the System Timer for the rest. + start = bcm2835_st_read(); + + if (micros > 450) + { + t1.tv_sec = 0; + t1.tv_nsec = 1000 * (long)(micros - 200); + nanosleep(&t1, NULL); + } + + bcm2835_st_delay(start, micros); +} + +// This function is added in order to simulate arduino millis() function +unsigned int bcm2835_millis(void) +{ +struct timeval now; +unsigned long long ms; + +gettimeofday(&now, NULL); + +ms = (now.tv_sec * 1000000 + now.tv_usec) / 1000 ; + +return ((uint32_t) (ms - epoch )); +} + +// +// Higher level convenience functions +// + +// Set the state of an output +void bcm2835_gpio_write(uint8_t pin, uint8_t on) +{ + if (on) + bcm2835_gpio_set(pin); + else + bcm2835_gpio_clr(pin); +} + +// Set the state of a all 32 outputs in the mask to on or off +void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on) +{ + if (on) + bcm2835_gpio_set_multi(mask); + else + bcm2835_gpio_clr_multi(mask); +} + +// Set the state of a all 32 outputs in the mask to the values in value +void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) +{ + bcm2835_gpio_set_multi(value & mask); + bcm2835_gpio_clr_multi((~value) & mask); +} + +// Set the pullup/down resistor for a pin +// +// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on +// the respective GPIO pins. These registers must be used in conjunction with the GPPUD +// register to effect GPIO Pull-up/down changes. The following sequence of events is +// required: +// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither +// to remove the current Pull-up/down) +// 2. Wait 150 cycles ? this provides the required set-up time for the control signal +// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to +// modify ? NOTE only the pads which receive a clock will be modified, all others will +// retain their previous state. +// 4. Wait 150 cycles ? this provides the required hold time for the control signal +// 5. Write to GPPUD to remove the control signal +// 6. Write to GPPUDCLK0/1 to remove the clock +// +// RPi has P1-03 and P1-05 with 1k8 pullup resistor +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); +} + +void bcm2835_spi_begin(uint8_t cs) +{ + /* + // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 + + + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK + + // Set the SPI CS register to the some sensible defaults + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + bcm2835_peri_write(paddr, 0); // All 0s + + // Clear TX and RX fifos + bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); + */ + + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them + // except if we need custom Chip Select Pin + // printf("bcm2835_spi_begin -> spi_custom_cs = %d \n",cs ); + + // Do we need custom chip select control or + // drive CE1 manually (because CE1 does not work with hardware) + if ( cs > BCM2835_SPI_CS_NONE || cs == BCM2835_SPI_CS1 ) + { + // indicate we will use a custom GPIO port + spi_custom_cs = cs ; + + // ok hard CE1 not working, drive it manually + if (cs == BCM2835_SPI_CS1 ) + { + // Dirty Hack CE1 in now custom Chip Select GPIO 26 + // the real CE1 pin + spi_custom_cs = RPI_GPIO_P1_26 ; + + bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_write(spi_custom_cs, HIGH); + } + + // Mask in we use custom CS (not sure it has a real effect) + bcm2835_peri_set_bits(paddr, BCM2835_SPI_CS_NONE, BCM2835_SPI0_CS_CS); + } + // Ok hardware driving of chip select + else + { + // Just in case + spi_custom_cs = 0 ; + + // Mask in the CS bits of CS + bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); + } + + // Now we can drive the I/O as asked + if (spi_custom_cs == 0) + { + // Not custom CS, so hardware driven + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + } + else + { + // so set custom CS as output, High level by default + bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_OUTP); // Custom GPIO + bcm2835_gpio_write(spi_custom_cs, HIGH); + } + + // Classic pin, hardware driven + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK + + // Set the SPI CS register to the some sensible defaults + bcm2835_peri_write(paddr, 0); // All 0s + + // Clear TX and RX fifos + bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); +} + +void bcm2835_spi_end(void) +{ + // Set all the SPI0 pins back to input + if (spi_custom_cs == 0) + { + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 + } + else + { + bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_INPT); // Custom GPIO + } + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK + + /* + // Set all the SPI0 pins back to input + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK +*/ + + +} + +// Drive Custom chip select pin +void bcm2835_spi_setChipSelect(uint8_t level) +{ + // Do this only if we are using custom ChipSelect I/O + if ( spi_custom_cs > BCM2835_SPI_CS_NONE ) + bcm2835_gpio_write(spi_custom_cs, level); +} + + + void bcm2835_spi_setBitOrder(uint8_t order) +{ + // BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one suported by SPI0 +} + +// defaults to 0, which means a divider of 65536. +// The divisor must be a power of 2. Odd numbers +// rounded down. The maximum SPI clock rate is +// of the APB clock +void bcm2835_spi_setClockDivider(uint16_t divider) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CLK/4; + bcm2835_peri_write(paddr, divider); +} + +void bcm2835_spi_setDataMode(uint8_t mode) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + // Mask in the CPO and CPHA bits of CS + bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA); +} + +// Writes (and reads) a single byte to SPI +uint8_t bcm2835_spi_transfer(uint8_t value) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + + // Custom chip select LOW + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(LOW); + } + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + // 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)){ + delayMicroseconds(10); + } + + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, value); + + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ + delayMicroseconds(10); + } + + // Read any byte that was sent back by the slave while we sere sending to it + uint32_t ret = bcm2835_peri_read_nb(fifo); + + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); + + // Custom chip select HIGH + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(HIGH); + } + return ret; +} + +// Writes (and reads) an number of bytes to SPI +void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + uint32_t TXCnt=0; + uint32_t RXCnt=0; + + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + + // 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); + + // Use the FIFO's to reduce the interbyte times + 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 )) + { + delayMicroseconds(10); + bcm2835_peri_write_nb(fifo, tbuf[TXCnt]); + TXCnt++; + } + //Rx fifo not empty, so get the next received bytes + while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) + { + delayMicroseconds(10); + rbuf[RXCnt] = bcm2835_peri_read_nb(fifo); + RXCnt++; + } + } + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ + delayMicroseconds(10); + } + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); +} + +// Writes an number of bytes to SPI +void bcm2835_spi_writenb(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; + + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(LOW); + } + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + + // 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); + + uint32_t i; + for (i = 0; i < len; i++) + { + // Maybe wait for TXD + while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) + ; + + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, tbuf[i]); + + // Read from FIFO to prevent stalling + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + } + + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) { + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + }; + + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); + + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(HIGH); + } +} + +// Writes (and reads) an number of bytes to SPI +// Read bytes are copied over onto the transmit buffer +void bcm2835_spi_transfern(char* buf, uint32_t len) +{ + bcm2835_spi_transfernb(buf, buf, len); +} + +void bcm2835_spi_chipSelect(uint8_t cs) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + // Mask in the CS bits of CS + bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); +} + +void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + uint8_t shift = 21 + cs; + // Mask in the appropriate CSPOLn bit + bcm2835_peri_set_bits(paddr, active << shift, 1 << shift); +} + +void bcm2835_i2c_begin(void) +{ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; + // Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; + // Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL +#endif + + // Read the clock divider register + uint16_t cdiv = bcm2835_peri_read(paddr); + // Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9; +} + +void bcm2835_i2c_end(void) +{ +#ifdef I2C_V1 + // Set all the I2C/BSC0 pins back to input + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL +#else + // Set all the I2C/BSC1 pins back to input + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL +#endif +} + +void bcm2835_i2c_setSlaveAddress(uint8_t addr) +{ + // Set I2C Device Address +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4; +#endif + bcm2835_peri_write(paddr, addr); +} + +// defaults to 0x5dc, should result in a 166.666 kHz I2C clock frequency. +// The divisor must be a power of 2. Odd numbers +// rounded down. +void bcm2835_i2c_setClockDivider(uint16_t divider) +{ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; +#endif + bcm2835_peri_write(paddr, divider); + // Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + i2c_byte_wait_us = ((float)divider / BCM2835_CORE_CLK_HZ) * 1000000 * 9; +} + +// set I2C clock divider by means of a baudrate number +void bcm2835_i2c_set_baudrate(uint32_t baudrate) +{ + uint32_t divider; + // use 0xFFFE mask to limit a max value and round down any odd number + divider = (BCM2835_CORE_CLK_HZ / baudrate) & 0xFFFE; + bcm2835_i2c_setClockDivider( (uint16_t)divider ); +} + +// Writes an number of bytes to I2C +uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, len); + // pre populate FIFO with max buffer + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, buf[i]); + i++; + remaining--; + } + + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // Transfer is over when BCM2835_BSC_S_DONE + while(!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE )) + { + while ( remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TXD )) + { + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, buf[i]); + i++; + remaining--; + } + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read an number of bytes from I2C +uint8_t bcm2835_i2c_read(char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, len); + // Start read + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ); + + // 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) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is received + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read an number of bytes from I2C sending a repeated start after writing +// the required register. Only works if your device supports this mode +uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, 1); + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN); + bcm2835_peri_write_nb(fifo, regaddr[0]); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // poll for transfer has started + while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) + { + // Linux may cause us to miss entire transfer stage + if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) + break; + } + + // Send a repeated start with read bit set in address + bcm2835_peri_write_nb(dlen, len); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + // Wait for write to complete and first byte back. + bcm2835_delayMicroseconds(i2c_byte_wait_us * 3); + + // 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 (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Sending an arbitrary number of bytes before issuing a repeated start +// (with no prior stop) and reading a response. Some devices require this behavior. +uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = cmds_len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, cmds_len); + + // pre populate FIFO with max buffer + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, cmds[i]); + i++; + remaining--; + } + + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // poll for transfer has started (way to do repeated start, from BCM2835 datasheet) + while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) + { + // Linux may cause us to miss entire transfer stage + if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) + break; + } + + remaining = buf_len; + i = 0; + + // Send a repeated start with read bit set in address + bcm2835_peri_write_nb(dlen, buf_len); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + // Wait for write to complete and first byte back. + bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1)); + + // 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 (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read the System Timer Counter (64-bits) +uint64_t bcm2835_st_read(void) +{ + volatile uint32_t* paddr; + uint64_t st; + paddr = bcm2835_st + BCM2835_ST_CHI/4; + st = bcm2835_peri_read(paddr); + st <<= 32; + paddr = bcm2835_st + BCM2835_ST_CLO/4; + st += bcm2835_peri_read(paddr); + return st; +} + +// Delays for the specified number of microseconds with offset +void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros) +{ + uint64_t compare = offset_micros + micros; + + while(bcm2835_st_read() < compare) + ; +} + +// PWM + +void bcm2835_pwm_set_clock(uint32_t divisor) +{ + // From Gerts code + divisor &= 0xfff; + // Stop PWM clock + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01); + bcm2835_delay(110); // Prevents clock going slow + // Wait for the clock to be not busy + while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) + bcm2835_delay(1); + // set the clock divider and enable PWM clock + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); // Source=osc and enable +} + +void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled) +{ + uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL); + + if (channel == 0) + { + if (markspace) + control |= BCM2835_PWM0_MS_MODE; + else + control &= ~BCM2835_PWM0_MS_MODE; + if (enabled) + control |= BCM2835_PWM0_ENABLE; + else + control &= ~BCM2835_PWM0_ENABLE; + } + else if (channel == 1) + { + if (markspace) + control |= BCM2835_PWM1_MS_MODE; + else + control &= ~BCM2835_PWM1_MS_MODE; + if (enabled) + control |= BCM2835_PWM1_ENABLE; + else + control &= ~BCM2835_PWM1_ENABLE; + } + + // If you use the barrier here, wierd things happen, and the commands dont work + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control); + // bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); + +} + +void bcm2835_pwm_set_range(uint8_t channel, uint32_t range) +{ + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range); +} + +void bcm2835_pwm_set_data(uint8_t channel, uint32_t data) +{ + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data); +} + +// Allocate page-aligned memory. +void *malloc_aligned(size_t size) +{ + void *mem; + errno = posix_memalign(&mem, BCM2835_PAGE_SIZE, size); + return (errno ? NULL : mem); +} + +// Map 'size' bytes starting at 'off' in file 'fd' to memory. +// Return mapped address on success, MAP_FAILED otherwise. +// On error print message. +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_FAILED == map) + fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); + return map; +} + +static void unmapmem(void **pmem, size_t size) +{ + if (*pmem == MAP_FAILED) return; + munmap(*pmem, size); + *pmem = MAP_FAILED; +} + +// Initialise this library. +int bcm2835_init(void) +{ + struct timeval tv ; + + if (debug) + { + bcm2835_pads = (uint32_t*)BCM2835_GPIO_PADS; + bcm2835_clk = (uint32_t*)BCM2835_CLOCK_BASE; + bcm2835_gpio = (uint32_t*)BCM2835_GPIO_BASE; + bcm2835_pwm = (uint32_t*)BCM2835_GPIO_PWM; + bcm2835_spi0 = (uint32_t*)BCM2835_SPI0_BASE; + bcm2835_bsc0 = (uint32_t*)BCM2835_BSC0_BASE; + bcm2835_bsc1 = (uint32_t*)BCM2835_BSC1_BASE; + bcm2835_st = (uint32_t*)BCM2835_ST_BASE; + return 1; // Success + } + int memfd = -1; + int ok = 0; + // Open the master /dev/memory device + if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) + { + fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", + strerror(errno)) ; + goto exit; + } + + // GPIO: + bcm2835_gpio = (volatile uint32_t *)mapmem("gpio", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_BASE); + if (bcm2835_gpio == MAP_FAILED) goto exit; + + // PWM + bcm2835_pwm = (volatile uint32_t *)mapmem("pwm", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PWM); + if (bcm2835_pwm == MAP_FAILED) goto exit; + + // Clock control (needed for PWM) + bcm2835_clk = (volatile uint32_t *)mapmem("clk", BCM2835_BLOCK_SIZE, memfd, BCM2835_CLOCK_BASE); + if (bcm2835_clk == MAP_FAILED) goto exit; + + bcm2835_pads = (volatile uint32_t *)mapmem("pads", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PADS); + if (bcm2835_pads == MAP_FAILED) goto exit; + + bcm2835_spi0 = (volatile uint32_t *)mapmem("spi0", BCM2835_BLOCK_SIZE, memfd, BCM2835_SPI0_BASE); + if (bcm2835_spi0 == MAP_FAILED) goto exit; + + // I2C + bcm2835_bsc0 = (volatile uint32_t *)mapmem("bsc0", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC0_BASE); + if (bcm2835_bsc0 == MAP_FAILED) goto exit; + + bcm2835_bsc1 = (volatile uint32_t *)mapmem("bsc1", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC1_BASE); + if (bcm2835_bsc1 == MAP_FAILED) goto exit; + + // ST + bcm2835_st = (volatile uint32_t *)mapmem("st", BCM2835_BLOCK_SIZE, memfd, BCM2835_ST_BASE); + if (bcm2835_st == MAP_FAILED) goto exit; + + ok = 1; + +exit: + if (memfd >= 0) + close(memfd); + + if (!ok) + bcm2835_close(); + + gettimeofday (&tv, NULL) ; + epoch = (tv.tv_sec * 1000000 + tv.tv_usec) / 1000 ; + + return ok; +} + +// Close this library and deallocate everything +int bcm2835_close(void) +{ + if (debug) return 1; // Success + unmapmem((void**) &bcm2835_gpio, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_pwm, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_clk, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_spi0, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_bsc0, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_bsc1, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_st, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_pads, BCM2835_BLOCK_SIZE); + return 1; // Success +} + +#ifdef BCM2835_TEST +// this is a simple test program that prints out what it will do rather than +// actually doing it +int main(int argc, char **argv) +{ + // Be non-destructive + bcm2835_set_debug(1); + + if (!bcm2835_init()) + return 1; + + // Configure some GPIO pins fo some testing + // Set RPI pin P1-11 to be an output + bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP); + // Set RPI pin P1-15 to be an input + bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT); + // with a pullup + bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP); + // And a low detect enable + bcm2835_gpio_len(RPI_GPIO_P1_15); + // and input hysteresis disabled on GPIOs 0 to 27 + bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA); + +#if 1 + // Blink + while (1) + { + // Turn it on + bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH); + + // wait a bit + bcm2835_delay(500); + + // turn it off + bcm2835_gpio_write(RPI_GPIO_P1_11, LOW); + + // wait a bit + bcm2835_delay(500); + } +#endif + +#if 0 + // Read input + while (1) + { + // Read some data + uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15); + printf("read from pin 15: %d\n", value); + + // wait a bit + bcm2835_delay(500); + } +#endif + +#if 0 + // Look for a low event detection + // eds will be set whenever pin 15 goes low + while (1) + { + if (bcm2835_gpio_eds(RPI_GPIO_P1_15)) + { + // Now clear the eds flag by setting it to 1 + bcm2835_gpio_set_eds(RPI_GPIO_P1_15); + printf("low event detect for pin 15\n"); + } + + // wait a bit + bcm2835_delay(500); + } +#endif + + if (!bcm2835_close()) + return 1; + + return 0; +} +#endif + + + diff --git a/RPi/RF24/bcm2835.h b/RPi/RF24/bcm2835.h new file mode 100644 index 0000000..ab47838 --- /dev/null +++ b/RPi/RF24/bcm2835.h @@ -0,0 +1,1353 @@ +// bcm2835.h +// +// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi +// +// Author: Mike McCauley +// Copyright (C) 2011-2013 Mike McCauley +// $Id: bcm2835.h,v 1.13 2013/12/06 22:24:52 mikem Exp mikem $ +// +//****************************************/ +// TMRh20 2014 - Merge updated lib from here: http://www.airspayce.com/mikem/bcm2835/group__spi.html#ga2fa186568605c21e9166a19b1d82ea95 +// with lib from here: https://github.com/farconada/RF24Network/blob/master/bcm2835.c +// Uses ONLY hardware CS pins +// Any CE pin can be used +// For use with optimized RPi RF24 and RF24 Network libs - see github.com/TMRh20 +//****************************************/ +// +/// \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi +/// +/// This is a C library for Raspberry Pi (RPi). It provides access to +/// GPIO and other IO functions on the Broadcom BCM 2835 chip, +/// 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. +/// +/// It provides functions for reading digital inputs and setting digital outputs, using SPI and I2C, +/// and for accessing the system timers. +/// Pin event detection is supported by polling (interrupts are not supported). +/// +/// 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.36.tar.gz +/// You can find the latest version at http://www.airspayce.com/mikem/bcm2835 +/// +/// Several example programs are provided. +/// +/// Based on data in http://elinux.org/RPi_Low-level_peripherals and +/// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +/// and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// +/// 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. +/// +/// Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian +/// and Occidentalisv01 +/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() +/// are used and the pin is pulled LOW +/// it can cause temporary hangs on 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian +/// and Occidentalisv01. +/// Reason for this is not yet determined, but we suspect that an interrupt handler is +/// hitting a hard loop on those OSs. +/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with +/// bcm2835_gpio_clr_len() and friends after use. +/// +/// \par Installation +/// +/// This library consists of a single non-shared library and header file, which will be +/// installed in the usual places by make install +/// +/// \code +/// # download the latest version of the library, say bcm2835-1.xx.tar.gz, then: +/// tar zxvf bcm2835-1.xx.tar.gz +/// cd bcm2835-1.xx +/// ./configure +/// make +/// sudo make check +/// sudo make install +/// \endcode +/// +/// \par Physical Addresses +/// +/// The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() +/// are low level peripheral register access functions. They are designed to use +/// physical addresses as described in section 1.2.3 ARM physical addresses +/// of the BCM2835 ARM Peripherals manual. +/// Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus +/// addresses for peripherals are set up to map onto the peripheral bus address range starting at +/// 0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at +/// physical address 0x20nnnnnn. +/// +/// The base address of the various peripheral registers are available with the following +/// externals: +/// bcm2835_gpio +/// bcm2835_pwm +/// bcm2835_clk +/// bcm2835_pads +/// bcm2835_spio0 +/// bcm2835_st +/// bcm2835_bsc0 +/// bcm2835_bsc1 +/// +/// \par Pin Numbering +/// +/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying +/// BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs +/// +/// RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, +/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the +/// IDE header. +/// +/// RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd. +/// +/// The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ +/// the RPi pin number. There are symbolic definitions for each of the available pins +/// that you should use for convenience. See \ref RPiGPIOPin. +/// +/// \par SPI Pins +/// +/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, +/// allowing you to send and received data by SPI (Serial Peripheral Interface). +/// For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus +/// +/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their +/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able +/// to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). +/// When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be +/// configured and controled with the usual bcm2835_gpio_* calls. +/// +/// The Raspberry Pi GPIO pins used for SPI are: +/// +/// - P1-19 (MOSI) +/// - P1-21 (MISO) +/// - P1-23 (CLK) +/// - P1-24 (CE0) +/// - P1-26 (CE1) +/// +/// \par I2C Pins +/// +/// The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface, +/// allowing you to send and received data by I2C ("eye-squared cee"; generically referred to as "two-wire interface") . +/// For more information about I?C, see http://en.wikipedia.org/wiki/I%C2%B2C +/// +/// The Raspberry Pi V2 GPIO pins used for I2C are: +/// +/// - P1-03 (SDA) +/// - P1-05 (SLC) +/// +/// \par PWM +/// +/// The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides +/// functions for configuring and controlling PWM output on these pins. +/// +/// The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset of +/// GPIO pins. The following GPIO pins may be connected to the following PWM channels (from section 9.5): +/// \code +/// GPIO PIN RPi pin PWM Channel ALT FUN +/// 12 0 0 +/// 13 1 0 +/// 18 1-12 0 5 +/// 19 1 5 +/// 40 0 0 +/// 41 1 0 +/// 45 1 0 +/// 52 0 1 +/// 53 1 1 +/// \endcode +/// In order for a GPIO pin to emit output from its PWM channel, it must be set to the Alt Function given above. +/// Note carefully that current versions of the Raspberry Pi only expose one of these pins (GPIO 18 = RPi Pin 1-12) +/// on the IO headers, and therefore this is the only IO pin on the RPi that can be used for PWM. +/// Further it must be set to ALT FUN 5 to get PWM output. +/// +/// Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using +/// bcm2835_pwm_set_clock(). Each channel can be separately enabled with bcm2835_pwm_set_mode(). +/// The average output of the PWM channel is determined by the ratio of DATA/RANGE for that channel. +/// Use bcm2835_pwm_set_range() to set the range and bcm2835_pwm_set_data() to set the data in that ratio +/// +/// Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware +/// sends a combination of clock pulses that results in an overall DATA pulses per RANGE pulses. +/// In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by +/// LOW for RANGE-DATA clock pulses. +/// +/// The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from +/// a 19.2MHz clock. You can set any divider, but some common ones are provided by the BCM2835_PWM_CLOCK_DIVIDER_* +/// values of \ref bcm2835PWMClockDivider. +/// +/// For example, say you wanted to drive a DC motor with PWM at about 1kHz, +/// and control the speed in 1/1024 increments from +/// 0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the +/// clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be +/// 1.2MHz/1024 = 1171.875Hz. +/// +/// \par Real Time performance constraints +/// +/// The bcm2835 is a library for user programs (i.e. they run in 'userland'). +/// Such programs are not part of the kernel and are usually +/// subject to paging and swapping by the kernel while it does other things besides running your program. +/// This means that you should not expect to get real-time performance or +/// real-time timing constraints from such programs. In particular, there is no guarantee that the +/// bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. +/// In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times +/// than the one you asked for. So please dont expect to get exactly the time delay you request. +/// +/// Arjan reports that you can prevent swapping on Linux with the following code fragment: +/// +/// \code +/// struct sched_param sp; +/// memset(&sp, 0, sizeof(sp)); +/// sp.sched_priority = sched_get_priority_max(SCHED_FIFO); +/// sched_setscheduler(0, SCHED_FIFO, &sp); +/// mlockall(MCL_CURRENT | MCL_FUTURE); +/// \endcode +/// +/// \par Bindings to other languages +/// +/// mikem has made Perl bindings available at CPAN: +/// http://search.cpan.org/~mikem/Device-BCM2835-1.9/lib/Device/BCM2835.pm +/// Matthew Baker has kindly made Python bindings available at: +/// https://github.com/mubeta06/py-libbcm2835 +/// Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility +/// for Raspberry Pi, based on the bcm2835 library. The +/// utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as a +/// free download with source included: http://ipsolutionscorp.com/raspberry-pi-spi-utility/ +/// +/// \par Open Source Licensing GPL V2 +/// +/// This is the appropriate option if you want to share the source code of your +/// application with everyone you distribute it to, and you also want to give them +/// 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 +/// +/// \par Acknowledgements +/// +/// Some of this code has been inspired by Dom and Gert. +/// The I2C code has been inspired by Alan Barr. +/// +/// \par Revision History +/// +/// \version 1.0 Initial release +/// \version 1.1 Minor bug fixes +/// \version 1.2 Added support for SPI +/// \version 1.3 Added bcm2835_spi_transfern() +/// \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson. +/// \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz +/// \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 +/// Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() +/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now +/// changes only the pin specified. Other pins that were already previously +/// enabled stay enabled. +/// Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() +/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() +/// to clear the enable for individual pins, suggested by Andreas Sundstrom. +/// \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write. +/// \version 1.8 Improvements to read barrier, as suggested by maddin. +/// \version 1.9 Improvements contributed by mikew: +/// I noticed that it was mallocing memory for the mmaps on /dev/mem. +/// It's not necessary to do that, you can just mmap the file directly, +/// so I've removed the mallocs (and frees). +/// I've also modified delayMicroseconds() to use nanosleep() for long waits, +/// and a busy wait on a high resolution timer for the rest. This is because +/// I've found that calling nanosleep() takes at least 100-200 us. +/// You need to link using '-lrt' using this version. +/// I've added some unsigned casts to the debug prints to silence compiler +/// warnings I was getting, fixed some typos, and changed the value of +/// BCM2835_PAD_HYSTERESIS_ENABLED to 0x08 as per Gert van Loo's doc at +/// http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// Also added a define for the passwrd value that Gert says is needed to +/// change pad control settings. +/// \version 1.10 Changed the names of the delay functions to bcm2835_delay() +/// and bcm2835_delayMicroseconds() to prevent collisions with wiringPi. +/// Macros to map delay()-> bcm2835_delay() and +/// Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which +/// can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY +/// \version 1.11 Fixed incorrect link to download file +/// \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) +/// \version 1.13 New GPIO pin definitions for RPi version 2 plug P5 +/// Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio +/// bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0. +/// \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead. +/// Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. +/// Reported by Ben Simpson. +/// \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe. +/// \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi +/// to allow a mask of pins to be set all at once. Requested by Sebastian Loncar. +/// \version 1.17 Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar. +/// \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: +/// now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. +/// No need to link with -lrt now. Contributed by Arjan van Vught. +/// \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. +/// Reported by olly. +/// \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted. +/// \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. +/// Patch courtesy Jeremy Mortis. +/// \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set +/// falling edge events. Reported by Mark Dootson. +/// \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. +/// Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions +/// to fix ocasional reads not completing. Patched by Mark Dootson. +/// \version 1.24 Mark Dootson p[atched a problem with his previously submitted code +/// under high load from other processes. +/// \version 1.25 Updated author and distribution location details to airspayce.com +/// \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. +/// Reported by Hartmut Henkel. +/// \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. +/// \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. +/// \version 1.29 Further fix to SPI from Peter Würtz. +/// \version 1.30 10 microsecond delays from bcm2835_spi_transfer and bcm2835_spi_transfern for +/// significant performance improvements, Patch by Alan Watson. +/// \version 1.31 Fix a GCC warning about dummy variable, patched by Alan Watson. Thanks. +/// \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. +/// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. +/// Contributed by Malcolm Wiles based on work by Arvi Govindaraj. +/// \version 1.33 Added command line utilities i2c and gpio to examples. Contributed by Shahrooz Shahparnia. +/// \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, +/// sends a repeat start, and reads from the device. Contributed by Eduardo Steinhorst. +/// \version 1.35 Fix build errors when compiled under Qt. Also performance improvements with SPI transfers. Contributed by Udo Klaas. +/// \version 1.36 Make automake's test runner detect that we're skipping tests when not root, the second +/// one makes us skip the test when using fakeroot (as used when building +/// Debian packages). Contributed by Guido Günther. +/// +/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS + + + +// Defines for BCM2835 +#ifndef BCM2835_H +#define BCM2835_H + +#include + +/// \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. +/// @{ + + +/// This means pin HIGH, true, 3.3volts on a pin. +#define HIGH 0x1 +/// This means pin LOW, false, 0volts on a pin. +#define LOW 0x0 + +/// Speed of the core clock core_clk +#define BCM2835_CORE_CLK_HZ 250000000 ///< 250 MHz + +// Physical addresses for various peripheral register sets +/// Base Physical Address of the BCM 2835 peripheral registers +#define BCM2835_PERI_BASE 0x20000000 +/// Base Physical Address of the System Timer registers +#define BCM2835_ST_BASE (BCM2835_PERI_BASE + 0x3000) +/// Base Physical Address of the Pads registers +#define BCM2835_GPIO_PADS (BCM2835_PERI_BASE + 0x100000) +/// Base Physical Address of the Clock/timer registers +#define BCM2835_CLOCK_BASE (BCM2835_PERI_BASE + 0x101000) +/// Base Physical Address of the GPIO registers +#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) +/// Base Physical Address of the SPI0 registers +#define BCM2835_SPI0_BASE (BCM2835_PERI_BASE + 0x204000) +/// Base Physical Address of the BSC0 registers +#define BCM2835_BSC0_BASE (BCM2835_PERI_BASE + 0x205000) +/// Base Physical Address of the PWM registers +#define BCM2835_GPIO_PWM (BCM2835_PERI_BASE + 0x20C000) + /// Base Physical Address of the BSC1 registers +#define BCM2835_BSC1_BASE (BCM2835_PERI_BASE + 0x804000) + + +/// Base of the ST (System Timer) registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_st; + +/// Base of the GPIO registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_gpio; + +/// Base of the PWM registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pwm; + +/// Base of the CLK registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_clk; + +/// Base of the PADS registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pads; + +/// Base of the SPI0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_spi0; + +/// Base of the BSC0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc0; + +/// Base of the BSC1 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc1; + +/// Size of memory page on RPi +#define BCM2835_PAGE_SIZE (4*1024) +/// Size of memory block on RPi +#define BCM2835_BLOCK_SIZE (4*1024) + + +// Defines for GPIO +// The BCM2835 has 54 GPIO pins. +// BCM2835 data sheet, Page 90 onwards. +/// GPIO register offsets from BCM2835_GPIO_BASE. Offsets into the GPIO Peripheral block in bytes per 6.1 Register View +#define BCM2835_GPFSEL0 0x0000 ///< GPIO Function Select 0 +#define BCM2835_GPFSEL1 0x0004 ///< GPIO Function Select 1 +#define BCM2835_GPFSEL2 0x0008 ///< GPIO Function Select 2 +#define BCM2835_GPFSEL3 0x000c ///< GPIO Function Select 3 +#define BCM2835_GPFSEL4 0x0010 ///< GPIO Function Select 4 +#define BCM2835_GPFSEL5 0x0014 ///< GPIO Function Select 5 +#define BCM2835_GPSET0 0x001c ///< GPIO Pin Output Set 0 +#define BCM2835_GPSET1 0x0020 ///< GPIO Pin Output Set 1 +#define BCM2835_GPCLR0 0x0028 ///< GPIO Pin Output Clear 0 +#define BCM2835_GPCLR1 0x002c ///< GPIO Pin Output Clear 1 +#define BCM2835_GPLEV0 0x0034 ///< GPIO Pin Level 0 +#define BCM2835_GPLEV1 0x0038 ///< GPIO Pin Level 1 +#define BCM2835_GPEDS0 0x0040 ///< GPIO Pin Event Detect Status 0 +#define BCM2835_GPEDS1 0x0044 ///< GPIO Pin Event Detect Status 1 +#define BCM2835_GPREN0 0x004c ///< GPIO Pin Rising Edge Detect Enable 0 +#define BCM2835_GPREN1 0x0050 ///< GPIO Pin Rising Edge Detect Enable 1 +#define BCM2835_GPFEN0 0x0058 ///< GPIO Pin Falling Edge Detect Enable 0 +#define BCM2835_GPFEN1 0x005c ///< GPIO Pin Falling Edge Detect Enable 1 +#define BCM2835_GPHEN0 0x0064 ///< GPIO Pin High Detect Enable 0 +#define BCM2835_GPHEN1 0x0068 ///< GPIO Pin High Detect Enable 1 +#define BCM2835_GPLEN0 0x0070 ///< GPIO Pin Low Detect Enable 0 +#define BCM2835_GPLEN1 0x0074 ///< GPIO Pin Low Detect Enable 1 +#define BCM2835_GPAREN0 0x007c ///< GPIO Pin Async. Rising Edge Detect 0 +#define BCM2835_GPAREN1 0x0080 ///< GPIO Pin Async. Rising Edge Detect 1 +#define BCM2835_GPAFEN0 0x0088 ///< GPIO Pin Async. Falling Edge Detect 0 +#define BCM2835_GPAFEN1 0x008c ///< GPIO Pin Async. Falling Edge Detect 1 +#define BCM2835_GPPUD 0x0094 ///< GPIO Pin Pull-up/down Enable +#define BCM2835_GPPUDCLK0 0x0098 ///< GPIO Pin Pull-up/down Enable Clock 0 +#define BCM2835_GPPUDCLK1 0x009c ///< GPIO Pin Pull-up/down Enable Clock 1 + +/// \brief bcm2835PortFunction +/// Port function select modes for bcm2835_gpio_fsel() +typedef enum +{ + BCM2835_GPIO_FSEL_INPT = 0b000, ///< Input + BCM2835_GPIO_FSEL_OUTP = 0b001, ///< Output + BCM2835_GPIO_FSEL_ALT0 = 0b100, ///< Alternate function 0 + BCM2835_GPIO_FSEL_ALT1 = 0b101, ///< Alternate function 1 + BCM2835_GPIO_FSEL_ALT2 = 0b110, ///< Alternate function 2 + BCM2835_GPIO_FSEL_ALT3 = 0b111, ///< Alternate function 3 + BCM2835_GPIO_FSEL_ALT4 = 0b011, ///< Alternate function 4 + BCM2835_GPIO_FSEL_ALT5 = 0b010, ///< Alternate function 5 + BCM2835_GPIO_FSEL_MASK = 0b111 ///< Function select bits mask +} bcm2835FunctionSelect; + +/// \brief bcm2835PUDControl +/// Pullup/Pulldown defines for bcm2835_gpio_pud() +typedef enum +{ + BCM2835_GPIO_PUD_OFF = 0b00, ///< Off ? disable pull-up/down + BCM2835_GPIO_PUD_DOWN = 0b01, ///< Enable Pull Down control + BCM2835_GPIO_PUD_UP = 0b10 ///< Enable Pull Up control +} bcm2835PUDControl; + +/// 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 +#define BCM2835_PADS_GPIO_46_53 0x0034 ///< Pad control register for pads 46 to 53 + +/// Pad Control masks +#define BCM2835_PAD_PASSWRD (0x5A << 24) ///< Password to enable setting pad mask +#define BCM2835_PAD_SLEW_RATE_UNLIMITED 0x10 ///< Slew rate unlimited +#define BCM2835_PAD_HYSTERESIS_ENABLED 0x08 ///< Hysteresis enabled +#define BCM2835_PAD_DRIVE_2mA 0x00 ///< 2mA drive current +#define BCM2835_PAD_DRIVE_4mA 0x01 ///< 4mA drive current +#define BCM2835_PAD_DRIVE_6mA 0x02 ///< 6mA drive current +#define BCM2835_PAD_DRIVE_8mA 0x03 ///< 8mA drive current +#define BCM2835_PAD_DRIVE_10mA 0x04 ///< 10mA drive current +#define BCM2835_PAD_DRIVE_12mA 0x05 ///< 12mA drive current +#define BCM2835_PAD_DRIVE_14mA 0x06 ///< 14mA drive current +#define BCM2835_PAD_DRIVE_16mA 0x07 ///< 16mA drive current + +/// \brief bcm2835PadGroup +/// Pad group specification for bcm2835_gpio_pad() +typedef enum +{ + BCM2835_PAD_GROUP_GPIO_0_27 = 0, ///< Pad group for GPIO pads 0 to 27 + BCM2835_PAD_GROUP_GPIO_28_45 = 1, ///< Pad group for GPIO pads 28 to 45 + BCM2835_PAD_GROUP_GPIO_46_53 = 2 ///< Pad group for GPIO pads 46 to 53 +} bcm2835PadGroup; + +/// \brief GPIO Pin Numbers +/// +/// Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers. +/// These can be passed as a pin number to any function requiring a pin. +/// Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins +/// and some can adopt an alternate function. +/// RPi version 2 has some slightly different pinouts, and these are values RPI_V2_*. +/// At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively +/// When SPI0 is in use (ie after bcm2835_spi_begin()), pins 19, 21, 23, 24, 26 are dedicated to SPI +/// and cant be controlled independently +typedef enum +{ + RPI_GPIO_P1_03 = 0, ///< Version 1, Pin P1-03 + RPI_GPIO_P1_05 = 1, ///< Version 1, Pin P1-05 + RPI_GPIO_P1_07 = 4, ///< Version 1, Pin P1-07 + RPI_GPIO_P1_08 = 14, ///< Version 1, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_GPIO_P1_10 = 15, ///< Version 1, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_GPIO_P1_11 = 17, ///< Version 1, Pin P1-11 + RPI_GPIO_P1_12 = 18, ///< Version 1, Pin P1-12, can be PWM channel 0 in ALT FUN 5 + RPI_GPIO_P1_13 = 21, ///< Version 1, Pin P1-13 + RPI_GPIO_P1_15 = 22, ///< Version 1, Pin P1-15 + RPI_GPIO_P1_16 = 23, ///< Version 1, Pin P1-16 + RPI_GPIO_P1_18 = 24, ///< Version 1, Pin P1-18 + RPI_GPIO_P1_19 = 10, ///< Version 1, Pin P1-19, MOSI when SPI0 in use + RPI_GPIO_P1_21 = 9, ///< Version 1, Pin P1-21, MISO when SPI0 in use + RPI_GPIO_P1_22 = 25, ///< Version 1, Pin P1-22 + RPI_GPIO_P1_23 = 11, ///< Version 1, Pin P1-23, CLK when SPI0 in use + RPI_GPIO_P1_24 = 8, ///< Version 1, Pin P1-24, CE0 when SPI0 in use + RPI_GPIO_P1_26 = 7, ///< Version 1, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2 + RPI_V2_GPIO_P1_03 = 2, ///< Version 2, Pin P1-03 + RPI_V2_GPIO_P1_05 = 3, ///< Version 2, Pin P1-05 + RPI_V2_GPIO_P1_07 = 4, ///< Version 2, Pin P1-07 + RPI_V2_GPIO_P1_08 = 14, ///< Version 2, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_V2_GPIO_P1_10 = 15, ///< Version 2, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_V2_GPIO_P1_11 = 17, ///< Version 2, Pin P1-11 + RPI_V2_GPIO_P1_12 = 18, ///< Version 2, Pin P1-12, can be PWM channel 0 in ALT FUN 5 + RPI_V2_GPIO_P1_13 = 27, ///< Version 2, Pin P1-13 + RPI_V2_GPIO_P1_15 = 22, ///< Version 2, Pin P1-15 + RPI_V2_GPIO_P1_16 = 23, ///< Version 2, Pin P1-16 + RPI_V2_GPIO_P1_18 = 24, ///< Version 2, Pin P1-18 + RPI_V2_GPIO_P1_19 = 10, ///< Version 2, Pin P1-19, MOSI when SPI0 in use + RPI_V2_GPIO_P1_21 = 9, ///< Version 2, Pin P1-21, MISO when SPI0 in use + RPI_V2_GPIO_P1_22 = 25, ///< Version 2, Pin P1-22 + RPI_V2_GPIO_P1_23 = 11, ///< Version 2, Pin P1-23, CLK when SPI0 in use + RPI_V2_GPIO_P1_24 = 8, ///< Version 2, Pin P1-24, CE0 when SPI0 in use + RPI_V2_GPIO_P1_26 = 7, ///< Version 2, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2, new plug P5 + RPI_V2_GPIO_P5_03 = 28, ///< Version 2, Pin P5-03 + RPI_V2_GPIO_P5_04 = 29, ///< Version 2, Pin P5-04 + RPI_V2_GPIO_P5_05 = 30, ///< Version 2, Pin P5-05 + RPI_V2_GPIO_P5_06 = 31, ///< Version 2, Pin P5-06 + +} RPiGPIOPin; + +// Defines for SPI +// GPIO register offsets from BCM2835_SPI0_BASE. +// Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map +#define BCM2835_SPI0_CS 0x0000 ///< SPI Master Control and Status +#define BCM2835_SPI0_FIFO 0x0004 ///< SPI Master TX and RX FIFOs +#define BCM2835_SPI0_CLK 0x0008 ///< SPI Master Clock Divider +#define BCM2835_SPI0_DLEN 0x000c ///< SPI Master Data Length +#define BCM2835_SPI0_LTOH 0x0010 ///< SPI LOSSI mode TOH +#define BCM2835_SPI0_DC 0x0014 ///< SPI DMA DREQ Controls + +// Register masks for SPI0_CS +#define BCM2835_SPI0_CS_LEN_LONG 0x02000000 ///< Enable Long data word in Lossi mode if DMA_LEN is set +#define BCM2835_SPI0_CS_DMA_LEN 0x01000000 ///< Enable DMA mode in Lossi mode +#define BCM2835_SPI0_CS_CSPOL2 0x00800000 ///< Chip Select 2 Polarity +#define BCM2835_SPI0_CS_CSPOL1 0x00400000 ///< Chip Select 1 Polarity +#define BCM2835_SPI0_CS_CSPOL0 0x00200000 ///< Chip Select 0 Polarity +#define BCM2835_SPI0_CS_RXF 0x00100000 ///< RXF - RX FIFO Full +#define BCM2835_SPI0_CS_RXR 0x00080000 ///< RXR RX FIFO needs Reading ( full) +#define BCM2835_SPI0_CS_TXD 0x00040000 ///< TXD TX FIFO can accept Data +#define BCM2835_SPI0_CS_RXD 0x00020000 ///< RXD RX FIFO contains Data +#define BCM2835_SPI0_CS_DONE 0x00010000 ///< Done transfer Done +#define BCM2835_SPI0_CS_TE_EN 0x00008000 ///< Unused +#define BCM2835_SPI0_CS_LMONO 0x00004000 ///< Unused +#define BCM2835_SPI0_CS_LEN 0x00002000 ///< LEN LoSSI enable +#define BCM2835_SPI0_CS_REN 0x00001000 ///< REN Read Enable +#define BCM2835_SPI0_CS_ADCS 0x00000800 ///< ADCS Automatically Deassert Chip Select +#define BCM2835_SPI0_CS_INTR 0x00000400 ///< INTR Interrupt on RXR +#define BCM2835_SPI0_CS_INTD 0x00000200 ///< INTD Interrupt on Done +#define BCM2835_SPI0_CS_DMAEN 0x00000100 ///< DMAEN DMA Enable +#define BCM2835_SPI0_CS_TA 0x00000080 ///< Transfer Active +#define BCM2835_SPI0_CS_CSPOL 0x00000040 ///< Chip Select Polarity +#define BCM2835_SPI0_CS_CLEAR 0x00000030 ///< Clear FIFO Clear RX and TX +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX +#define BCM2835_SPI0_CS_CPOL 0x00000008 ///< Clock Polarity +#define BCM2835_SPI0_CS_CPHA 0x00000004 ///< Clock Phase +#define BCM2835_SPI0_CS_CS 0x00000003 ///< Chip Select + +/// \brief bcm2835SPIBitOrder SPI Bit order +/// Specifies the SPI data bit ordering for bcm2835_spi_setBitOrder() +typedef enum +{ + BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, ///< LSB First + BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 ///< MSB First +}bcm2835SPIBitOrder; + +/// \brief SPI Data mode +/// Specify the SPI data mode to be passed to bcm2835_spi_setDataMode() +typedef enum +{ + BCM2835_SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0 + BCM2835_SPI_MODE1 = 1, ///< CPOL = 0, CPHA = 1 + BCM2835_SPI_MODE2 = 2, ///< CPOL = 1, CPHA = 0 + BCM2835_SPI_MODE3 = 3, ///< CPOL = 1, CPHA = 1 +}bcm2835SPIMode; + +/// \brief bcm2835SPIChipSelect +/// Specify the SPI chip select pin(s) +typedef enum +{ + // Can also use non-standard pins with this lib + BCM2835_SPI_CS0 = 0, ///< Chip Select 0 + BCM2835_SPI_CS1 = 1, ///< Chip Select 1 + BCM2835_SPI_CS2 = 2, ///< Chip Select 2 (ie pins CS1 and CS2 are asserted) + BCM2835_SPI_CS_NONE = 3, ///< No CS, control it yourself + + // Only GPIO > 3 can be used (to not interfere with the previous value just above ) + // Lucky we have plenty of theese pins + BCM2835_SPI_CS_GPIO4 = RPI_V2_GPIO_P1_07, /// BCM GPIO 4 + BCM2835_SPI_CS_GPIO17 = RPI_V2_GPIO_P1_11, /// BCM GPIO 17 + BCM2835_SPI_CS_GPIO18 = RPI_V2_GPIO_P1_12, /// BCM GPIO 18 + BCM2835_SPI_CS_GPIO22 = RPI_V2_GPIO_P1_15, /// BCM GPIO 22 + BCM2835_SPI_CS_GPIO23 = RPI_V2_GPIO_P1_16, /// BCM GPIO 23 + BCM2835_SPI_CS_GPIO24 = RPI_V2_GPIO_P1_18, /// BCM GPIO 24 + BCM2835_SPI_CS_GPIO25 = RPI_V2_GPIO_P1_22, /// BCM GPIO 25 + BCM2835_SPI_CS_GPIO28 = RPI_V2_GPIO_P5_03, /// BCM GPIO 28 + BCM2835_SPI_CS_GPIO29 = RPI_V2_GPIO_P5_04, /// BCM GPIO 29 + BCM2835_SPI_CS_GPIO30 = RPI_V2_GPIO_P5_05, /// BCM GPIO 30 + BCM2835_SPI_CS_GPIO31 = RPI_V2_GPIO_P5_06, /// BCM GPIO 31 + +} bcm2835SPIChipSelect; + +/// \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 +/// It is reported that (contrary to the documentation) any even divider may used. +/// The frequencies shown for each divider have been confirmed by measurement +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.5625MHz + 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 +} bcm2835SPIClockDivider; + +/// \brief bcm2835SPISpeed +/// Specifies the divider used to generate the SPI clock from the system clock. +/// Figures below give the clock speed instead of clock divider. +#define BCM2835_SPI_SPEED_64MHZ BCM2835_SPI_CLOCK_DIVIDER_4 +#define BCM2835_SPI_SPEED_32MHZ BCM2835_SPI_CLOCK_DIVIDER_8 +#define BCM2835_SPI_SPEED_16MHZ BCM2835_SPI_CLOCK_DIVIDER_16 +#define BCM2835_SPI_SPEED_8MHZ BCM2835_SPI_CLOCK_DIVIDER_32 +#define BCM2835_SPI_SPEED_4MHZ BCM2835_SPI_CLOCK_DIVIDER_64 +#define BCM2835_SPI_SPEED_2MHZ BCM2835_SPI_CLOCK_DIVIDER_128 +#define BCM2835_SPI_SPEED_1MHZ BCM2835_SPI_CLOCK_DIVIDER_256 +#define BCM2835_SPI_SPEED_512KHZ BCM2835_SPI_CLOCK_DIVIDER_512 +#define BCM2835_SPI_SPEED_256KHZ BCM2835_SPI_CLOCK_DIVIDER_1024 +#define BCM2835_SPI_SPEED_128KHZ BCM2835_SPI_CLOCK_DIVIDER_2048 +#define BCM2835_SPI_SPEED_64KHZ BCM2835_SPI_CLOCK_DIVIDER_4096 +#define BCM2835_SPI_SPEED_32KHZ BCM2835_SPI_CLOCK_DIVIDER_8192 +#define BCM2835_SPI_SPEED_16KHZ BCM2835_SPI_CLOCK_DIVIDER_16384 +#define BCM2835_SPI_SPEED_8KHZ BCM2835_SPI_CLOCK_DIVIDER_32768 + +// Defines for I2C +// GPIO register offsets from BCM2835_BSC*_BASE. +// Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map +#define BCM2835_BSC_C 0x0000 ///< BSC Master Control +#define BCM2835_BSC_S 0x0004 ///< BSC Master Status +#define BCM2835_BSC_DLEN 0x0008 ///< BSC Master Data Length +#define BCM2835_BSC_A 0x000c ///< BSC Master Slave Address +#define BCM2835_BSC_FIFO 0x0010 ///< BSC Master Data FIFO +#define BCM2835_BSC_DIV 0x0014 ///< BSC Master Clock Divider +#define BCM2835_BSC_DEL 0x0018 ///< BSC Master Data Delay +#define BCM2835_BSC_CLKT 0x001c ///< BSC Master Clock Stretch Timeout + +// Register masks for BSC_C +#define BCM2835_BSC_C_I2CEN 0x00008000 ///< I2C Enable, 0 = disabled, 1 = enabled +#define BCM2835_BSC_C_INTR 0x00000400 ///< Interrupt on RX +#define BCM2835_BSC_C_INTT 0x00000200 ///< Interrupt on TX +#define BCM2835_BSC_C_INTD 0x00000100 ///< Interrupt on DONE +#define BCM2835_BSC_C_ST 0x00000080 ///< Start transfer, 1 = Start a new transfer +#define BCM2835_BSC_C_CLEAR_1 0x00000020 ///< Clear FIFO Clear +#define BCM2835_BSC_C_CLEAR_2 0x00000010 ///< Clear FIFO Clear +#define BCM2835_BSC_C_READ 0x00000001 ///< Read transfer + +// Register masks for BSC_S +#define BCM2835_BSC_S_CLKT 0x00000200 ///< Clock stretch timeout +#define BCM2835_BSC_S_ERR 0x00000100 ///< ACK error +#define BCM2835_BSC_S_RXF 0x00000080 ///< RXF FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_TXE 0x00000040 ///< TXE FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_RXD 0x00000020 ///< RXD FIFO contains data +#define BCM2835_BSC_S_TXD 0x00000010 ///< TXD FIFO can accept data +#define BCM2835_BSC_S_RXR 0x00000008 ///< RXR FIFO needs reading (full) +#define BCM2835_BSC_S_TXW 0x00000004 ///< TXW FIFO needs writing (full) +#define BCM2835_BSC_S_DONE 0x00000002 ///< Transfer DONE +#define BCM2835_BSC_S_TA 0x00000001 ///< Transfer Active + +#define BCM2835_BSC_FIFO_SIZE 16 ///< BSC FIFO size + +/// \brief bcm2835I2CClockDivider +/// Specifies the divider used to generate the I2C clock from the system clock. +/// Clock divided is based on nominal base clock rate of 250MHz +typedef enum +{ + BCM2835_I2C_CLOCK_DIVIDER_2500 = 2500, ///< 2500 = 10us = 100 kHz + BCM2835_I2C_CLOCK_DIVIDER_626 = 626, ///< 622 = 2.504us = 399.3610 kHz + BCM2835_I2C_CLOCK_DIVIDER_150 = 150, ///< 150 = 60ns = 1.666 MHz (default at reset) + BCM2835_I2C_CLOCK_DIVIDER_148 = 148, ///< 148 = 59ns = 1.689 MHz +} bcm2835I2CClockDivider; + +/// \brief bcm2835I2CReasonCodes +/// Specifies the reason codes for the bcm2835_i2c_write and bcm2835_i2c_read functions. +typedef enum +{ + BCM2835_I2C_REASON_OK = 0x00, ///< Success + BCM2835_I2C_REASON_ERROR_NACK = 0x01, ///< Received a NACK + BCM2835_I2C_REASON_ERROR_CLKT = 0x02, ///< Received Clock Stretch Timeout + BCM2835_I2C_REASON_ERROR_DATA = 0x04, ///< Not all data is sent / received +} bcm2835I2CReasonCodes; + +// Defines for ST +// GPIO register offsets from BCM2835_ST_BASE. +// Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers +// The System Timer peripheral provides four 32-bit timer channels and a single 64-bit free running counter. +// BCM2835_ST_CLO is the System Timer Counter Lower bits register. +// The system timer free-running counter lower register is a read-only register that returns the current value +// of the lower 32-bits of the free running counter. +// BCM2835_ST_CHI is the System Timer Counter Upper bits register. +// The system timer free-running counter upper register is a read-only register that returns the current value +// of the upper 32-bits of the free running counter. +#define BCM2835_ST_CS 0x0000 ///< System Timer Control/Status +#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 +#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 +#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 +#define BCM2835_PWM1_USEFIFO 0x2000 ///< Data from FIFO +#define BCM2835_PWM1_REVPOLAR 0x1000 ///< Reverse polarity +#define BCM2835_PWM1_OFFSTATE 0x0800 ///< Ouput Off state +#define BCM2835_PWM1_REPEATFF 0x0400 ///< Repeat last value if FIFO empty +#define BCM2835_PWM1_SERIAL 0x0200 ///< Run in serial mode +#define BCM2835_PWM1_ENABLE 0x0100 ///< Channel Enable + +#define BCM2835_PWM0_MS_MODE 0x0080 ///< Run in Mark/Space mode +#define BCM2835_PWM_CLEAR_FIFO 0x0040 ///< Clear FIFO +#define BCM2835_PWM0_USEFIFO 0x0020 ///< Data from FIFO +#define BCM2835_PWM0_REVPOLAR 0x0010 ///< Reverse polarity +#define BCM2835_PWM0_OFFSTATE 0x0008 ///< Ouput Off state +#define BCM2835_PWM0_REPEATFF 0x0004 ///< Repeat last value if FIFO empty +#define BCM2835_PWM0_SERIAL 0x0002 ///< Run in serial mode +#define BCM2835_PWM0_ENABLE 0x0001 ///< Channel Enable + + +/// \brief bcm2835PWMClockDivider +/// Specifies the divider used to generate the PWM clock from the system clock. +/// Figures below give the divider, clock period and clock frequency. +/// Clock divided is based on nominal PWM base clock rate of 19.2MHz +/// The frequencies shown for each divider have been confirmed by measurement +typedef enum +{ + BCM2835_PWM_CLOCK_DIVIDER_32768 = 32768, ///< 32768 = 585Hz + BCM2835_PWM_CLOCK_DIVIDER_16384 = 16384, ///< 16384 = 1171.8Hz + BCM2835_PWM_CLOCK_DIVIDER_8192 = 8192, ///< 8192 = 2.34375kHz + BCM2835_PWM_CLOCK_DIVIDER_4096 = 4096, ///< 4096 = 4.6875kHz + BCM2835_PWM_CLOCK_DIVIDER_2048 = 2048, ///< 2048 = 9.375kHz + BCM2835_PWM_CLOCK_DIVIDER_1024 = 1024, ///< 1024 = 18.75kHz + BCM2835_PWM_CLOCK_DIVIDER_512 = 512, ///< 512 = 37.5kHz + BCM2835_PWM_CLOCK_DIVIDER_256 = 256, ///< 256 = 75kHz + BCM2835_PWM_CLOCK_DIVIDER_128 = 128, ///< 128 = 150kHz + BCM2835_PWM_CLOCK_DIVIDER_64 = 64, ///< 64 = 300kHz + BCM2835_PWM_CLOCK_DIVIDER_32 = 32, ///< 32 = 600.0kHz + BCM2835_PWM_CLOCK_DIVIDER_16 = 16, ///< 16 = 1.2MHz + BCM2835_PWM_CLOCK_DIVIDER_8 = 8, ///< 8 = 2.4MHz + BCM2835_PWM_CLOCK_DIVIDER_4 = 4, ///< 4 = 4.8MHz + BCM2835_PWM_CLOCK_DIVIDER_2 = 2, ///< 2 = 9.6MHz, fastest you can get + 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) +#define delayMicroseconds(x) bcm2835_delayMicroseconds(x) +#define millis() bcm2835_millis() +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /// \defgroup init Library initialisation and management + /// These functions allow you to intialise and control the bcm2835 library + /// @{ + + /// Initialise the library by opening /dev/mem and getting pointers to the + /// internal memory for BCM 2835 device registers. You must call this (successfully) + /// before calling any other + /// functions in this library (except bcm2835_set_debug). + /// If bcm2835_init() fails by returning 0, + /// calling any other function may result in crashes or other failures. + /// Prints messages to stderr in case of errors. + /// \return 1 if successful else 0 + extern int bcm2835_init(void); + + /// Close the library, deallocating any allocated memory and closing /dev/mem + /// \return 1 if successful else 0 + extern int bcm2835_close(void); + + /// Sets the debug level of the library. + /// A value of 1 prevents mapping to /dev/mem, and makes the library print out + /// what it would do, rather than accessing the GPIO registers. + /// A value of 0, the default, causes normal operation. + /// Call this before calling bcm2835_init(); + /// \param[in] debug The new debug level. 1 means debug + extern void bcm2835_set_debug(uint8_t debug); + + /// @} // end of init + + /// \defgroup lowlevel Low level register access + /// These functions provide low level register access, and should not generally + /// need to be used + /// + /// @{ + + /// Reads 32 bit value from a peripheral address + /// The read is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr); + + + /// Reads 32 bit value from a peripheral address without the read barrier + /// You should only use this when your code has previously called bcm2835_peri_read() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr); + + + /// Writes 32 bit value from a peripheral address + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value); + + /// Writes 32 bit value from a peripheral address without the write barrier + /// You should only use this when your code has previously called bcm2835_peri_write() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); + + /// Alters a number of bits in a 32 peripheral regsiter. + /// It reads the current valu and then alters the bits deines as 1 in mask, + /// according to the bit value in value. + /// All other bits that are 0 in the mask are unaffected. + /// Use this to alter a subset of the bits in a register. + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write, masked in by mask. + /// \param[in] mask Bitmask that defines the bits that will be altered in the register. + /// \sa Physical Addresses + extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); + /// @} // end of lowlevel + + /// \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. + /// @{ + + /// Sets the Function Select register for the given pin, which configures + /// the pin as Input, Output or one of the 6 alternate functions. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect + extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); + + /// Sets the specified pin output to + /// HIGH. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_set(uint8_t pin); + + /// Sets the specified pin output to + /// LOW. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_clr(uint8_t pin); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// HIGH. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_set_multi(uint32_t mask); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// LOW. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_clr_multi(uint32_t mask); + + /// Reads the current level on the specified + /// pin and returns either HIGH or LOW. Works whether or not the pin + /// is an input or an output. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return the current level either HIGH or LOW + extern uint8_t bcm2835_gpio_lev(uint8_t pin); + + /// Event Detect Status. + /// Tests whether the specified pin has detected a level or edge + /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), + /// bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). + /// Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return HIGH if the event detect status for the given pin is true. + extern uint8_t bcm2835_gpio_eds(uint8_t pin); + + /// Sets the Event Detect Status register for a given pin to 1, + /// which has the effect of clearing the flag. Use this afer seeing + /// an Event Detect Status on the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_set_eds(uint8_t pin); + + /// Enable Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?011? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_ren(uint8_t pin); + + /// Disable Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_ren(uint8_t pin); + + /// Enable Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?100? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_fen(uint8_t pin); + + /// Disable Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_fen(uint8_t pin); + + /// Enable High Detect Enable for the specified pin. + /// When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_hen(uint8_t pin); + + /// Disable High Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_hen(uint8_t pin); + + /// Enable Low Detect Enable for the specified pin. + /// When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_len(uint8_t pin); + + /// Disable Low Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_len(uint8_t pin); + + /// Enable Asynchronous Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// rising edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_aren(uint8_t pin); + + /// Disable Asynchronous Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_aren(uint8_t pin); + + /// Enable Asynchronous Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// falling edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_afen(uint8_t pin); + + /// Disable Asynchronous Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_afen(uint8_t pin); + + /// Sets the Pull-up/down register for the given pin. This is + /// 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 + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pud(uint8_t pud); + + /// Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. + /// \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. + /// \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. + /// \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. + /// \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 + /// is automatically included. + extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); + + /// Delays for the specified number of milliseconds. + /// Uses nanosleep(), and therefore does not use CPU until the time is up. + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// \param[in] millis Delay in milliseconds + extern void bcm2835_delay (unsigned int millis); + + /// Delays for the specified number of microseconds. + /// Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers, + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// For times less than about 450 microseconds, uses a busy wait on the System Timer. + /// It is reported that a delay of 0 microseconds on RaspberryPi will in fact + /// result in a delay of about 80 microseconds. Your mileage may vary. + /// \param[in] micros Delay in microseconds + extern void bcm2835_delayMicroseconds (uint64_t micros); + + /// Indicate the number of milliseconds since startup of PI + /// This function is like the Arduino millis function + /// \return Number of milliseconds + extern unsigned int bcm2835_millis(void); + + /// Sets the output state of the specified pin + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write(uint8_t pin, uint8_t on); + + /// Sets any of the first 32 GPIO output pins specified in the mask to the state given by on + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on); + + /// Sets the first 32 GPIO output pins specified in the mask to the value given by value + /// \param[in] value values required for each bit masked in by mask, eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask); + + /// Sets the Pull-up/down mode for the specified pin. This is more convenient than + /// clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk(). + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); + + /// @} + + /// \defgroup spi SPI access + /// These functions let you use SPI0 (Serial Peripheral Interface) to + /// interface with an external SPI device. + /// @{ + + /// Start SPI operations. + /// Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// to alternate function ALT0, which enables those pins for SPI interface. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// by default PI hardware driven using CE0 + /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to + /// their default functions + /// \sa bcm2835_spi_end() + extern void bcm2835_spi_begin(uint8_t cs); + + /// End SPI operations. + /// SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// are returned to their default INPUT behaviour. + extern void bcm2835_spi_end(void); + + /// Sets the SPI custom Chip Select pin to correct level + /// Defaults to + /// \param[in] level The desired level, LOW or HIGH, + extern void bcm2835_spi_setChipSelect(uint8_t level); + + /// Sets the SPI bit order + /// NOTE: has no effect. Not supported by SPI0. + /// Defaults to + /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, + /// see \ref bcm2835SPIBitOrder + extern void bcm2835_spi_setBitOrder(uint8_t order); + + /// Sets the SPI clock divider and therefore the + /// SPI clock speed. + /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, + /// see \ref bcm2835SPIClockDivider + extern void bcm2835_spi_setClockDivider(uint16_t divider); + + /// Sets the SPI data mode + /// Sets the clock polariy and phase + /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, + /// see \ref bcm2835SPIMode + extern void bcm2835_spi_setDataMode(uint8_t mode); + + /// Sets the chip select pin(s) + /// When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the + /// transfer. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + extern void bcm2835_spi_chipSelect(uint8_t cs); + + /// Sets the chip select pin polarity for a given pin + /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) + /// will be asserted to the + /// value given by active. When transfers are not happening, the chip select pin(s) + /// return to the complement (inactive) value. + /// \param[in] cs The chip select pin to affect + /// \param[in] active Whether the chip select pin is to be active HIGH + extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); + + /// Transfers one byte 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] value The 8 bit data byte to write to MOSI + /// \return The 8 bit byte simultaneously read from MISO + /// \sa bcm2835_spi_transfern() + extern uint8_t bcm2835_spi_transfer(uint8_t value); + + /// Transfers any number of bytes 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 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 + /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + /// \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 + /// \sa bcm2835_spi_transfer() + extern void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len); + + /// Transfers any number of bytes to and from the currently selected SPI slave + /// using bcm2835_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_spi_transfer() + extern void bcm2835_spi_transfern(char* buf, uint32_t len); + + /// Transfers any number of bytes to the currently selected SPI slave. + /// 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 tbuf buffer, and the number of bytes to send + extern void bcm2835_spi_writenb(char* buf, uint32_t len); + + /// @} + + /// \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. + /// @{ + + /// Start I2C operations. + /// Forces RPi I2C pins P1-03 (SDA) and P1-05 (SCL) + /// to alternate function ALT0, which enables those pins for I2C interface. + /// You should call bcm2835_i2c_end() when all I2C functions are complete to return the pins to + /// their default functions + /// \sa bcm2835_i2c_end() + extern void bcm2835_i2c_begin(void); + + /// End I2C operations. + /// I2C pins P1-03 (SDA) and P1-05 (SCL) + /// are returned to their default INPUT behaviour. + extern void bcm2835_i2c_end(void); + + /// Sets the I2C slave address. + /// \param[in] addr The I2C slave address. + extern void bcm2835_i2c_setSlaveAddress(uint8_t addr); + + /// Sets the I2C clock divider and therefore the I2C clock speed. + /// \param[in] divider The desired I2C clock divider, one of BCM2835_I2C_CLOCK_DIVIDER_*, + /// see \ref bcm2835I2CClockDivider + extern void bcm2835_i2c_setClockDivider(uint16_t divider); + + /// Sets the I2C clock divider by converting the baudrate parameter to + /// the equivalent I2C clock divider. ( see \sa bcm2835_i2c_setClockDivider) + /// For the I2C standard 100khz you would set baudrate to 100000 + /// The use of baudrate corresponds to its use in the I2C kernel device + /// driver. (Of course, bcm2835 has nothing to do with the kernel driver) + extern void bcm2835_i2c_set_baudrate(uint32_t baudrate); + + /// Transfers any number of bytes to the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \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. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_write(const char * buf, uint32_t len); + + /// Transfers any number of bytes from the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read(char* buf, uint32_t len); + + /// Allows reading from I2C slaves that require a repeated start (without any prior stop) + /// to read after the required slave register has been set. For example, the popular + /// MPL3115A2 pressure and temperature sensor. Note that your device must support or + /// require this mode. If your device does not require this mode then the standard + /// combined: + /// \sa bcm2835_i2c_write + /// \sa bcm2835_i2c_read + /// are a better choice. + /// Will read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + /// \param[in] regaddr Buffer containing the slave register you wish to read from. + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len); + + /// Allows sending an arbitrary number of bytes to I2C slaves before issuing a repeated + /// start (with no prior stop) and reading a response. + /// Necessary for devices that require such behavior, such as the MLX90620. + /// Will write to and read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + /// \param[in] cmds Buffer containing the bytes to send before the repeated start condition. + /// \param[in] cmds_len Number of bytes to send from cmds buffer + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] buf_len Number of bytes to receive in the buf buffer. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len); + + /// @} + + /// \defgroup st System Timer access + /// Allows access to and delays using the System Timer Counter. + /// @{ + + /// Read the System Timer Counter register. + /// \return the value read from the System Timer Counter Lower 32 bits register + extern uint64_t bcm2835_st_read(void); + + /// Delays for the specified number of microseconds with offset. + /// \param[in] offset_micros Offset in microseconds + /// \param[in] micros Delay in microseconds + extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); + + /// @} + + /// \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 + /// documentation on the Main Page. + /// @{ + + /// Sets the PWM clock divisor, + /// to control the basic PWM pulse widths. + /// \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common + /// values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider + extern void bcm2835_pwm_set_clock(uint32_t divisor); + + /// Sets the mode of the given PWM channel, + /// allowing you to control the PWM mode and enable/disable that channel + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode. + /// \param[in] enabled Set true to enable this channel and produce PWM pulses. + extern void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled); + + /// Sets the maximum range of the PWM output. + /// The data value can vary between 0 and this range to control PWM output + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] range The maximum value permitted for DATA. + extern void bcm2835_pwm_set_range(uint8_t channel, uint32_t range); + + /// Sets the PWM pulse ratio to emit to DATA/RANGE, + /// where RANGE is set by bcm2835_pwm_set_range(). + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] data Controls the PWM output ratio as a fraction of the range. + /// Can vary from 0 to RANGE. + extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data); + + /// @} +#ifdef __cplusplus +} +#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. diff --git a/RPi/RF24/examples/Makefile b/RPi/RF24/examples/Makefile new file mode 100644 index 0000000..73da217 --- /dev/null +++ b/RPi/RF24/examples/Makefile @@ -0,0 +1,40 @@ +############################################################################# +# +# Makefile for librf24 examples on Raspberry Pi +# +# License: GPL (General Public License) +# Author: gnulnulf +# Date: 2013/02/07 (version 1.0) +# +# Description: +# ------------ +# use make all and make install to install the examples +# You can change the install directory by editing the prefix line +# +prefix := /usr/local + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s +#CCFLAGS= + +# define all programs +#PROGRAMS = scanner pingtest gettingstarted +PROGRAMS = gettingstarted gettingstarted_call_response transfer pingpair_dyn +SOURCES = ${PROGRAMS:=.cpp} + +all: ${PROGRAMS} + +${PROGRAMS}: ${SOURCES} + g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ + +clean: + rm -rf $(PROGRAMS) + +install: all + test -d $(prefix) || mkdir $(prefix) + test -d $(prefix)/bin || mkdir $(prefix)/bin + for prog in $(PROGRAMS); do \ + install -m 0755 $$prog $(prefix)/bin; \ + done + +.PHONY: install diff --git a/RPi/RF24/examples/extra/Makefile b/RPi/RF24/examples/extra/Makefile new file mode 100644 index 0000000..c313b5b --- /dev/null +++ b/RPi/RF24/examples/extra/Makefile @@ -0,0 +1,40 @@ +############################################################################# +# +# Makefile for librf24 examples on Raspberry Pi +# +# License: GPL (General Public License) +# Author: gnulnulf +# Date: 2013/02/07 (version 1.0) +# +# Description: +# ------------ +# use make all and make install to install the examples +# You can change the install directory by editing the prefix line +# +prefix := /usr/local + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s +#CCFLAGS= + +# define all programs +PROGRAMS = rpi-hub scanner + +SOURCES = ${PROGRAMS:=.cpp} + +all: ${PROGRAMS} + +${PROGRAMS}: ${SOURCES} + g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ + +clean: + rm -rf $(PROGRAMS) + +install: all + test -d $(prefix) || mkdir $(prefix) + test -d $(prefix)/bin || mkdir $(prefix)/bin + for prog in $(PROGRAMS); do \ + install -m 0755 $$prog $(prefix)/bin; \ + done + +.PHONY: install diff --git a/RPi/RF24/examples/extra/rpi-hub.cpp b/RPi/RF24/examples/extra/rpi-hub.cpp new file mode 100644 index 0000000..4785148 --- /dev/null +++ b/RPi/RF24/examples/extra/rpi-hub.cpp @@ -0,0 +1,134 @@ +/* + * + * Filename : rpi-hub.cpp + * + * This program makes the RPi as a hub listening to all six pipes from the remote sensor nodes ( usually Arduino ) + * and will return the packet back to the sensor on pipe0 so that the sender can calculate the round trip delays + * when the payload matches. + * + * I encounter that at times, it also receive from pipe7 ( or pipe0 ) with content of FFFFFFFFF that I will not sent + * back to the sender + * + * Refer to RF24/examples/rpi_hub_arduino/ for the corresponding Arduino sketches to work with this code. + * + * + * CE is not used and CSN is GPIO25 (not pinout) + * + * Refer to RPi docs for GPIO numbers + * + * Author : Stanley Seow + * e-mail : stanleyseow@gmail.com + * date : 6th Mar 2013 + * + * 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + * Modified to use with Arduipi board http://hallard.me/arduipi + * Changed to use modified bcm2835 and RF24 library + * + * + */ + +#include +#include +#include "../RF24.h" + +using namespace std; + +// Radio pipe addresses for the 2 nodes to communicate. +// First pipe is for writing, 2nd, 3rd, 4th, 5th & 6th is for reading... +const uint64_t pipes[6] = + { 0xF0F0F0F0D2LL, 0xF0F0F0F0E1LL, + 0xF0F0F0F0E2LL, 0xF0F0F0F0E3LL, + 0xF0F0F0F0F1, 0xF0F0F0F0F2 + }; + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +int main(int argc, char** argv) +{ + uint8_t len; + + // Refer to RF24.h or nRF24L01 DS for settings + radio.begin(); + radio.enableDynamicPayloads(); + radio.setAutoAck(1); + radio.setRetries(15,15); + radio.setDataRate(RF24_1MBPS); + radio.setPALevel(RF24_PA_MAX); + radio.setChannel(76); + radio.setCRCLength(RF24_CRC_16); + + // Open 6 pipes for readings ( 5 plus pipe0, also can be used for reading ) + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + radio.openReadingPipe(2,pipes[2]); + radio.openReadingPipe(3,pipes[3]); + radio.openReadingPipe(4,pipes[4]); + radio.openReadingPipe(5,pipes[5]); + + // + // Start listening + // + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + radio.printDetails(); + + printf("Output below : \n"); + delay(1); + + while(1) + { + char receivePayload[32]; + uint8_t pipe = 1; + + // Start listening + radio.startListening(); + + while ( radio.available(&pipe) ) + { + len = radio.getDynamicPayloadSize(); + radio.read( receivePayload, len ); + + // Display it on screen + printf("Recv: size=%i payload=%s pipe=%i",len,receivePayload,pipe); + + // Send back payload to sender + radio.stopListening(); + + // if pipe is 7, do not send it back + if ( pipe != 7 ) + { + radio.write(receivePayload,len); + receivePayload[len]=0; + printf("\t Send: size=%i payload=%s pipe:%i\n",len,receivePayload,pipe); + } + else + { + printf("\n"); + } + + pipe++; + + // reset pipe to 0 + if ( pipe > 6 ) + pipe = 0; + } + + delayMicroseconds(20); + } + + return 0; +} + diff --git a/RPi/RF24/examples/extra/scanner.cpp b/RPi/RF24/examples/extra/scanner.cpp new file mode 100644 index 0000000..1390495 --- /dev/null +++ b/RPi/RF24/examples/extra/scanner.cpp @@ -0,0 +1,147 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 and RF24 library + + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include +#include "../RF24.h" + +using namespace std; + +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +// +// Channel info +// +//const uint8_t num_channels = 128; +const uint8_t num_channels = 120; +uint8_t values[num_channels]; + + +const int num_reps = 100; +int reset_array=0; + + +int main(int argc, char** argv) +{ + // + // Print preamble + // + + //Serial.begin(57600); + //printf_begin(); + printf("RF24/examples/scanner/\n"); + + // + // Setup and configure rf radio + // + radio.begin(); + + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + radio.printDetails(); + + // Print out header, high then low digit + int i = 0; + + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n"); + + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n"); + + // forever loop + while(1) + { + if ( reset_array == 1 ) + { + // Clear measurement values + memset(values,0,sizeof(values)); + printf("\n"); + } + + // Scan all channels num_reps times + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(128); + //radio.stopListening(); + + // Did we get a carrier? + if ( radio.testCarrier() ) + ++values[i]; + if ( values[i] == 0xf ) + { + reset_array = 2; + } + radio.stopListening(); + } + + // Print out channel measurements, clamped to a single hex digit + i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,(values[i]&0xf))); + ++i; + } + + printf("\n"); + } + + return 0; +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RPi/RF24/examples/gettingstarted.cpp b/RPi/RF24/examples/gettingstarted.cpp new file mode 100644 index 0000000..d4aeaa3 --- /dev/null +++ b/RPi/RF24/examples/gettingstarted.cpp @@ -0,0 +1,188 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 and RF24 library +TMRh20 2014 - Updated to work with optimized RF24 Arduino library + + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class on RPi, communicating to an Arduino running + * the GettingStarted sketch. + */ + +#include +#include +#include +#include +#include "./RF24.h" + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; + +int main(int argc, char** argv){ + + + // Print preamble: + printf("RF24/examples/pingtest/\n"); + + // Setup and configure rf radio + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // Dump the configuration of the rf unit for debugging + radio.printDetails(); + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit) \n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + + } + + // forever loop + while (1) + { + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + + printf("Now sending...\n"); + unsigned long time = millis(); + + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (!ok){ + printf("failed.\n"); + } + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) { + // by bcatalin » Thu Feb 14, 2013 11:26 am + //delay(5); //add a small delay to let radio.available to check payload + if (millis() - started_waiting_at > 200 ) + timeout = true; + } + + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n",got_time,millis()-got_time); + } + + // Try again 1s later + // delay(1000); + sleep(1); + + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + //printf("Check available...\n"); + //delay(3); + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + + + // Fetch the payload, and see if this was the last one. + radio.read( &got_time, sizeof(unsigned long) ); + + radio.stopListening(); + //delay(1); + // Seem to need a delay, or the RPi is too quick + radio.write( &got_time, sizeof(unsigned long) ); + + //delay(1); + // Now, resume listening so we catch the next packets. + radio.startListening(); + //delay(1); + // Spew it + printf("Got payload(%d) %lu...\n",sizeof(unsigned long), got_time); + } + //delay(5); + } + } // forever loop + + return 0; +} + diff --git a/RPi/RF24/examples/gettingstarted_call_response.cpp b/RPi/RF24/examples/gettingstarted_call_response.cpp new file mode 100644 index 0000000..4444191 --- /dev/null +++ b/RPi/RF24/examples/gettingstarted_call_response.cpp @@ -0,0 +1,138 @@ +/* +TMRh20 2014 - Updated to work with optimized RF24 Arduino library +*/ + + +/** + * Example for efficient call-response using ack-payloads + * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. + * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to + * primary transmitter if wanting to initiate communication instead of respond to a commmunication. + */ + +#include +#include +#include +#include +#include "./RF24.h" + +using namespace std; + +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0, role = 0; + +uint8_t counter = 1; // A single byte to keep track of the data being sent back and forth + + +int main(int argc, char** argv){ + + + printf("RF24/examples/gettingstarted_call_response\n"); + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.enableAckPayload(); // Allow optional ack payloads + radio.setRetries(1,15); // Smallest time between retries, max no. of retries + radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed + //radio.powerUp(); + radio.printDetails(); // Dump the configuration of the rf unit for debugging + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit)\n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + + if ( role == role_ping_out ) { + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); + }else{ + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); + radio.startListening(); + } + +// forever loop +while (1){ + + +/****************** Ping Out Role ***************************/ + + if (role == role_ping_out){ // Radio is in ping mode + + uint8_t gotByte; // Initialize a variable for the incoming response + + radio.stopListening(); // First, stop listening so we can talk. + printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload + unsigned long time = millis(); // Record the current microsecond count + + if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio + if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank + printf("Got blank response. round-trip delay: %lu ms\n\r",millis()-time); + }else{ + while(radio.available() ){ // If an ack with payload was received + radio.read( &gotByte, 1 ); // Read it, and display the response time + printf("Got response %d, round-trip delay: %lu ms\n\r",gotByte,millis()-time); + counter++; // Increment the counter variable + } + } + + }else{ printf("Sending failed.\n\r"); } // If no ack response, sending failed + + sleep(1); // Try again later + } + +/****************** Pong Back Role ***************************/ + + if ( role == role_pong_back ) { + uint8_t pipeNo, gotByte; // Declare variables for the pipe and the byte received + while( radio.available(&pipeNo)){ // Read all available payloads + radio.read( &gotByte, 1 ); + // Since this is a call-response. Respond directly with an ack payload. + // Ack payloads are much more efficient than switching to transmit mode to respond to a call + radio.writeAckPayload(pipeNo,&gotByte, 1 ); // This can be commented out to send empty payloads. + printf("Sent response %d \n\r", gotByte); + } + } + +} //while 1 +} //main + + diff --git a/RPi/RF24/examples/pingpair_dyn.cpp b/RPi/RF24/examples/pingpair_dyn.cpp new file mode 100644 index 0000000..b7e4d70 --- /dev/null +++ b/RPi/RF24/examples/pingpair_dyn.cpp @@ -0,0 +1,181 @@ +/* + TMRh20 2014 - Optimized RF24 Library Fork +*/ + +/** + * Example using Dynamic Payloads + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +#include +#include +#include +#include +#include "./RF24.h" + + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; + +const int min_payload_size = 4; +const int max_payload_size = 32; +const int payload_size_increments_by = 1; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + +int main(int argc, char** argv){ + + + // Print preamble: + printf("RF24/examples/pingpair_dyn/\n"); + + // Setup and configure rf radio + radio.begin(); + radio.enableDynamicPayloads(); + radio.setRetries(5,15); + radio.printDetails(); + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit) \n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + } + + +// forever loop + while (1) + { + +if (role == role_ping_out) + { + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + printf("Now sending length %i...",next_payload_size); + radio.write( send_payload, next_payload_size ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 500 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + uint8_t len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got response size=%i value=%s\n\r",len,receive_payload); + } + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; + + // Try again 1s later + delay(100); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + uint8_t len; + + while (radio.available()) + { + // Fetch the payload, and see if this was the last one. + len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got payload size=%i value=%s\n\r",len,receive_payload); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( receive_payload, len ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +} + + diff --git a/RPi/RF24/examples/transfer.cpp b/RPi/RF24/examples/transfer.cpp new file mode 100644 index 0000000..c4e4ecb --- /dev/null +++ b/RPi/RF24/examples/transfer.cpp @@ -0,0 +1,148 @@ +/* +TMRh20 2014 + + 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. + */ + +/** General Data Transfer Rate Test + * This example demonstrates basic data transfer functionality with the + updated library. This example will display the transfer rates acheived using + the slower form of high-speed transfer using blocking-writes. + */ + +#include +#include +#include +#include +#include "./RF24.h" + + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; +uint8_t data[32]; +unsigned long startTime, stopTime, counter, rxTimer=0; + +int main(int argc, char** argv){ + + + // Print preamble: + + printf("RF24/examples/Transfer/\n"); + + radio.begin(); // Setup and configure rf radio + radio.setChannel(1); + radio.setPALevel(RF24_PA_MAX); + radio.setDataRate(RF24_2MBPS); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries + radio.setCRCLength(RF24_CRC_8); + radio.printDetails(); +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit)\n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + + if ( role == role_ping_out ) { + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); + } else { + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); + radio.startListening(); + } + + + for(int i=0; i<32; i++){ + data[i] = rand() % 255; //Load the buffer with random data + } + + // forever loop + while (1){ + + if (role == role_ping_out){ + sleep(2); + printf("Initiating Basic Data Transfer\n\r"); + + long int cycles = 10000; //Change this to a higher or lower number. + + startTime = millis(); + + for(int i=0; i 1000){ + rxTimer = millis(); + printf("Rate: "); + float numBytes = counter*32; + printf("%.2f KB/s \n\r",numBytes/1000); + printf("Payload Count: %lu \n\r", counter); + counter = 0; + } + } + +} // loop +} // main + + + + + + diff --git a/RPi/RF24/nRF24L01.h b/RPi/RF24/nRF24L01.h new file mode 100644 index 0000000..ba83534 --- /dev/null +++ b/RPi/RF24/nRF24L01.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2007 Stefan Engelke + Portions Copyright (C) 2011 Greg Copeland + + 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. +*/ + +/* Memory Map */ +#define CONFIG 0x00 +#define EN_AA 0x01 +#define EN_RXADDR 0x02 +#define SETUP_AW 0x03 +#define SETUP_RETR 0x04 +#define RF_CH 0x05 +#define RF_SETUP 0x06 +#define STATUS 0x07 +#define OBSERVE_TX 0x08 +#define CD 0x09 +#define RX_ADDR_P0 0x0A +#define RX_ADDR_P1 0x0B +#define RX_ADDR_P2 0x0C +#define RX_ADDR_P3 0x0D +#define RX_ADDR_P4 0x0E +#define RX_ADDR_P5 0x0F +#define TX_ADDR 0x10 +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D + +/* Bit Mnemonics */ +#define MASK_RX_DR 6 +#define MASK_TX_DS 5 +#define MASK_MAX_RT 4 +#define EN_CRC 3 +#define CRCO 2 +#define PWR_UP 1 +#define PRIM_RX 0 +#define ENAA_P5 5 +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW 0 +#define ARD 4 +#define ARC 0 +#define PLL_LOCK 4 +#define RF_DR 3 +#define RF_PWR 6 +#define RX_DR 6 +#define TX_DS 5 +#define MAX_RT 4 +#define RX_P_NO 1 +#define TX_FULL 0 +#define PLOS_CNT 4 +#define ARC_CNT 0 +#define TX_REUSE 6 +#define FIFO_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 + +/* Instruction Mnemonics */ +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define NOP 0xFF + +/* Non-P omissions */ +#define LNA_HCURR 0 + +/* P model memory Map */ +#define RPD 0x09 +#define W_TX_PAYLOAD_NO_ACK 0xB0 + +/* P model bit Mnemonics */ +#define RF_DR_LOW 5 +#define RF_DR_HIGH 3 +#define RF_PWR_LOW 1 +#define RF_PWR_HIGH 2 diff --git a/RPi/RF24/readme.md b/RPi/RF24/readme.md new file mode 100644 index 0000000..9c4ce74 --- /dev/null +++ b/RPi/RF24/readme.md @@ -0,0 +1,108 @@ + +Optimized Raspberry Pi RF24 and RF24 Network Libraries +============================================= + General Documentation: http://tmrh20.github.io + + Library functions are mostly the same. + See the included examples for RPi specific usage + +##Raspberry Pi - PreConfig + + +####Possible pre-configuration: + If SPI is not already enabled, load it on boot: +``` + sudo raspi-config +``` +A. Update the tool via the menu as required +B. Select Advanced and enable the SPI kernel module +C. Update other software and libraries: +``` + sudo apt-get update + sudo apt-get upgrade +``` +RPi - RF24 Quick-Start +====================== +A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: +``` + mkdir ~/rf24libs + cd ~/rf24libs +``` +B. Clone the RF24 Repo +``` + git clone https://github.com/tmrh20/RF24.git rtemp +``` +C. Copy the RPi library folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24 ./ + rm -r rtemp + cd RF24 +``` +D. Build the library, and run an example file: +``` + sudo make + cd examples + sudo make + sudo ./gettingstarted +``` + +RPi - RF24Network Quick-Start +============================= +A. Enter the same directory that contains the RF24 library folder +``` + cd ~/rf24libs +``` +B. Clone the RF24Network Repo +``` + git clone https://github.com/tmrh20/RF24Network.git ntemp +``` +C. Copy the RF24Network folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24Network ./ + rm -r ntemp + cd RF24Network +``` +D. Build the library +``` + sudo make + cd examples + sudo make + sudo ./helloworld_rx OR sudo ./helloworld_tx +``` + +Connection Info +=============== + +Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN + +**Constructor:** +``` +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); +``` + +**Pins:** + + |NRF24L01 | RPI | P1 Connector | + |----- |----------- | -------------| + |VCC: | rpi-3v3 | (17) | + |GND: | rpi-gnd | (25) | + |CE: | rpi-gpio22 | (15) | + |CSN: | rpi-gpio8 | (24) | + |SCK: | rpi-sckl | (23) | + |MOSI: | rpi-mosi | (19) | + |MISO: | rpi-miso | (21) | + + +**************** + + +Based on the arduino lib from J. Coliz . +the library was berryfied by Purinda Gunasekara . +then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi +Network lib also based on https://github.com/farconada/RF24Network + +Currently optimized and aligned with Arduino fork of libraries by TMRh20: +https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi +Documentation: http://tmrh20.github.io + + diff --git a/RPi/readme.md b/RPi/readme.md new file mode 100644 index 0000000..9c4ce74 --- /dev/null +++ b/RPi/readme.md @@ -0,0 +1,108 @@ + +Optimized Raspberry Pi RF24 and RF24 Network Libraries +============================================= + General Documentation: http://tmrh20.github.io + + Library functions are mostly the same. + See the included examples for RPi specific usage + +##Raspberry Pi - PreConfig + + +####Possible pre-configuration: + If SPI is not already enabled, load it on boot: +``` + sudo raspi-config +``` +A. Update the tool via the menu as required +B. Select Advanced and enable the SPI kernel module +C. Update other software and libraries: +``` + sudo apt-get update + sudo apt-get upgrade +``` +RPi - RF24 Quick-Start +====================== +A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: +``` + mkdir ~/rf24libs + cd ~/rf24libs +``` +B. Clone the RF24 Repo +``` + git clone https://github.com/tmrh20/RF24.git rtemp +``` +C. Copy the RPi library folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24 ./ + rm -r rtemp + cd RF24 +``` +D. Build the library, and run an example file: +``` + sudo make + cd examples + sudo make + sudo ./gettingstarted +``` + +RPi - RF24Network Quick-Start +============================= +A. Enter the same directory that contains the RF24 library folder +``` + cd ~/rf24libs +``` +B. Clone the RF24Network Repo +``` + git clone https://github.com/tmrh20/RF24Network.git ntemp +``` +C. Copy the RF24Network folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24Network ./ + rm -r ntemp + cd RF24Network +``` +D. Build the library +``` + sudo make + cd examples + sudo make + sudo ./helloworld_rx OR sudo ./helloworld_tx +``` + +Connection Info +=============== + +Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN + +**Constructor:** +``` +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); +``` + +**Pins:** + + |NRF24L01 | RPI | P1 Connector | + |----- |----------- | -------------| + |VCC: | rpi-3v3 | (17) | + |GND: | rpi-gnd | (25) | + |CE: | rpi-gpio22 | (15) | + |CSN: | rpi-gpio8 | (24) | + |SCK: | rpi-sckl | (23) | + |MOSI: | rpi-mosi | (19) | + |MISO: | rpi-miso | (21) | + + +**************** + + +Based on the arduino lib from J. Coliz . +the library was berryfied by Purinda Gunasekara . +then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi +Network lib also based on https://github.com/farconada/RF24Network + +Currently optimized and aligned with Arduino fork of libraries by TMRh20: +https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi +Documentation: http://tmrh20.github.io + + From cd8bb19dc3aa63d30b4e474ce95b8224f0fa71c2 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Thu, 24 Apr 2014 15:32:55 -0600 Subject: [PATCH 229/326] fix typo in readme --- RPi/RF24/readme.md | 2 +- RPi/readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RPi/RF24/readme.md b/RPi/RF24/readme.md index 9c4ce74..0f374a7 100644 --- a/RPi/RF24/readme.md +++ b/RPi/RF24/readme.md @@ -58,7 +58,7 @@ B. Clone the RF24Network Repo ``` C. Copy the RF24Network folder to the current directory, and delete the rest ``` - mv rtemp/RPi/RF24Network ./ + mv ntemp/RPi/RF24Network ./ rm -r ntemp cd RF24Network ``` diff --git a/RPi/readme.md b/RPi/readme.md index 9c4ce74..0f374a7 100644 --- a/RPi/readme.md +++ b/RPi/readme.md @@ -58,7 +58,7 @@ B. Clone the RF24Network Repo ``` C. Copy the RF24Network folder to the current directory, and delete the rest ``` - mv rtemp/RPi/RF24Network ./ + mv ntemp/RPi/RF24Network ./ rm -r ntemp cd RF24Network ``` From 6530f954a18731e213efeb967265b612b1087ac5 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 25 Apr 2014 19:38:08 -0600 Subject: [PATCH 230/326] RPi SPI functionality and RPi fix for startWrite - Enabled use of hardware CS1 pin - Removed custom_cs code for software driven CSN - Modified BCM driver to allow use of hardware SPI pins not in use while SPI is active. - Updated constructor in examples --- README.md | 1 + RPi/RF24/RF24.cpp | 157 ++++++----- RPi/RF24/bcm2835.c | 238 +++++++--------- RPi/RF24/bcm2835.h | 254 +++++++++--------- RPi/RF24/examples/gettingstarted.cpp | 9 +- .../examples/gettingstarted_call_response.cpp | 9 +- RPi/RF24/examples/pingpair_dyn.cpp | 20 +- RPi/RF24/examples/transfer.cpp | 36 +-- RPi/RF24/readme.md | 25 +- RPi/readme.md | 25 +- 10 files changed, 376 insertions(+), 398 deletions(-) diff --git a/README.md b/README.md index 054d80d..d0f2c0e 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,5 @@ Supported Boards: * Mega Types (2560, 1280, etc) * ARM (Arduino Due) via extended SPI methods * ATTiny 24/44/84 25/45/85 +* Raspberry Pi * See the [documentation](http://tmrh20.github.io/) for more info \ No newline at end of file diff --git a/RPi/RF24/RF24.cpp b/RPi/RF24/RF24.cpp index 303fde0..c45afaf 100644 --- a/RPi/RF24/RF24.cpp +++ b/RPi/RF24/RF24.cpp @@ -4,12 +4,12 @@ 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. - - - + + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) Modified to use with Arduipi board http://hallard.me/arduipi - Changed to use modified bcm2835 library + Changed to use modified bcm2835 library TMRh20 2014: Updated to work with optimized RF24 and RF24 Network Arduino libs. */ @@ -53,7 +53,7 @@ uint8_t RF24::read_register(uint8_t reg) *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); *ptx++ = NOP ; // Dummy operation, just for reading - + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); result = *++prx; // result is 2nd byte of receive buffer @@ -71,12 +71,12 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value) *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); *ptx = value ; - + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); - + status = *prx++; // status is 1st byte of receive buffer - if (debug) + if (debug) printf("write_register(%02x,%02x)\r\n",reg,value); return status; @@ -90,7 +90,7 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) uint8_t * prx = spi_rxbuff; uint8_t * ptx = spi_txbuff; uint8_t size = len + 1; // Add register value to transmit buffer - + *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); while ( len-- ) *ptx++ = *buf++; @@ -98,7 +98,7 @@ uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); status = *prx; // status is 1st byte of receive buffer - + return status; } @@ -110,15 +110,15 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeTyp uint8_t status; uint8_t * prx = spi_rxbuff; uint8_t * ptx = spi_txbuff; - uint8_t size ; - + uint8_t size ; + const uint8_t* current = reinterpret_cast(buf); uint8_t data_len = min(len,payload_size); uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - + size = data_len + blank_len + 1 ; // Add register value to transmit buffer - + if (debug) printf("[Writing %u bytes %u blanks]",data_len,blank_len); @@ -126,13 +126,13 @@ uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeTyp while ( data_len-- ) *ptx++ = *current++; while ( blank_len-- ) - *ptx++ = 0; + *ptx++ = 0; bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); status = *prx; // status is 1st byte of receive buffer - + return status; } @@ -143,30 +143,30 @@ uint8_t RF24::read_payload(void* buf, uint8_t len) uint8_t status; uint8_t * prx = spi_rxbuff; uint8_t * ptx = spi_txbuff; - uint8_t size ; - + uint8_t size ; + uint8_t* current = reinterpret_cast(buf); uint8_t data_len = min(len,payload_size); uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - + size = data_len + blank_len + 1; // Add register value to transmit buffer - + if (debug) printf("[Reading %u bytes %u blanks]",data_len,blank_len); - + *ptx++ = R_RX_PAYLOAD; while(size--) *ptx++ = NOP; - + // Size has been lost during while, re affect size = data_len + blank_len + 1; // Add register value to transmit buffer - + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); - + // 1st byte is status status = *prx++; - + // Decrement before to skip 1st status byte while ( --size ) *current++ = *prx++; @@ -264,7 +264,7 @@ void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) /****************************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin, uint32_t _spi_speed): - ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), wide_band(true), p_variant(false), + ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), wide_band(true), p_variant(false), payload_size(32), dynamic_payloads_enabled(false), pipe0_reading_address(0) { @@ -324,7 +324,7 @@ static const char rf24_pa_dbm_e_str_0[] = "PA_MIN"; static const char rf24_pa_dbm_e_str_1[] = "PA_LOW"; static const char rf24_pa_dbm_e_str_2[] = "PA_HIGH"; static const char rf24_pa_dbm_e_str_3[] = "PA_MAX"; -static const char * const rf24_pa_dbm_e_str_P[] = { +static const char * const rf24_pa_dbm_e_str_P[] = { rf24_pa_dbm_e_str_0, rf24_pa_dbm_e_str_1, rf24_pa_dbm_e_str_2, @@ -335,7 +335,7 @@ static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)"; static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)"; static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)"; static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven"; -static const char * const rf24_csn_e_str_P[] = { +static const char * const rf24_csn_e_str_P[] = { rf24_csn_e_str_0, rf24_csn_e_str_1, rf24_csn_e_str_2, @@ -352,13 +352,13 @@ void RF24::printDetails(void) printf("CSN Pin \t = %s\n",rf24_csn_e_str_P[csn_pin]); } else - { - printf("CSN Pin \t = Custom GPIO%d%s\n", csn_pin, + { + printf("CSN Pin \t = Custom GPIO%d%s\n", csn_pin, csn_pin==RPI_V2_GPIO_P1_26 ? " (CE1) Software Driven" : "" ); } - + printf("CE Pin \t = Custom GPIO%d\n", ce_pin ); - + // SPI Bus Speed printf("Clock Speed\t = " ); switch (spi_speed) @@ -409,32 +409,45 @@ bool RF24::begin(void) { debug = false; //debug = true; + // This initialize the SPI bus with + // csn pin as chip select (custom or not) + // Init BCM2835 chipset for talking with us if (!bcm2835_init()){ return false; } - // Initialise the CE pin of NRF24 (chip enable) - bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP); - bcm2835_gpio_write(ce_pin, LOW); - + + switch(csn_pin){ //Ensure valid hardware CS pin + case 0: break; + case 1: break; + case 8: csn_pin = 0; break; + case 7: csn_pin = 1; break; + default: csn_pin = 0; break; + } + + + bcm2835_spi_begin(); + // used to drive custom I/O to trigger my logic analyser // bcm2835_gpio_fsel(GPIO_CTRL_PIN , BCM2835_GPIO_FSEL_OUTP); // start the SPI library: // Note the NRF24 wants mode 0, MSB first and default to 1 Mbps - bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); - bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); + bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); + bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // Set SPI bus Speed bcm2835_spi_setClockDivider(spi_speed); - if(csn_pin <= BCM2835_SPI_CS_NONE){ - bcm2835_spi_chipSelect(csn_pin); - } - // This initialize the SPI bus with - // csn pin as chip select (custom or not) - bcm2835_spi_begin(csn_pin); + // Choose hardware CSN pin + bcm2835_spi_chipSelect(csn_pin); + + + // Initialise the CE pin of NRF24 (chip enable) after the CSN pin, so that + // The input mode is not changed if using one of the hardware CE pins + bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_write(ce_pin, LOW); // wait 100ms delay(100); @@ -453,14 +466,14 @@ bool RF24::begin(void) { p_variant = true ; } - + // Then set the data rate to the slowest (and most reliable) speed supported by all // hardware. setDataRate( RF24_1MBPS ) ; // Initialize CRC and request 2-byte (16bit) CRC setCRCLength( RF24_CRC_16 ) ; - + // Reset current status // Notice reset and flush is the last thing we do write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); @@ -479,7 +492,7 @@ bool RF24::begin(void) // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) // PTX should use only 22uA of power write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); - + return true; } @@ -501,7 +514,7 @@ void RF24::startListening(void) // Go! bcm2835_gpio_write(ce_pin, HIGH); - + // wait for the radio to come up (130us actually only needed) //delayMicroseconds(130); } @@ -514,7 +527,7 @@ void RF24::stopListening(void) flush_tx(); flush_rx(); write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); - + } /****************************************************************************/ @@ -528,7 +541,7 @@ void RF24::powerDown(void) /****************************************************************************/ void RF24::powerUp(void) -{ +{ bool up = read_register(CONFIG) & _BV(PWR_UP); if(! up ){ @@ -536,7 +549,7 @@ void RF24::powerUp(void) delay(5); } - + } /******************************************************************/ @@ -544,13 +557,13 @@ void RF24::powerUp(void) bool RF24::write( const void* buf, uint8_t len, const bool multicast ){ // Begin the write - startWrite(buf,len, multicast); + startFastWrite(buf,len, multicast); //Wait until complete or failed // If this hangs, it ain't coming back, no sense in timing out while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } - + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); bcm2835_gpio_write(ce_pin, LOW); @@ -566,7 +579,7 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast ){ bool RF24::write( const void* buf, uint8_t len ){ return write(buf,len,0); -} +} /****************************************************************************/ @@ -600,7 +613,7 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) void RF24::reUseTX(){ write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag //spiTrans( REUSE_TX_PL ); - bcm2835_spi_transfer( REUSE_TX_PL); + bcm2835_spi_transfer( REUSE_TX_PL); bcm2835_gpio_write(ce_pin, LOW); //Re-Transfer packet bcm2835_gpio_write(ce_pin, HIGH); } @@ -648,18 +661,18 @@ void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ bcm2835_gpio_write(ce_pin, HIGH); } - + /****************************************************************************/ void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ) { // Send the payload + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; - bcm2835_gpio_write(ce_pin, HIGH); - //bcm2835_gpio_write(ce_pin, LOW); - + delayMicroseconds(10); + bcm2835_gpio_write(ce_pin, LOW); } /****************************************************************************/ @@ -736,7 +749,7 @@ bool RF24::available(uint8_t* pipe_num) //Check the FIFO buffer to see if data is waitng to be read if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ - + // If the caller wants the pipe number, include that if ( pipe_num ){ @@ -840,12 +853,12 @@ void RF24::toggle_features(void) void RF24::enableDynamicPayloads(void) { - + // So enable them and try again toggle_features(); write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); - if (debug) + if (debug) printf("FEATURE=%i\r\n",read_register(FEATURE)); // Enable dynamic payload on all pipes @@ -900,14 +913,14 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) { uint8_t * ptx = spi_txbuff; - uint8_t size ; - + uint8_t size ; + const uint8_t* current = reinterpret_cast(buf); uint8_t data_len = min(len,payload_size); - + size = data_len + 1 ; // Add register value to transmit buffer - + if (debug){ printf("[Writing %u bytes]",data_len); } @@ -917,9 +930,9 @@ void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) } bcm2835_spi_transfern( (char *) spi_txbuff, size); -} - - +} + + /****************************************************************************/ bool RF24::isAckPayloadAvailable(void) @@ -1053,7 +1066,7 @@ rf24_datarate_e RF24::getDataRate( void ) { rf24_datarate_e result ; uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); - + // switch uses RAM (evil!) // Order matters in our case below if ( dr == _BV(RF_DR_LOW) ) @@ -1079,11 +1092,11 @@ rf24_datarate_e RF24::getDataRate( void ) void RF24::setCRCLength(rf24_crclength_e length) { uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - + // switch uses RAM (evil!) if ( length == RF24_CRC_DISABLED ) { - // Do nothing, we turned it off above. + // Do nothing, we turned it off above. } else if ( length == RF24_CRC_8 ) { diff --git a/RPi/RF24/bcm2835.c b/RPi/RF24/bcm2835.c index 1c76a13..5c5e0c4 100644 --- a/RPi/RF24/bcm2835.c +++ b/RPi/RF24/bcm2835.c @@ -59,7 +59,7 @@ static uint8_t debug = 0; static int i2c_byte_wait_us = 0; // SPI Custom Chip Select Pin -static int spi_custom_cs = 0; +//static int spi_custom_cs = 0; // Time for millis() static unsigned long long epoch ; @@ -365,7 +365,7 @@ void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) void bcm2835_delay(unsigned int millis) { struct timespec sleeper; - + sleeper.tv_sec = (time_t)(millis / 1000); sleeper.tv_nsec = (long)(millis % 1000) * 1000000; nanosleep(&sleeper, NULL); @@ -376,18 +376,18 @@ void bcm2835_delayMicroseconds(uint64_t micros) { struct timespec t1; uint64_t start; - + // Calling nanosleep() takes at least 100-200 us, so use it for // long waits and use a busy wait on the System Timer for the rest. start = bcm2835_st_read(); - + if (micros > 450) { t1.tv_sec = 0; t1.tv_nsec = 1000 * (long)(micros - 200); nanosleep(&t1, NULL); - } - + } + bcm2835_st_delay(start, micros); } @@ -460,91 +460,31 @@ void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud) bcm2835_gpio_pudclk(pin, 0); } -void bcm2835_spi_begin(uint8_t cs) +void bcm2835_spi_begin() { - /* + // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them - bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + //bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + + // Enable CE0 by default, will be disabled if bcm2835_chipSelect() is called using another pin bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 - - + + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK - + // Set the SPI CS register to the some sensible defaults volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; bcm2835_peri_write(paddr, 0); // All 0s - + // Clear TX and RX fifos bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); - */ - - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; - // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them - // except if we need custom Chip Select Pin - // printf("bcm2835_spi_begin -> spi_custom_cs = %d \n",cs ); - - // Do we need custom chip select control or - // drive CE1 manually (because CE1 does not work with hardware) - if ( cs > BCM2835_SPI_CS_NONE || cs == BCM2835_SPI_CS1 ) - { - // indicate we will use a custom GPIO port - spi_custom_cs = cs ; - - // ok hard CE1 not working, drive it manually - if (cs == BCM2835_SPI_CS1 ) - { - // Dirty Hack CE1 in now custom Chip Select GPIO 26 - // the real CE1 pin - spi_custom_cs = RPI_GPIO_P1_26 ; - - bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_OUTP); - bcm2835_gpio_write(spi_custom_cs, HIGH); - } - - // Mask in we use custom CS (not sure it has a real effect) - bcm2835_peri_set_bits(paddr, BCM2835_SPI_CS_NONE, BCM2835_SPI0_CS_CS); - } - // Ok hardware driving of chip select - else - { - // Just in case - spi_custom_cs = 0 ; - - // Mask in the CS bits of CS - bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); - } - - // Now we can drive the I/O as asked - if (spi_custom_cs == 0) - { - // Not custom CS, so hardware driven - bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 - bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 - } - else - { - // so set custom CS as output, High level by default - bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_OUTP); // Custom GPIO - bcm2835_gpio_write(spi_custom_cs, HIGH); - } - - // Classic pin, hardware driven - bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO - bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI - bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK - - // Set the SPI CS register to the some sensible defaults - bcm2835_peri_write(paddr, 0); // All 0s - - // Clear TX and RX fifos - bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); } void bcm2835_spi_end(void) -{ - // Set all the SPI0 pins back to input +{ + /* // Set all the SPI0 pins back to input if (spi_custom_cs == 0) { bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 @@ -557,26 +497,25 @@ void bcm2835_spi_end(void) bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK - - /* + */ + // Set all the SPI0 pins back to input bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK -*/ - } +/* // Drive Custom chip select pin void bcm2835_spi_setChipSelect(uint8_t level) { // Do this only if we are using custom ChipSelect I/O if ( spi_custom_cs > BCM2835_SPI_CS_NONE ) bcm2835_gpio_write(spi_custom_cs, level); -} +}*/ void bcm2835_spi_setBitOrder(uint8_t order) @@ -606,14 +545,15 @@ uint8_t bcm2835_spi_transfer(uint8_t value) { volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; - + // Custom chip select LOW - if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + /*if(spi_custom_cs > BCM2835_SPI_CS_NONE){ bcm2835_spi_setChipSelect(LOW); - } + }*/ + // This is Polled transfer as per section 10.6.1 // BUG ALERT: what happens if we get interupted in this section, and someone else - // accesses a different peripheral? + // accesses a different peripheral? // Clear TX and RX fifos bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); @@ -640,9 +580,9 @@ uint8_t bcm2835_spi_transfer(uint8_t value) bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); // Custom chip select HIGH - if(spi_custom_cs > BCM2835_SPI_CS_NONE){ - bcm2835_spi_setChipSelect(HIGH); - } + //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + // bcm2835_spi_setChipSelect(HIGH); + //} return ret; } @@ -656,7 +596,7 @@ void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) // This is Polled transfer as per section 10.6.1 // BUG ALERT: what happens if we get interupted in this section, and someone else - // accesses a different peripheral? + // accesses a different peripheral? // Clear TX and RX fifos bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); @@ -696,9 +636,9 @@ void bcm2835_spi_writenb(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; - if(spi_custom_cs > BCM2835_SPI_CS_NONE){ - bcm2835_spi_setChipSelect(LOW); - } + //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + // bcm2835_spi_setChipSelect(LOW); + //} // This is Polled transfer as per section 10.6.1 // BUG ALERT: what happens if we get interupted in this section, and someone else // accesses a different peripheral? @@ -715,15 +655,15 @@ void bcm2835_spi_writenb(char* tbuf, uint32_t len) // Maybe wait for TXD while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) ; - + // Write to FIFO, no barrier bcm2835_peri_write_nb(fifo, tbuf[i]); - + // Read from FIFO to prevent stalling while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) (void) bcm2835_peri_read_nb(fifo); } - + // Wait for DONE to be set while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) { while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) @@ -732,10 +672,10 @@ void bcm2835_spi_writenb(char* tbuf, uint32_t len) // Set TA = 0, and also set the barrier bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); - - if(spi_custom_cs > BCM2835_SPI_CS_NONE){ - bcm2835_spi_setChipSelect(HIGH); - } + + //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + // bcm2835_spi_setChipSelect(HIGH); + //} } // Writes (and reads) an number of bytes to SPI @@ -748,6 +688,14 @@ void bcm2835_spi_transfern(char* buf, uint32_t len) void bcm2835_spi_chipSelect(uint8_t cs) { volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + + if(cs){ + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // Revert default of CE0 back to input + }else{ + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 + } + // Mask in the CS bits of CS bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); } @@ -772,7 +720,7 @@ void bcm2835_i2c_begin(void) // Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL -#endif +#endif // Read the clock divider register uint16_t cdiv = bcm2835_peri_read(paddr); @@ -800,7 +748,7 @@ void bcm2835_i2c_setSlaveAddress(uint8_t addr) // Set I2C Device Address #ifdef I2C_V1 volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4; -#else +#else volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4; #endif bcm2835_peri_write(paddr, addr); @@ -815,7 +763,7 @@ void bcm2835_i2c_setClockDivider(uint16_t divider) volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; #else volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; -#endif +#endif bcm2835_peri_write(paddr, divider); // Calculate time for transmitting one byte // 1000000 = micros seconds in a second @@ -845,7 +793,7 @@ uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif +#endif uint32_t remaining = len; uint32_t i = 0; @@ -864,10 +812,10 @@ uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) i++; remaining--; } - + // Enable device and start transfer bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); - + // Transfer is over when BCM2835_BSC_S_DONE while(!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE )) { @@ -916,7 +864,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif +#endif uint32_t remaining = len; uint32_t i = 0; @@ -930,7 +878,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) bcm2835_peri_write_nb(dlen, len); // Start read bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ); - + // wait for transfer to complete while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) { @@ -943,7 +891,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) remaining--; } } - + // transfer has finished - grab any remaining stuff in FIFO while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) { @@ -952,7 +900,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) i++; remaining--; } - + // Received a NACK if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) { @@ -979,7 +927,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) // Read an number of bytes from I2C sending a repeated start after writing // the required register. Only works if your device supports this mode uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) -{ +{ #ifdef I2C_V1 volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; @@ -990,11 +938,11 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif +#endif uint32_t remaining = len; uint32_t i = 0; uint8_t reason = BCM2835_I2C_REASON_OK; - + // Clear FIFO bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); // Clear Status @@ -1005,7 +953,7 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN); bcm2835_peri_write_nb(fifo, regaddr[0]); bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); - + // poll for transfer has started while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) { @@ -1013,14 +961,14 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) break; } - + // Send a repeated start with read bit set in address bcm2835_peri_write_nb(dlen, len); bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); - - // Wait for write to complete and first byte back. + + // Wait for write to complete and first byte back. bcm2835_delayMicroseconds(i2c_byte_wait_us * 3); - + // wait for transfer to complete while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) { @@ -1033,7 +981,7 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) remaining--; } } - + // transfer has finished - grab any remaining stuff in FIFO while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) { @@ -1042,7 +990,7 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) i++; remaining--; } - + // Received a NACK if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) { @@ -1066,10 +1014,10 @@ uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) return reason; } -// Sending an arbitrary number of bytes before issuing a repeated start +// Sending an arbitrary number of bytes before issuing a repeated start // (with no prior stop) and reading a response. Some devices require this behavior. uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len) -{ +{ #ifdef I2C_V1 volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; @@ -1080,12 +1028,12 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif +#endif uint32_t remaining = cmds_len; uint32_t i = 0; uint8_t reason = BCM2835_I2C_REASON_OK; - + // Clear FIFO bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); @@ -1094,7 +1042,7 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint // Set Data Length bcm2835_peri_write_nb(dlen, cmds_len); - + // pre populate FIFO with max buffer while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) { @@ -1103,9 +1051,9 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint remaining--; } - // Enable device and start transfer + // Enable device and start transfer bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); - + // poll for transfer has started (way to do repeated start, from BCM2835 datasheet) while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) { @@ -1113,17 +1061,17 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) break; } - + remaining = buf_len; i = 0; // Send a repeated start with read bit set in address bcm2835_peri_write_nb(dlen, buf_len); bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); - - // Wait for write to complete and first byte back. + + // Wait for write to complete and first byte back. bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1)); - + // wait for transfer to complete while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) { @@ -1136,7 +1084,7 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint remaining--; } } - + // transfer has finished - grab any remaining stuff in FIFO while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) { @@ -1145,7 +1093,7 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint i++; remaining--; } - + // Received a NACK if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) { @@ -1202,7 +1150,7 @@ void bcm2835_pwm_set_clock(uint32_t divisor) bcm2835_delay(110); // Prevents clock going slow // Wait for the clock to be not busy while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) - bcm2835_delay(1); + bcm2835_delay(1); // set the clock divider and enable PWM clock bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); // Source=osc and enable @@ -1288,7 +1236,7 @@ int bcm2835_init(void) { struct timeval tv ; - if (debug) + if (debug) { bcm2835_pads = (uint32_t*)BCM2835_GPIO_PADS; bcm2835_clk = (uint32_t*)BCM2835_CLOCK_BASE; @@ -1303,13 +1251,13 @@ int bcm2835_init(void) int memfd = -1; int ok = 0; // Open the master /dev/memory device - if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) + if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) { fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", strerror(errno)) ; goto exit; } - + // GPIO: bcm2835_gpio = (volatile uint32_t *)mapmem("gpio", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_BASE); if (bcm2835_gpio == MAP_FAILED) goto exit; @@ -1321,10 +1269,10 @@ int bcm2835_init(void) // Clock control (needed for PWM) bcm2835_clk = (volatile uint32_t *)mapmem("clk", BCM2835_BLOCK_SIZE, memfd, BCM2835_CLOCK_BASE); if (bcm2835_clk == MAP_FAILED) goto exit; - + bcm2835_pads = (volatile uint32_t *)mapmem("pads", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PADS); if (bcm2835_pads == MAP_FAILED) goto exit; - + bcm2835_spi0 = (volatile uint32_t *)mapmem("spi0", BCM2835_BLOCK_SIZE, memfd, BCM2835_SPI0_BASE); if (bcm2835_spi0 == MAP_FAILED) goto exit; @@ -1367,10 +1315,10 @@ int bcm2835_close(void) unmapmem((void**) &bcm2835_st, BCM2835_BLOCK_SIZE); unmapmem((void**) &bcm2835_pads, BCM2835_BLOCK_SIZE); return 1; // Success -} +} #ifdef BCM2835_TEST -// this is a simple test program that prints out what it will do rather than +// this is a simple test program that prints out what it will do rather than // actually doing it int main(int argc, char **argv) { @@ -1398,13 +1346,13 @@ int main(int argc, char **argv) { // Turn it on bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH); - + // wait a bit bcm2835_delay(500); - + // turn it off bcm2835_gpio_write(RPI_GPIO_P1_11, LOW); - + // wait a bit bcm2835_delay(500); } @@ -1417,7 +1365,7 @@ int main(int argc, char **argv) // Read some data uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15); printf("read from pin 15: %d\n", value); - + // wait a bit bcm2835_delay(500); } diff --git a/RPi/RF24/bcm2835.h b/RPi/RF24/bcm2835.h index ab47838..f550fcb 100644 --- a/RPi/RF24/bcm2835.h +++ b/RPi/RF24/bcm2835.h @@ -16,7 +16,7 @@ // /// \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi /// -/// This is a C library for Raspberry Pi (RPi). It provides access to +/// This is a C library for Raspberry Pi (RPi). It provides access to /// GPIO and other IO functions on the Broadcom BCM 2835 chip, /// 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. @@ -25,34 +25,34 @@ /// and for accessing the system timers. /// Pin event detection is supported by polling (interrupts are not supported). /// -/// 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 +/// 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 +/// The version of the package that this documentation refers to can be downloaded /// from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.36.tar.gz /// You can find the latest version at http://www.airspayce.com/mikem/bcm2835 /// /// Several example programs are provided. /// -/// Based on data in http://elinux.org/RPi_Low-level_peripherals and +/// Based on data in http://elinux.org/RPi_Low-level_peripherals and /// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf /// and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 /// /// 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. +/// 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. /// /// Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian /// and Occidentalisv01 -/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() +/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() /// are used and the pin is pulled LOW /// it can cause temporary hangs on 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian /// and Occidentalisv01. /// Reason for this is not yet determined, but we suspect that an interrupt handler is /// hitting a hard loop on those OSs. -/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with -/// bcm2835_gpio_clr_len() and friends after use. +/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with +/// bcm2835_gpio_clr_len() and friends after use. /// /// \par Installation /// @@ -71,10 +71,10 @@ /// /// \par Physical Addresses /// -/// The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() +/// The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() /// are low level peripheral register access functions. They are designed to use /// physical addresses as described in section 1.2.3 ARM physical addresses -/// of the BCM2835 ARM Peripherals manual. +/// of the BCM2835 ARM Peripherals manual. /// Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus /// addresses for peripherals are set up to map onto the peripheral bus address range starting at /// 0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at @@ -93,37 +93,37 @@ /// /// \par Pin Numbering /// -/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying +/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying /// BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs -/// +/// /// RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, -/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the +/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the /// IDE header. /// /// RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd. /// -/// The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ +/// The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ /// the RPi pin number. There are symbolic definitions for each of the available pins /// that you should use for convenience. See \ref RPiGPIOPin. /// /// \par SPI Pins -/// -/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, +/// +/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, /// allowing you to send and received data by SPI (Serial Peripheral Interface). /// For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus /// -/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their -/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able +/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their +/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able /// to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). /// When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be /// configured and controled with the usual bcm2835_gpio_* calls. /// /// The Raspberry Pi GPIO pins used for SPI are: -/// +/// /// - P1-19 (MOSI) -/// - P1-21 (MISO) -/// - P1-23 (CLK) -/// - P1-24 (CE0) +/// - P1-21 (MISO) +/// - P1-23 (CLK) +/// - P1-24 (CE0) /// - P1-26 (CE1) /// /// \par I2C Pins @@ -139,10 +139,10 @@ /// /// \par PWM /// -/// The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides +/// The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides /// functions for configuring and controlling PWM output on these pins. /// -/// The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset of +/// The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset of /// GPIO pins. The following GPIO pins may be connected to the following PWM channels (from section 9.5): /// \code /// GPIO PIN RPi pin PWM Channel ALT FUN @@ -161,34 +161,34 @@ /// on the IO headers, and therefore this is the only IO pin on the RPi that can be used for PWM. /// Further it must be set to ALT FUN 5 to get PWM output. /// -/// Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using +/// Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using /// bcm2835_pwm_set_clock(). Each channel can be separately enabled with bcm2835_pwm_set_mode(). /// The average output of the PWM channel is determined by the ratio of DATA/RANGE for that channel. /// Use bcm2835_pwm_set_range() to set the range and bcm2835_pwm_set_data() to set the data in that ratio /// -/// Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware +/// Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware /// sends a combination of clock pulses that results in an overall DATA pulses per RANGE pulses. -/// In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by -/// LOW for RANGE-DATA clock pulses. +/// In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by +/// LOW for RANGE-DATA clock pulses. /// -/// The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from +/// The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from /// a 19.2MHz clock. You can set any divider, but some common ones are provided by the BCM2835_PWM_CLOCK_DIVIDER_* /// values of \ref bcm2835PWMClockDivider. -/// -/// For example, say you wanted to drive a DC motor with PWM at about 1kHz, -/// and control the speed in 1/1024 increments from -/// 0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the +/// +/// For example, say you wanted to drive a DC motor with PWM at about 1kHz, +/// and control the speed in 1/1024 increments from +/// 0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the /// clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be /// 1.2MHz/1024 = 1171.875Hz. /// /// \par Real Time performance constraints /// -/// The bcm2835 is a library for user programs (i.e. they run in 'userland'). +/// The bcm2835 is a library for user programs (i.e. they run in 'userland'). /// Such programs are not part of the kernel and are usually -/// subject to paging and swapping by the kernel while it does other things besides running your program. -/// This means that you should not expect to get real-time performance or -/// real-time timing constraints from such programs. In particular, there is no guarantee that the -/// bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. +/// subject to paging and swapping by the kernel while it does other things besides running your program. +/// This means that you should not expect to get real-time performance or +/// real-time timing constraints from such programs. In particular, there is no guarantee that the +/// bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. /// In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times /// than the one you asked for. So please dont expect to get exactly the time delay you request. /// @@ -208,9 +208,9 @@ /// http://search.cpan.org/~mikem/Device-BCM2835-1.9/lib/Device/BCM2835.pm /// Matthew Baker has kindly made Python bindings available at: /// https://github.com/mubeta06/py-libbcm2835 -/// Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility -/// for Raspberry Pi, based on the bcm2835 library. The -/// utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as a +/// Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility +/// for Raspberry Pi, based on the bcm2835 library. The +/// utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as a /// free download with source included: http://ipsolutionscorp.com/raspberry-pi-spi-utility/ /// /// \par Open Source Licensing GPL V2 @@ -226,7 +226,7 @@ /// /// Some of this code has been inspired by Dom and Gert. /// The I2C code has been inspired by Alan Barr. -/// +/// /// \par Revision History /// /// \version 1.0 Initial release @@ -237,15 +237,15 @@ /// \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz /// \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 /// Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() -/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now +/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now /// changes only the pin specified. Other pins that were already previously /// enabled stay enabled. /// Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() -/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() +/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() /// to clear the enable for individual pins, suggested by Andreas Sundstrom. /// \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write. /// \version 1.8 Improvements to read barrier, as suggested by maddin. -/// \version 1.9 Improvements contributed by mikew: +/// \version 1.9 Improvements contributed by mikew: /// I noticed that it was mallocing memory for the mmaps on /dev/mem. /// It's not necessary to do that, you can just mmap the file directly, /// so I've removed the mallocs (and frees). @@ -259,40 +259,40 @@ /// http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 /// Also added a define for the passwrd value that Gert says is needed to /// change pad control settings. -/// \version 1.10 Changed the names of the delay functions to bcm2835_delay() +/// \version 1.10 Changed the names of the delay functions to bcm2835_delay() /// and bcm2835_delayMicroseconds() to prevent collisions with wiringPi. /// Macros to map delay()-> bcm2835_delay() and /// Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which /// can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY /// \version 1.11 Fixed incorrect link to download file -/// \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) +/// \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) /// \version 1.13 New GPIO pin definitions for RPi version 2 plug P5 /// Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio /// bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0. /// \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead. -/// Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. +/// Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. /// Reported by Ben Simpson. /// \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe. /// \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi /// to allow a mask of pins to be set all at once. Requested by Sebastian Loncar. /// \version 1.17 Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar. -/// \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: -/// now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. +/// \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: +/// now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. /// No need to link with -lrt now. Contributed by Arjan van Vught. -/// \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. +/// \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. /// Reported by olly. /// \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted. -/// \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. +/// \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. /// Patch courtesy Jeremy Mortis. -/// \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set +/// \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set /// falling edge events. Reported by Mark Dootson. -/// \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. +/// \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. /// Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions /// to fix ocasional reads not completing. Patched by Mark Dootson. /// \version 1.24 Mark Dootson p[atched a problem with his previously submitted code -/// under high load from other processes. +/// under high load from other processes. /// \version 1.25 Updated author and distribution location details to airspayce.com -/// \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. +/// \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. /// Reported by Hartmut Henkel. /// \version 1.27 bcm2835_gpio_set_pad() no longer needs BCM2835_PAD_PASSWRD: it is /// now automatically included. @@ -303,11 +303,11 @@ /// \version 1.30 10 microsecond delays from bcm2835_spi_transfer and bcm2835_spi_transfern for /// significant performance improvements, Patch by Alan Watson. /// \version 1.31 Fix a GCC warning about dummy variable, patched by Alan Watson. Thanks. -/// \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. +/// \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. /// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. /// Contributed by Malcolm Wiles based on work by Arvi Govindaraj. /// \version 1.33 Added command line utilities i2c and gpio to examples. Contributed by Shahrooz Shahparnia. -/// \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, +/// \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, /// sends a repeat start, and reads from the device. Contributed by Eduardo Steinhorst. /// \version 1.35 Fix build errors when compiled under Qt. Also performance improvements with SPI transfers. Contributed by Udo Klaas. /// \version 1.36 Make automake's test runner detect that we're skipping tests when not root, the second @@ -539,7 +539,7 @@ typedef enum } RPiGPIOPin; // Defines for SPI -// GPIO register offsets from BCM2835_SPI0_BASE. +// GPIO register offsets from BCM2835_SPI0_BASE. // Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map #define BCM2835_SPI0_CS 0x0000 ///< SPI Master Control and Status #define BCM2835_SPI0_FIFO 0x0004 ///< SPI Master TX and RX FIFOs @@ -570,8 +570,8 @@ typedef enum #define BCM2835_SPI0_CS_TA 0x00000080 ///< Transfer Active #define BCM2835_SPI0_CS_CSPOL 0x00000040 ///< Chip Select Polarity #define BCM2835_SPI0_CS_CLEAR 0x00000030 ///< Clear FIFO Clear RX and TX -#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX -#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX #define BCM2835_SPI0_CS_CPOL 0x00000008 ///< Clock Polarity #define BCM2835_SPI0_CS_CPHA 0x00000004 ///< Clock Phase #define BCM2835_SPI0_CS_CS 0x00000003 ///< Chip Select @@ -597,13 +597,13 @@ typedef enum /// \brief bcm2835SPIChipSelect /// Specify the SPI chip select pin(s) typedef enum -{ +{ // Can also use non-standard pins with this lib BCM2835_SPI_CS0 = 0, ///< Chip Select 0 BCM2835_SPI_CS1 = 1, ///< Chip Select 1 BCM2835_SPI_CS2 = 2, ///< Chip Select 2 (ie pins CS1 and CS2 are asserted) BCM2835_SPI_CS_NONE = 3, ///< No CS, control it yourself - + // Only GPIO > 3 can be used (to not interfere with the previous value just above ) // Lucky we have plenty of theese pins BCM2835_SPI_CS_GPIO4 = RPI_V2_GPIO_P1_07, /// BCM GPIO 4 @@ -617,7 +617,7 @@ typedef enum BCM2835_SPI_CS_GPIO29 = RPI_V2_GPIO_P5_04, /// BCM GPIO 29 BCM2835_SPI_CS_GPIO30 = RPI_V2_GPIO_P5_05, /// BCM GPIO 30 BCM2835_SPI_CS_GPIO31 = RPI_V2_GPIO_P5_06, /// BCM GPIO 31 - + } bcm2835SPIChipSelect; /// \brief bcm2835SPIClockDivider @@ -812,11 +812,11 @@ extern "C" { /// These functions allow you to intialise and control the bcm2835 library /// @{ - /// Initialise the library by opening /dev/mem and getting pointers to the + /// Initialise the library by opening /dev/mem and getting pointers to the /// internal memory for BCM 2835 device registers. You must call this (successfully) - /// before calling any other - /// functions in this library (except bcm2835_set_debug). - /// If bcm2835_init() fails by returning 0, + /// before calling any other + /// functions in this library (except bcm2835_set_debug). + /// If bcm2835_init() fails by returning 0, /// calling any other function may result in crashes or other failures. /// Prints messages to stderr in case of errors. /// \return 1 if successful else 0 @@ -838,12 +838,12 @@ extern "C" { /// \defgroup lowlevel Low level register access /// These functions provide low level register access, and should not generally - /// need to be used - /// + /// need to be used + /// /// @{ /// Reads 32 bit value from a peripheral address - /// The read is done twice, and is therefore always safe in terms of + /// The read is done twice, and is therefore always safe in terms of /// manual section 1.3 Peripheral access precautions for correct memory ordering /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. /// \return the value read from the 32 bit register @@ -861,7 +861,7 @@ extern "C" { /// Writes 32 bit value from a peripheral address - /// The write is done twice, and is therefore always safe in terms of + /// The write is done twice, and is therefore always safe in terms of /// manual section 1.3 Peripheral access precautions for correct memory ordering /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. /// \param[in] value The 32 bit value to write @@ -877,11 +877,11 @@ extern "C" { extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); /// Alters a number of bits in a 32 peripheral regsiter. - /// It reads the current valu and then alters the bits deines as 1 in mask, - /// according to the bit value in value. + /// It reads the current valu and then alters the bits deines as 1 in mask, + /// according to the bit value in value. /// All other bits that are 0 in the mask are unaffected. /// Use this to alter a subset of the bits in a register. - /// The write is done twice, and is therefore always safe in terms of + /// The write is done twice, and is therefore always safe in terms of /// manual section 1.3 Peripheral access precautions for correct memory ordering /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. /// \param[in] value The 32 bit value to write, masked in by mask. @@ -891,7 +891,7 @@ extern "C" { /// @} // end of lowlevel /// \defgroup gpio GPIO register access - /// These functions allow you to control the GPIO interface. You can set the + /// 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. /// @{ @@ -901,31 +901,31 @@ extern "C" { /// \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); - /// Sets the specified pin output to + /// Sets the specified pin output to /// HIGH. /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. /// \sa bcm2835_gpio_write() extern void bcm2835_gpio_set(uint8_t pin); - /// Sets the specified pin output to + /// Sets the specified pin output to /// LOW. /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. /// \sa bcm2835_gpio_write() extern void bcm2835_gpio_clr(uint8_t pin); - /// Sets any of the first 32 GPIO output pins specified in the mask to + /// Sets any of the first 32 GPIO output pins specified in the mask to /// HIGH. /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) /// \sa bcm2835_gpio_write_multi() extern void bcm2835_gpio_set_multi(uint32_t mask); - /// Sets any of the first 32 GPIO output pins specified in the mask to + /// Sets any of the first 32 GPIO output pins specified in the mask to /// LOW. /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) /// \sa bcm2835_gpio_write_multi() extern void bcm2835_gpio_clr_multi(uint32_t mask); - /// Reads the current level on the specified + /// Reads the current level on the specified /// pin and returns either HIGH or LOW. Works whether or not the pin /// is an input or an output. /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. @@ -934,14 +934,14 @@ extern "C" { /// Event Detect Status. /// Tests whether the specified pin has detected a level or edge - /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), + /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), /// bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). /// Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. /// \return HIGH if the event detect status for the given pin is true. extern uint8_t bcm2835_gpio_eds(uint8_t pin); - /// Sets the Event Detect Status register for a given pin to 1, + /// Sets the Event Detect Status register for a given pin to 1, /// which has the effect of clearing the flag. Use this afer seeing /// an Event Detect Status on the pin. /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. @@ -1022,8 +1022,8 @@ extern "C" { /// Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. /// \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. + /// \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. + /// LOW to remove the clock. /// \sa bcm2835_gpio_set_pud() extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on); @@ -1034,7 +1034,7 @@ extern "C" { /// Sets the Pad Control for the given GPIO group. /// \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 + /// \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 /// is automatically included. extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); @@ -1042,9 +1042,9 @@ extern "C" { /// Delays for the specified number of milliseconds. /// Uses nanosleep(), and therefore does not use CPU until the time is up. /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): - /// If the interval specified in req is not an exact multiple of the granularity + /// If the interval specified in req is not an exact multiple of the granularity /// underlying clock (see time(7)), then the interval will be - /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// rounded up to the next multiple. Furthermore, after the sleep completes, /// there may still be a delay before the CPU becomes free to once /// again execute the calling thread. /// \param[in] millis Delay in milliseconds @@ -1053,9 +1053,9 @@ extern "C" { /// Delays for the specified number of microseconds. /// Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers, /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): - /// If the interval specified in req is not an exact multiple of the granularity + /// If the interval specified in req is not an exact multiple of the granularity /// underlying clock (see time(7)), then the interval will be - /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// rounded up to the next multiple. Furthermore, after the sleep completes, /// there may still be a delay before the CPU becomes free to once /// again execute the calling thread. /// For times less than about 450 microseconds, uses a busy wait on the System Timer. @@ -1063,7 +1063,7 @@ extern "C" { /// result in a delay of about 80 microseconds. Your mileage may vary. /// \param[in] micros Delay in microseconds extern void bcm2835_delayMicroseconds (uint64_t micros); - + /// Indicate the number of milliseconds since startup of PI /// This function is like the Arduino millis function /// \return Number of milliseconds @@ -1090,24 +1090,24 @@ extern "C" { /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); - /// @} + /// @} /// \defgroup spi SPI access - /// These functions let you use SPI0 (Serial Peripheral Interface) to + /// These functions let you use SPI0 (Serial Peripheral Interface) to /// interface with an external SPI device. /// @{ /// Start SPI operations. /// Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) /// to alternate function ALT0, which enables those pins for SPI interface. - /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect - /// by default PI hardware driven using CE0 - /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to + /// by default PI hardware driven using CE0 + /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to /// their default functions /// \sa bcm2835_spi_end() - extern void bcm2835_spi_begin(uint8_t cs); + extern void bcm2835_spi_begin(); /// End SPI operations. /// SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) @@ -1115,64 +1115,64 @@ extern "C" { extern void bcm2835_spi_end(void); /// Sets the SPI custom Chip Select pin to correct level - /// Defaults to - /// \param[in] level The desired level, LOW or HIGH, + /// Defaults to + /// \param[in] level The desired level, LOW or HIGH, extern void bcm2835_spi_setChipSelect(uint8_t level); - + /// Sets the SPI bit order /// NOTE: has no effect. Not supported by SPI0. - /// Defaults to - /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, + /// Defaults to + /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, /// see \ref bcm2835SPIBitOrder extern void bcm2835_spi_setBitOrder(uint8_t order); - /// Sets the SPI clock divider and therefore the - /// SPI clock speed. - /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, + /// Sets the SPI clock divider and therefore the + /// SPI clock speed. + /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, /// see \ref bcm2835SPIClockDivider extern void bcm2835_spi_setClockDivider(uint16_t divider); /// Sets the SPI data mode /// Sets the clock polariy and phase - /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, + /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, /// see \ref bcm2835SPIMode extern void bcm2835_spi_setDataMode(uint8_t mode); /// Sets the chip select pin(s) /// When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the /// transfer. - /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect - /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect extern void bcm2835_spi_chipSelect(uint8_t cs); /// Sets the chip select pin polarity for a given pin - /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) - /// will be asserted to the - /// value given by active. When transfers are not happening, the chip select pin(s) + /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) + /// will be asserted to the + /// value given by active. When transfers are not happening, the chip select pin(s) /// return to the complement (inactive) value. /// \param[in] cs The chip select pin to affect /// \param[in] active Whether the chip select pin is to be active HIGH extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); /// Transfers one byte to and from the currently selected SPI slave. - /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// 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. + /// 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] value The 8 bit data byte to write to MOSI /// \return The 8 bit byte simultaneously read from MISO /// \sa bcm2835_spi_transfern() extern uint8_t bcm2835_spi_transfer(uint8_t value); - + /// Transfers any number of bytes to and from the currently selected SPI slave. - /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) /// during the transfer. - /// Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO. + /// 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 /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual - /// \param[in] tbuf Buffer of bytes to send. + /// \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 /// \sa bcm2835_spi_transfer() @@ -1284,7 +1284,7 @@ extern "C" { /// \param[in] micros Delay in microseconds extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); - /// @} + /// @} /// \defgroup pwm Pulse Width Modulation /// Allows control of 2 independent PWM channels. A limited subset of GPIO pins @@ -1293,12 +1293,12 @@ extern "C" { /// documentation on the Main Page. /// @{ - /// Sets the PWM clock divisor, + /// Sets the PWM clock divisor, /// to control the basic PWM pulse widths. /// \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common /// values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider extern void bcm2835_pwm_set_clock(uint32_t divisor); - + /// Sets the mode of the given PWM channel, /// allowing you to control the PWM mode and enable/disable that channel /// \param[in] channel The PWM channel. 0 or 1. @@ -1311,15 +1311,15 @@ extern "C" { /// \param[in] channel The PWM channel. 0 or 1. /// \param[in] range The maximum value permitted for DATA. extern void bcm2835_pwm_set_range(uint8_t channel, uint32_t range); - - /// Sets the PWM pulse ratio to emit to DATA/RANGE, + + /// Sets the PWM pulse ratio to emit to DATA/RANGE, /// where RANGE is set by bcm2835_pwm_set_range(). /// \param[in] channel The PWM channel. 0 or 1. - /// \param[in] data Controls the PWM output ratio as a fraction of the range. + /// \param[in] data Controls the PWM output ratio as a fraction of the range. /// Can vary from 0 to RANGE. extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data); - /// @} + /// @} #ifdef __cplusplus } #endif @@ -1345,9 +1345,9 @@ extern "C" { /// Shows how to use PWM to control GPIO pins /// @example i2c.c -/// Command line utility for executing i2c commands with the +/// 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 +/// Command line utility for executing gpio commands with the /// Broadcom bcm2835. Contributed by Shahrooz Shahparnia. diff --git a/RPi/RF24/examples/gettingstarted.cpp b/RPi/RF24/examples/gettingstarted.cpp index d4aeaa3..5539c4d 100644 --- a/RPi/RF24/examples/gettingstarted.cpp +++ b/RPi/RF24/examples/gettingstarted.cpp @@ -32,15 +32,14 @@ using namespace std; // CE Pin, CSN Pin, SPI Speed -// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); // Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz //RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); -RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); diff --git a/RPi/RF24/examples/gettingstarted_call_response.cpp b/RPi/RF24/examples/gettingstarted_call_response.cpp index 4444191..19a16cc 100644 --- a/RPi/RF24/examples/gettingstarted_call_response.cpp +++ b/RPi/RF24/examples/gettingstarted_call_response.cpp @@ -27,15 +27,14 @@ using namespace std; // CE Pin, CSN Pin, SPI Speed -// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); // Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz //RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); - +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); // Radio pipe addresses for the 2 nodes to communicate. diff --git a/RPi/RF24/examples/pingpair_dyn.cpp b/RPi/RF24/examples/pingpair_dyn.cpp index b7e4d70..09f94e6 100644 --- a/RPi/RF24/examples/pingpair_dyn.cpp +++ b/RPi/RF24/examples/pingpair_dyn.cpp @@ -3,9 +3,9 @@ */ /** - * Example using Dynamic Payloads + * Example using Dynamic Payloads * - * This is an example of how to use payloads of a varying (dynamic) size. + * This is an example of how to use payloads of a varying (dynamic) size. */ #include @@ -22,14 +22,14 @@ using namespace std; // CE Pin, CSN Pin, SPI Speed -// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); // Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); // Radio pipe addresses for the 2 nodes to communicate. @@ -47,7 +47,7 @@ char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating int main(int argc, char** argv){ - + // Print preamble: printf("RF24/examples/pingpair_dyn/\n"); @@ -65,7 +65,7 @@ int main(int argc, char** argv){ char myChar = {0}; cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit) \n>"; getline(cin,input); - + if(input.length() == 1) { myChar = input[0]; if(myChar == '0'){ @@ -129,7 +129,7 @@ if (role == role_ping_out) // Spew it printf("Got response size=%i value=%s\n\r",len,receive_payload); } - + // Update size for next time. next_payload_size += payload_size_increments_by; if ( next_payload_size > max_payload_size ) diff --git a/RPi/RF24/examples/transfer.cpp b/RPi/RF24/examples/transfer.cpp index c4e4ecb..9d55470 100644 --- a/RPi/RF24/examples/transfer.cpp +++ b/RPi/RF24/examples/transfer.cpp @@ -7,7 +7,7 @@ TMRh20 2014 */ /** General Data Transfer Rate Test - * This example demonstrates basic data transfer functionality with the + * This example demonstrates basic data transfer functionality with the updated library. This example will display the transfer rates acheived using the slower form of high-speed transfer using blocking-writes. */ @@ -26,14 +26,14 @@ using namespace std; // CE Pin, CSN Pin, SPI Speed -// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); // Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); @@ -42,16 +42,16 @@ const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; bool role_ping_out = 1, role_pong_back = 0; bool role = 0; -uint8_t data[32]; -unsigned long startTime, stopTime, counter, rxTimer=0; +uint8_t data[32]; +unsigned long startTime, stopTime, counter, rxTimer=0; int main(int argc, char** argv){ - + // Print preamble: printf("RF24/examples/Transfer/\n"); - + radio.begin(); // Setup and configure rf radio radio.setChannel(1); radio.setPALevel(RF24_PA_MAX); @@ -67,7 +67,7 @@ int main(int argc, char** argv){ char myChar = {0}; cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit)\n>"; getline(cin,input); - + if(input.length() == 1) { myChar = input[0]; if(myChar == '0'){ @@ -86,8 +86,8 @@ int main(int argc, char** argv){ radio.openReadingPipe(1,addresses[0]); radio.startListening(); } - - + + for(int i=0; i<32; i++){ data[i] = rand() % 255; //Load the buffer with random data } @@ -105,7 +105,7 @@ int main(int argc, char** argv){ for(int i=0; i Date: Wed, 30 Apr 2014 23:39:06 +0200 Subject: [PATCH 231/326] Only scan for relay node max 3 times per transmission attempt (to save battery) Lower transmission speed to reach longer distances. Remove RF24 to prepare gittree import. --- libraries/MySensors/Config.h | 2 +- libraries/MySensors/Sensor.cpp | 8 +- libraries/RF24/.gitignore | 10 - libraries/RF24/Doxyfile | 1551 ----------------- libraries/RF24/FAQ | 56 - libraries/RF24/README.md | 29 - libraries/RF24/RF24.cpp | 1027 ----------- libraries/RF24/RF24.h | 863 --------- libraries/RF24/RF24_config.h | 65 - libraries/RF24/doxygen-custom.css | 835 --------- .../GettingStarted/GettingStarted.pde | 227 --- .../RF24/examples/GettingStarted/Jamfile | 210 --- .../RF24/examples/GettingStarted/printf.h | 37 - libraries/RF24/examples/led_remote/Jamfile | 206 --- .../RF24/examples/led_remote/led_remote.pde | 254 --- libraries/RF24/examples/led_remote/printf.h | 33 - libraries/RF24/examples/nordic_fob/Jamfile | 219 --- .../RF24/examples/nordic_fob/nordic_fob.pde | 142 -- libraries/RF24/examples/nordic_fob/printf.h | 37 - libraries/RF24/examples/pingpair/Jamfile | 206 --- libraries/RF24/examples/pingpair/pingpair.pde | 218 --- libraries/RF24/examples/pingpair/printf.h | 33 - libraries/RF24/examples/pingpair_dyn/Jamfile | 206 --- .../examples/pingpair_dyn/pingpair_dyn.pde | 234 --- libraries/RF24/examples/pingpair_dyn/printf.h | 31 - libraries/RF24/examples/pingpair_irq/Jamfile | 206 --- .../examples/pingpair_irq/pingpair_irq.pde | 217 --- libraries/RF24/examples/pingpair_irq/printf.h | 32 - .../RF24/examples/pingpair_maple/Jamfile | 182 -- .../RF24/examples/pingpair_maple/main.cpp | 87 - .../pingpair_maple/pingpair_maple.pde | 242 --- .../RF24/examples/pingpair_multi_dyn/Jamfile | 206 --- .../pingpair_multi_dyn/pingpair_multi_dyn.pde | 253 --- .../RF24/examples/pingpair_multi_dyn/printf.h | 31 - libraries/RF24/examples/pingpair_pl/Jamfile | 206 --- .../RF24/examples/pingpair_pl/pingpair_pl.pde | 180 -- libraries/RF24/examples/pingpair_pl/printf.h | 33 - .../RF24/examples/pingpair_sleepy/Jamfile | 206 --- .../pingpair_sleepy/pingpair_sleepy.pde | 288 --- .../RF24/examples/pingpair_sleepy/printf.h | 31 - libraries/RF24/examples/scanner/Jamfile | 206 --- libraries/RF24/examples/scanner/output/core.a | Bin 0 -> 187630 bytes .../RF24/examples/scanner/output/scanner.cpp | 128 ++ .../RF24/examples/scanner/output/scanner.elf | Bin 0 -> 100436 bytes .../RF24/examples/scanner/output/scanner.hex | 325 ++++ libraries/RF24/examples/scanner/printf.h | 31 - libraries/RF24/examples/scanner/scanner.pde | 138 -- libraries/RF24/examples/starping/Jamfile | 206 --- libraries/RF24/examples/starping/printf.h | 31 - libraries/RF24/examples/starping/starping.pde | 293 ---- libraries/RF24/keywords.txt | 26 - libraries/RF24/nRF24L01.h | 127 -- libraries/RF24/tests/README | 7 - libraries/RF24/tests/native/Jamfile | 300 ---- libraries/RF24/tests/native/pingpair_irq.pde | 223 --- libraries/RF24/tests/native/printf.h | 33 - .../RF24/tests/pingpair_blocking/Jamfile | 206 --- .../pingpair_blocking/pingpair_blocking.pde | 268 --- .../RF24/tests/pingpair_blocking/printf.h | 31 - .../RF24/tests/pingpair_blocking/runtest.py | 25 - .../RF24/tests/pingpair_blocking/runtests.sh | 9 - .../RF24/tests/pingpair_blocking/test.ex | 11 - libraries/RF24/tests/pingpair_test/Jamfile | 206 --- .../tests/pingpair_test/pingpair_test.pde | 395 ----- libraries/RF24/tests/pingpair_test/printf.h | 31 - libraries/RF24/tests/pingpair_test/runtest.py | 25 - .../RF24/tests/pingpair_test/runtests.sh | 16 - libraries/RF24/tests/pingpair_test/test.ex | 11 - libraries/RF24/wikidoc.xslt | 41 - 69 files changed, 459 insertions(+), 12029 deletions(-) delete mode 100644 libraries/RF24/.gitignore delete mode 100644 libraries/RF24/Doxyfile delete mode 100644 libraries/RF24/FAQ delete mode 100644 libraries/RF24/README.md delete mode 100644 libraries/RF24/RF24.cpp delete mode 100644 libraries/RF24/RF24.h delete mode 100644 libraries/RF24/RF24_config.h delete mode 100644 libraries/RF24/doxygen-custom.css delete mode 100644 libraries/RF24/examples/GettingStarted/GettingStarted.pde delete mode 100644 libraries/RF24/examples/GettingStarted/Jamfile delete mode 100644 libraries/RF24/examples/GettingStarted/printf.h delete mode 100644 libraries/RF24/examples/led_remote/Jamfile delete mode 100644 libraries/RF24/examples/led_remote/led_remote.pde delete mode 100644 libraries/RF24/examples/led_remote/printf.h delete mode 100644 libraries/RF24/examples/nordic_fob/Jamfile delete mode 100644 libraries/RF24/examples/nordic_fob/nordic_fob.pde delete mode 100644 libraries/RF24/examples/nordic_fob/printf.h delete mode 100644 libraries/RF24/examples/pingpair/Jamfile delete mode 100644 libraries/RF24/examples/pingpair/pingpair.pde delete mode 100644 libraries/RF24/examples/pingpair/printf.h delete mode 100644 libraries/RF24/examples/pingpair_dyn/Jamfile delete mode 100644 libraries/RF24/examples/pingpair_dyn/pingpair_dyn.pde delete mode 100644 libraries/RF24/examples/pingpair_dyn/printf.h delete mode 100644 libraries/RF24/examples/pingpair_irq/Jamfile delete mode 100644 libraries/RF24/examples/pingpair_irq/pingpair_irq.pde delete mode 100644 libraries/RF24/examples/pingpair_irq/printf.h delete mode 100644 libraries/RF24/examples/pingpair_maple/Jamfile delete mode 100644 libraries/RF24/examples/pingpair_maple/main.cpp delete mode 100644 libraries/RF24/examples/pingpair_maple/pingpair_maple.pde delete mode 100644 libraries/RF24/examples/pingpair_multi_dyn/Jamfile delete mode 100644 libraries/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.pde delete mode 100644 libraries/RF24/examples/pingpair_multi_dyn/printf.h delete mode 100644 libraries/RF24/examples/pingpair_pl/Jamfile delete mode 100644 libraries/RF24/examples/pingpair_pl/pingpair_pl.pde delete mode 100644 libraries/RF24/examples/pingpair_pl/printf.h delete mode 100644 libraries/RF24/examples/pingpair_sleepy/Jamfile delete mode 100644 libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.pde delete mode 100644 libraries/RF24/examples/pingpair_sleepy/printf.h delete mode 100644 libraries/RF24/examples/scanner/Jamfile create mode 100644 libraries/RF24/examples/scanner/output/core.a create mode 100644 libraries/RF24/examples/scanner/output/scanner.cpp create mode 100644 libraries/RF24/examples/scanner/output/scanner.elf create mode 100644 libraries/RF24/examples/scanner/output/scanner.hex delete mode 100644 libraries/RF24/examples/scanner/printf.h delete mode 100644 libraries/RF24/examples/scanner/scanner.pde delete mode 100644 libraries/RF24/examples/starping/Jamfile delete mode 100644 libraries/RF24/examples/starping/printf.h delete mode 100644 libraries/RF24/examples/starping/starping.pde delete mode 100644 libraries/RF24/keywords.txt delete mode 100644 libraries/RF24/nRF24L01.h delete mode 100644 libraries/RF24/tests/README delete mode 100644 libraries/RF24/tests/native/Jamfile delete mode 100644 libraries/RF24/tests/native/pingpair_irq.pde delete mode 100644 libraries/RF24/tests/native/printf.h delete mode 100644 libraries/RF24/tests/pingpair_blocking/Jamfile delete mode 100644 libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde delete mode 100644 libraries/RF24/tests/pingpair_blocking/printf.h delete mode 100644 libraries/RF24/tests/pingpair_blocking/runtest.py delete mode 100644 libraries/RF24/tests/pingpair_blocking/runtests.sh delete mode 100644 libraries/RF24/tests/pingpair_blocking/test.ex delete mode 100644 libraries/RF24/tests/pingpair_test/Jamfile delete mode 100644 libraries/RF24/tests/pingpair_test/pingpair_test.pde delete mode 100644 libraries/RF24/tests/pingpair_test/printf.h delete mode 100644 libraries/RF24/tests/pingpair_test/runtest.py delete mode 100644 libraries/RF24/tests/pingpair_test/runtests.sh delete mode 100644 libraries/RF24/tests/pingpair_test/test.ex delete mode 100644 libraries/RF24/wikidoc.xslt diff --git a/libraries/MySensors/Config.h b/libraries/MySensors/Config.h index 3f7e97f..ab2a284 100644 --- a/libraries/MySensors/Config.h +++ b/libraries/MySensors/Config.h @@ -5,7 +5,7 @@ * Configure Sensor Network */ #define RF24_CHANNEL 76 //RF channel for the sensor net, 0-127 -#define RF24_DATARATE RF24_2MBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps +#define RF24_DATARATE RF24_250KBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps #define RF24_PA_LEVEL RF24_PA_MAX //Senor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_HIGH=0dBm #define RF24_PA_LEVEL_GW RF24_PA_LEVEL //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. diff --git a/libraries/MySensors/Sensor.cpp b/libraries/MySensors/Sensor.cpp index 2824e39..5b74064 100644 --- a/libraries/MySensors/Sensor.cpp +++ b/libraries/MySensors/Sensor.cpp @@ -107,7 +107,7 @@ void Sensor::initializeRadioId() { void Sensor::findRelay() { // This can be improved by using status = read_register(OBSERVE_TX,&observe_tx,1) on the // sending side.. the problem is getting this information here without too much fuss. - // Stay in this method until one or more relay nodes answers ping message. + // Try to find relay 3 times before giving up if (radioId == GATEWAY_ADDRESS) return; // Gateway has no business here! @@ -120,7 +120,8 @@ void Sensor::findRelay() { } distance = 255; uint8_t oldRelayId = relayId; - while (distance == 255) { + uint8_t retries=0; + while (distance == 255 && retries<3) { // Send ping message to BROADCAST_ADDRESS (to which all nodes listens and should reply to) buildMsg(radioId, BROADCAST_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_PING, "", 0, false); sendWrite(BROADCAST_ADDRESS, msg, 0); @@ -152,6 +153,7 @@ void Sensor::findRelay() { debug(PSTR("No relay nodes was found. Trying again in 10 seconds.\n")); delay(10000); } + retries++; } // Store new relay address in EEPROM @@ -454,7 +456,7 @@ boolean Sensor::readMessage() { uint8_t len = RF24::getDynamicPayloadSize(); RF24::read(&msg, len); - if (!(msg.header.messageType==M_INTERNAL && msg.header.type == I_PING_ACK)) { + if (!(msg.header.messageType==M_INTERNAL && msg.header.type == I_PING)) { delay(ACK_SEND_DELAY); // Small delay here to let other side switch to reading mode RF24::stopListening(); RF24::openWritingPipe(TO_ADDR(msg.header.last)); diff --git a/libraries/RF24/.gitignore b/libraries/RF24/.gitignore deleted file mode 100644 index 1ac5497..0000000 --- a/libraries/RF24/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -*.bak -*.o -.*.swp -*.orig -.swp -docs/ -output/ -ojam/ -version.h -Session.vim diff --git a/libraries/RF24/Doxyfile b/libraries/RF24/Doxyfile deleted file mode 100644 index 2bd1ea0..0000000 --- a/libraries/RF24/Doxyfile +++ /dev/null @@ -1,1551 +0,0 @@ -# Doxyfile 1.6.3 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = RF24 - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = v1 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = docs - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then 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. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = YES - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -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. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. 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 that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip 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. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -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 only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -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 (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -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 (the default) these declarations will be included in the -# documentation. - -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 (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = 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. - -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. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -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. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED 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. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be 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. - -INPUT = . - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. 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. - -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 pattern (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 *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.h FAQ - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = examples - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = YES - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -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. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = NO - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# 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. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = 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. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# 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, 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. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, 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). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, 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. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -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 -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -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. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# 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. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = 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. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# 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 very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = 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. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -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. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# 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 them the man command -# would be unable to find the correct page. The default is NO. - -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 captures the structure of -# the code including all documentation. - -GENERATE_XML = YES - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# 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. - -XML_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 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. - -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 file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# 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. - -PERLMOD_LATEX = NO - -# 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 size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -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 (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all 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. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that 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. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -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 listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# 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. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need 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 setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# 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. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. 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. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. 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. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# 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 by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# 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 root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES 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. - -DOT_MULTI_TARGETS = YES - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/libraries/RF24/FAQ b/libraries/RF24/FAQ deleted file mode 100644 index d134bb8..0000000 --- a/libraries/RF24/FAQ +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @page FAQ Frequently Asked Questions - * - * @ref starting - * - * @ref hardware - * - * @ref range - * - * @ref tests - * - * @section starting Where do I start? - * - * Start with the pingpair example. Follow the instructions in the comments - * of that sketch. - * - * @section hardware Where can I buy some hardware? - * - * I've been using modules from mdfly.com. - * - * Mostly these units as the workhorse of the system: - * - * @li 2.4Ghz Wireless nRF24L01 Transceiver Module $6.95 - * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=82 - * - * And then also experimenting with these for greater range - * - * @li nRF24L01 2.4GHz Transceiver Module w/ Power Amplifier $12.95 - * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=394 - * @li 2.4GHz Transceiver Module w/ Power Amplifier $19.95 - * http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=583 - * - * However, I also noticed that iTeadStudio has nRF24L01+ units for $4! - * - * @section range What is the range of these units? - * - * Here are some results from measurements I have taken. I recommend that everyone - * take their own measurements in their particular circumstances. - * - * @li non-plus unit, 2MBps (worst case), 41+ ft line of sight indoors, immediate dropoff with any deviation from LOS. (41 ft is as far as I can go in my house without turning a corner) - * @li Plus unit, 250kbps (best case), 46 ft around two corners indoors, 49 ft around one corner. More importantly, at 250k, packet loss is almost negligible through almost all of that range. - * @li Both units at 1MBps, plus unit gets about 10% range improvement over non-plus in almost all situations. - * - * @section tests Why are the examples in the 'tests' directory failing? - * - * The sketches in the 'tests' directory are not for general use. - * Please use the examples in the 'examples' directory instead. - * - * The 'tests' directory is only for people making changes to the library - * to ensure that their changes do not break anything. - * - * - * - * - * - */ diff --git a/libraries/RF24/README.md b/libraries/RF24/README.md deleted file mode 100644 index 9d30e6c..0000000 --- a/libraries/RF24/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Arduino driver for nRF24L01(+) 2.4GHz Wireless Transceiver - -Design Goals: This library is designed to be... - -* Maximally compliant with the intended operation of the chip -* Easy for beginners to use -* Consumed with a public interface that's similiar to other Arduino standard libraries -* Built against the standard SPI library. - -* Modifications to the RF24 library in this fork is backward compatible. A single - enhancement which may cause issue, is code which relies on the driver to power down the - radio, as a side effect. The radio is no longer powered down after each transmit. Rather, - the application must take responsibility for power management. Normally this is - achieved by use of powerDown and powerUp. If you wish to maximize power efficiency, - you must call powerDown after transmit (write, startWrite). - -Please refer to: - -* [Documentation Main Page](http://maniacbug.github.com/RF24) -* [RF24 Class Documentation](http://maniacbug.github.com/RF24/classRF24.html) -* [Source Code](https://github.com/maniacbug/RF24) -* [Downloads](https://github.com/maniacbug/RF24/archives/master) -* [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) - -This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or -the SPI hardware will go into 'slave' mode. This is because the 'SS', or slave select, pin on the arduino -controls if the arduino is the slave. For RF24 use, the arduino is the master and the RF24 is the slave. - - diff --git a/libraries/RF24/RF24.cpp b/libraries/RF24/RF24.cpp deleted file mode 100644 index d54bb6e..0000000 --- a/libraries/RF24/RF24.cpp +++ /dev/null @@ -1,1027 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - Portions Copyright (C) 2011 Greg Copeland - - 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 "RF24.h" - - -/****************************************************************************/ - -void RF24::csn(int mode) -{ - // Minimum ideal SPI bus speed is 2x data rate - // If we assume 2Mbs data rate and 16Mhz clock, a - // divider of 4 is the minimum we want. - // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz -#ifdef ARDUINO - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV4); -#endif - digitalWrite(csn_pin,mode); -} - -/****************************************************************************/ - -void RF24::ce(int level) -{ - digitalWrite(ce_pin,level); -} - -/****************************************************************************/ - -uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) -{ - uint8_t status; - - csn(LOW); - status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); - while ( len-- ) - *buf++ = SPI.transfer(0xff); - - csn(HIGH); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::read_register(uint8_t reg) -{ - csn(LOW); - SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); - uint8_t result = SPI.transfer(0xff); - - csn(HIGH); - return result; -} - -/****************************************************************************/ - -uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) -{ - uint8_t status; - - csn(LOW); - status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); - while ( len-- ) - SPI.transfer(*buf++); - - csn(HIGH); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::write_register(uint8_t reg, uint8_t value) -{ - uint8_t status; - - IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); - - csn(LOW); - status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); - SPI.transfer(value); - csn(HIGH); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeType) -{ - uint8_t status; - - const uint8_t* current = reinterpret_cast(buf); - - uint8_t data_len = min(len,payload_size); - uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - - //printf("[Writing %u bytes %u blanks]",data_len,blank_len); - - csn(LOW); - status = SPI.transfer( writeType ); - while ( data_len-- ) - SPI.transfer(*current++); - while ( blank_len-- ) - SPI.transfer(0); - csn(HIGH); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::read_payload(void* buf, uint8_t len) -{ - uint8_t status; - uint8_t* current = reinterpret_cast(buf); - - uint8_t data_len = min(len,payload_size); - uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - - //printf("[Reading %u bytes %u blanks]",data_len,blank_len); - ce(LOW); // <----- NEED THIS ALSO - - csn(LOW); - status = SPI.transfer( R_RX_PAYLOAD ); - while ( data_len-- ) - *current++ = SPI.transfer(0xff); - while ( blank_len-- ) - SPI.transfer(0xff); - csn(HIGH); - ce(HIGH); //? ADDED IN - return status; -} - -/****************************************************************************/ - -uint8_t RF24::flush_rx(void) -{ - uint8_t status; - - csn(LOW); - status = SPI.transfer( FLUSH_RX ); - csn(HIGH); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::flush_tx(void) -{ - uint8_t status; - - csn(LOW); - status = SPI.transfer( FLUSH_TX ); - csn(HIGH); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::get_status(void) -{ - uint8_t status; - - csn(LOW); - status = SPI.transfer( NOP ); - csn(HIGH); - - return status; -} - -/****************************************************************************/ - -void RF24::print_status(uint8_t status) -{ - printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), - status, - (status & _BV(RX_DR))?1:0, - (status & _BV(TX_DS))?1:0, - (status & _BV(MAX_RT))?1:0, - ((status >> RX_P_NO) & B111), - (status & _BV(TX_FULL))?1:0 - ); -} - -/****************************************************************************/ - -void RF24::print_observe_tx(uint8_t value) -{ - printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), - value, - (value >> PLOS_CNT) & B1111, - (value >> ARC_CNT) & B1111 - ); -} - -/****************************************************************************/ - -void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) -{ - char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); - while (qty--) - printf_P(PSTR(" 0x%02x"),read_register(reg++)); - printf_P(PSTR("\r\n")); -} - -/****************************************************************************/ - -void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) -{ - char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); - - while (qty--) - { - uint8_t buffer[5]; - read_register(reg++,buffer,sizeof buffer); - - printf_P(PSTR(" 0x")); - uint8_t* bufptr = buffer + sizeof buffer; - while( --bufptr >= buffer ) - printf_P(PSTR("%02x"),*bufptr); - } - - printf_P(PSTR("\r\n")); -} - -/****************************************************************************/ - -RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false), - payload_size(32), ack_payload_available(false), dynamic_payloads_enabled(false), - pipe0_reading_address(0) -{ -} - -/****************************************************************************/ - -void RF24::setChannel(uint8_t channel) -{ - // TODO: This method could take advantage of the 'wide_band' calculation - // done in setChannel() to require certain channel spacing. - - const uint8_t max_channel = 127; - write_register(RF_CH,min(channel,max_channel)); -} - -/****************************************************************************/ - -uint8_t RF24::getChannel( void ) -{ - return read_register( RF_CH ); -} - -/****************************************************************************/ - -void RF24::setPayloadSize(uint8_t size) -{ - const uint8_t max_payload_size = 32; - payload_size = min(size,max_payload_size); -} - -/****************************************************************************/ - -uint8_t RF24::getPayloadSize(void) -{ - return payload_size; -} - -/****************************************************************************/ - -static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; -static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; -static const char rf24_datarate_e_str_2[] PROGMEM = "250KBPS"; -static const char * const rf24_datarate_e_str_P[] PROGMEM = { - rf24_datarate_e_str_0, - rf24_datarate_e_str_1, - rf24_datarate_e_str_2, -}; -static const char rf24_model_e_str_0[] PROGMEM = "nRF24L01"; -static const char rf24_model_e_str_1[] PROGMEM = "nRF24L01+"; -static const char * const rf24_model_e_str_P[] PROGMEM = { - rf24_model_e_str_0, - rf24_model_e_str_1, -}; -static const char rf24_crclength_e_str_0[] PROGMEM = "Disabled"; -static const char rf24_crclength_e_str_1[] PROGMEM = "8 bits"; -static const char rf24_crclength_e_str_2[] PROGMEM = "16 bits" ; -static const char * const rf24_crclength_e_str_P[] PROGMEM = { - rf24_crclength_e_str_0, - rf24_crclength_e_str_1, - rf24_crclength_e_str_2, -}; -static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; -static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; -static const char rf24_pa_dbm_e_str_2[] PROGMEM = "PA_HIGH"; -static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_MAX"; -static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { - rf24_pa_dbm_e_str_0, - rf24_pa_dbm_e_str_1, - rf24_pa_dbm_e_str_2, - rf24_pa_dbm_e_str_3, -}; - -void RF24::printDetails(void) -{ - print_status(get_status()); - - print_address_register(PSTR("RX_ADDR_P0-1"),RX_ADDR_P0,2); - print_byte_register(PSTR("RX_ADDR_P2-5"),RX_ADDR_P2,4); - print_address_register(PSTR("TX_ADDR"),TX_ADDR); - - print_byte_register(PSTR("RX_PW_P0-6"),RX_PW_P0,6); - print_byte_register(PSTR("EN_AA"),EN_AA); - print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); - print_byte_register(PSTR("RF_CH"),RF_CH); - print_byte_register(PSTR("RF_SETUP"),RF_SETUP); - print_byte_register(PSTR("CONFIG"),CONFIG); - print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); - - printf_P(PSTR("Data Rate\t = %S\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); - printf_P(PSTR("Model\t\t = %S\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); - printf_P(PSTR("CRC Length\t = %S\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); - printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); -} - -/****************************************************************************/ - -void RF24::begin(void) -{ - // Initialize pins - pinMode(ce_pin,OUTPUT); - pinMode(csn_pin,OUTPUT); - - // Initialize SPI bus - SPI.begin(); - - ce(LOW); - csn(HIGH); - - // Must allow the radio time to settle else configuration bits will not necessarily stick. - // This is actually only required following power up but some settling time also appears to - // be required after resets too. For full coverage, we'll always assume the worst. - // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped. - // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure. - // WARNING: Delay is based on P-variant whereby non-P *may* require different timing. - delay( 5 ) ; - - // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier - // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet - // sizes must never be used. See documentation for a more complete explanation. - write_register(SETUP_RETR,(B0101 << ARD) | (B1111 << ARC)); - - // Restore our default PA level - setPALevel( RF24_PA_MAX ) ; - - // Determine if this is a p or non-p RF24 module and then - // reset our data rate back to default value. This works - // because a non-P variant won't allow the data rate to - // be set to 250Kbps. - if( setDataRate( RF24_250KBPS ) ) - { - p_variant = true ; - } - - // Then set the data rate to the slowest (and most reliable) speed supported by all - // hardware. - setDataRate( RF24_1MBPS ) ; - - // Initialize CRC and request 2-byte (16bit) CRC - setCRCLength( RF24_CRC_16 ) ; - - // Disable dynamic payloads, to match dynamic_payloads_enabled setting - write_register(DYNPD,0); - - // Reset current status - // Notice reset and flush is the last thing we do - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Set up default configuration. Callers can always change it later. - // This channel should be universally safe and not bleed over into adjacent - // spectrum. - setChannel(76); - - // Flush buffers - flush_rx(); - flush_tx(); -} - -/****************************************************************************/ - -void RF24::startListening(void) -{ - - // receiver on, transmitter off - write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); - - // if not powered up already, power up the radio - powerUp(); - - write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Restore the pipe0 adddress, if exists - if (pipe0_reading_address) - write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); - -#if 0 - // Flush buffers - flush_rx(); - flush_tx(); -#endif - - // Go! - ce(HIGH); -} - -/****************************************************************************/ - -void RF24::stopListening(void) -{ - ce(LOW); - flush_tx(); - flush_rx(); -} - -/****************************************************************************/ - -void RF24::powerDown(void) -{ - write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); -} - -/****************************************************************************/ - -void RF24::powerUp(void) -{ - uint8_t cfg = read_register(CONFIG); - - // if not powered up then power up and wait for the radio to initialize - if (!(cfg & _BV(PWR_UP))) - { - //printf("RF24::powerUp - powering up radio\n"); - write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); - delayMicroseconds(400); - } -} - -/******************************************************************/ - -bool RF24::write( const void* buf, uint8_t len, const bool multicast ) -{ - bool result = false; - - // Begin the write - startWrite( buf, len, multicast ); - - // ------------ - // At this point we could return from a non-blocking write, and then call - // the rest after an interrupt - - // Instead, we are going to block here until we get TX_DS (transmission completed and ack'd) - // or MAX_RT (maximum retries, transmission failed). Also, we'll timeout in case the radio - // is flaky and we get neither. - - // IN the end, the send should be blocking. It comes back in 60ms worst case. - // Generally much faster. - uint8_t observe_tx; - uint8_t status; - uint32_t sent_at = micros(); - const uint16_t timeout = getMaxTimeout() ; //us to wait for timeout - - // Monitor the send - do - { - status = read_register(OBSERVE_TX,&observe_tx,1); - IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); - } - while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) && ( micros() - sent_at < timeout ) ); - - // The part above is what you could recreate with your own interrupt handler, - // and then call this when you got an interrupt - // ------------ - - // Call this when you get an interrupt - // The status tells us three things - // * The send was successful (TX_DS) - // * The send failed, too many retries (MAX_RT) - // * There is an ack packet waiting (RX_DR) - bool tx_ok, tx_fail; - whatHappened(tx_ok,tx_fail,ack_payload_available); - - //printf("%u%u%u\r\n",tx_ok,tx_fail,ack_payload_available); - - result = tx_ok; - IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); - - // Handle the ack packet - if ( ack_payload_available ) - { - ack_payload_length = getDynamicPayloadSize(); - IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); - IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); - } - - return result; -} -/****************************************************************************/ - -void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ) -{ - // receiver off, transmitter on - write_register(CONFIG, read_register(CONFIG) & ~_BV(PRIM_RX)); - - // if not powered up already, power up the radio - powerUp(); - - // Send the payload - Unicast (W_TX_PAYLOAD) or multicast (W_TX_PAYLOAD_NO_ACK) - write_payload( buf, len, - multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD) ) ; - - // Allons! - ce(HIGH); - delayMicroseconds(10); - ce(LOW); -} - -/****************************************************************************/ - -uint8_t RF24::getDynamicPayloadSize(void) -{ - uint8_t result = 0; - - csn(LOW); - SPI.transfer( R_RX_PL_WID ); - result = SPI.transfer(0xff); - csn(HIGH); - - return result; -} - -/****************************************************************************/ - -bool RF24::available(void) -{ - return available(NULL); -} - -/****************************************************************************/ - -bool RF24::available(uint8_t* pipe_num) -{ - uint8_t status = get_status(); - - // Too noisy, enable if you really want lots o data!! - //IF_SERIAL_DEBUG(print_status(status)); - - bool result = ( status & _BV(RX_DR) ); - - if (result) - { - // If the caller wants the pipe number, include that - if ( pipe_num ) - *pipe_num = ( status >> RX_P_NO ) & B111; - - // Clear the status bit - - // ??? Should this REALLY be cleared now? Or wait until we - // actually READ the payload? - - write_register(STATUS,_BV(RX_DR) ); - - // Handle ack payload receipt - if ( status & _BV(TX_DS) ) - { - write_register(STATUS,_BV(TX_DS)); - } - } - - return result; -} - -/****************************************************************************/ - -bool RF24::read( void* buf, uint8_t len ) -{ - // Fetch the payload - read_payload( buf, len ); - - // was this the last of the data available? - return read_register(FIFO_STATUS) & _BV(RX_EMPTY); -} - -/****************************************************************************/ - -void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) -{ - // Read the status & reset the status in one easy call - // Or is that such a good idea? - uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Report to the user what happened - tx_ok = status & _BV(TX_DS); - tx_fail = status & _BV(MAX_RT); - rx_ready = status & _BV(RX_DR); -} - -/****************************************************************************/ - -void RF24::openWritingPipe(uint64_t value) -{ - // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) - // expects it LSB first too, so we're good. - - write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); - write_register(TX_ADDR, reinterpret_cast(&value), 5); - - const uint8_t max_payload_size = 32; - write_register(RX_PW_P0,min(payload_size,max_payload_size)); -} - -/****************************************************************************/ - -static const uint8_t child_pipe[] PROGMEM = -{ - RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 -}; -static const uint8_t child_payload_size[] PROGMEM = -{ - RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 -}; -static const uint8_t child_pipe_enable[] PROGMEM = -{ - ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 -}; - -void RF24::openReadingPipe(uint8_t child, uint64_t address) -{ - // If this is pipe 0, cache the address. This is needed because - // openWritingPipe() will overwrite the pipe 0 address, so - // startListening() will have to restore it. - if (child == 0) - pipe0_reading_address = address; - - if (child <= 6) - { - // For pipes 2-5, only write the LSB - if ( child < 2 ) - write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); - else - write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); - - write_register(pgm_read_byte(&child_payload_size[child]),payload_size); - - // Note it would be more efficient to set all of the bits for all open - // pipes at once. However, I thought it would make the calling code - // more simple to do it this way. - write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); - } -} - -/****************************************************************************/ - -void RF24::closeReadingPipe( uint8_t pipe ) -{ - write_register(EN_RXADDR,read_register(EN_RXADDR) & ~_BV(pgm_read_byte(&child_pipe_enable[pipe]))); -} - -/****************************************************************************/ - -void RF24::toggle_features(void) -{ - csn(LOW); - SPI.transfer( ACTIVATE ); - SPI.transfer( 0x73 ); - csn(HIGH); -} - -/****************************************************************************/ - -void RF24::enableDynamicPayloads(void) -{ - // Enable dynamic payload throughout the system - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); - - // If it didn't work, the features are not enabled - if ( ! read_register(FEATURE) ) - { - // So enable them and try again - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); - } - - IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); - - // Enable dynamic payload on all pipes - // - // Not sure the use case of only having dynamic payload on certain - // pipes, so the library does not support it. - write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); - - dynamic_payloads_enabled = true; -} - -/****************************************************************************/ - -void RF24::enableAckPayload(void) -{ - // - // enable ack payload and dynamic payload features - // - - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); - - // If it didn't work, the features are not enabled - if ( ! read_register(FEATURE) ) - { - // So enable them and try again - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); - } - - IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); - - // - // Enable dynamic payload on pipes 0 & 1 - // - - write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); -} - -/****************************************************************************/ - -void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) -{ - const uint8_t* current = reinterpret_cast(buf); - - csn(LOW); - SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); - const uint8_t max_payload_size = 32; - uint8_t data_len = min(len,max_payload_size); - while ( data_len-- ) - SPI.transfer(*current++); - - csn(HIGH); -} - -/****************************************************************************/ - -bool RF24::isAckPayloadAvailable(void) -{ - bool result = ack_payload_available; - ack_payload_available = false; - return result; -} - -/****************************************************************************/ - -bool RF24::isPVariant(void) -{ - return p_variant ; -} - -/****************************************************************************/ - -void RF24::setAutoAck(bool enable) -{ - if ( enable ) - write_register(EN_AA, B111111); - else - write_register(EN_AA, 0); -} - -/****************************************************************************/ - -void RF24::setAutoAck( uint8_t pipe, bool enable ) -{ - if ( pipe <= 6 ) - { - uint8_t en_aa = read_register( EN_AA ) ; - if( enable ) - { - en_aa |= _BV(pipe) ; - } - else - { - en_aa &= ~_BV(pipe) ; - } - write_register( EN_AA, en_aa ) ; - } -} - -/****************************************************************************/ - -bool RF24::testCarrier(void) -{ - return ( read_register(CD) & 1 ); -} - -/****************************************************************************/ - -bool RF24::testRPD(void) -{ - return ( read_register(RPD) & 1 ) ; -} - -/****************************************************************************/ - -void RF24::setPALevel(rf24_pa_dbm_e level) -{ - uint8_t setup = read_register(RF_SETUP) ; - setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - - // switch uses RAM (evil!) - if ( level == RF24_PA_MAX ) - { - setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - } - else if ( level == RF24_PA_HIGH ) - { - setup |= _BV(RF_PWR_HIGH) ; - } - else if ( level == RF24_PA_LOW ) - { - setup |= _BV(RF_PWR_LOW); - } - else if ( level == RF24_PA_MIN ) - { - // nothing - } - else if ( level == RF24_PA_ERROR ) - { - // On error, go to maximum PA - setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - } - - write_register( RF_SETUP, setup ) ; -} - -/****************************************************************************/ - -rf24_pa_dbm_e RF24::getPALevel(void) -{ - rf24_pa_dbm_e result = RF24_PA_ERROR ; - uint8_t power = read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ; - - // switch uses RAM (evil!) - if ( power == (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)) ) - { - result = RF24_PA_MAX ; - } - else if ( power == _BV(RF_PWR_HIGH) ) - { - result = RF24_PA_HIGH ; - } - else if ( power == _BV(RF_PWR_LOW) ) - { - result = RF24_PA_LOW ; - } - else - { - result = RF24_PA_MIN ; - } - - return result ; -} - -/****************************************************************************/ - -bool RF24::setDataRate(rf24_datarate_e speed) -{ - bool result = false; - uint8_t setup = read_register(RF_SETUP) ; - - // HIGH and LOW '00' is 1Mbs - our default - wide_band = false ; - setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; - if( speed == RF24_250KBPS ) - { - // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 - // Making it '10'. - wide_band = false ; - setup |= _BV( RF_DR_LOW ) ; - } - else - { - // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 - // Making it '01' - if ( speed == RF24_2MBPS ) - { - wide_band = true ; - setup |= _BV(RF_DR_HIGH); - } - else - { - // 1Mbs - wide_band = false ; - } - } - write_register(RF_SETUP,setup); - - // Verify our result - if ( read_register(RF_SETUP) == setup ) - { - result = true; - } - else - { - wide_band = false; - } - - return result; -} - -/****************************************************************************/ - -rf24_datarate_e RF24::getDataRate( void ) -{ - rf24_datarate_e result ; - uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); - - // switch uses RAM (evil!) - // Order matters in our case below - if ( dr == _BV(RF_DR_LOW) ) - { - // '10' = 250KBPS - result = RF24_250KBPS ; - } - else if ( dr == _BV(RF_DR_HIGH) ) - { - // '01' = 2MBPS - result = RF24_2MBPS ; - } - else - { - // '00' = 1MBPS - result = RF24_1MBPS ; - } - return result ; -} - -/****************************************************************************/ - -void RF24::setCRCLength(rf24_crclength_e length) -{ - uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - - // switch uses RAM (evil!) - if ( length == RF24_CRC_DISABLED ) - { - // Do nothing, we turned it off above. - } - else if ( length == RF24_CRC_8 ) - { - config |= _BV(EN_CRC); - } - else - { - config |= _BV(EN_CRC); - config |= _BV( CRCO ); - } - write_register( CONFIG, config ) ; -} - -/****************************************************************************/ - -rf24_crclength_e RF24::getCRCLength(void) -{ - rf24_crclength_e result = RF24_CRC_DISABLED; - uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; - - if ( config & _BV(EN_CRC ) ) - { - if ( config & _BV(CRCO) ) - result = RF24_CRC_16; - else - result = RF24_CRC_8; - } - - return result; -} - -/****************************************************************************/ - -void RF24::disableCRC( void ) -{ - uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; - write_register( CONFIG, disable ) ; -} - -/****************************************************************************/ - -void RF24::setRetries(uint8_t delay, uint8_t count) -{ - write_register(SETUP_RETR,(delay&0xf)<> 4))) * (retries & 0x0f)) ; - - return to ; -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp - diff --git a/libraries/RF24/RF24.h b/libraries/RF24/RF24.h deleted file mode 100644 index 713b38e..0000000 --- a/libraries/RF24/RF24.h +++ /dev/null @@ -1,863 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - Portions Copyright (C) 2011 Greg Copeland - - 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. - */ - -/** - * @file RF24.h - * - * Class declaration for RF24 and helper enums - */ - -#ifndef __RF24_H__ -#define __RF24_H__ - -#include -#include - -/** - * Power Amplifier level. - * - * For use with setPALevel() - */ -typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; - -/** - * Data rate. How fast data moves through the air. - * - * For use with setDataRate() - */ -typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; - -/** - * CRC Length. How big (if any) of a CRC is included. - * - * For use with setCRCLength() - */ -typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; - -/** - * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver - */ - -class RF24 -{ -private: - uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ - uint8_t csn_pin; /**< SPI Chip select */ - bool wide_band; /* 2Mbs data rate in use? */ - bool p_variant; /* False for RF24L01 and true for RF24L01P */ - uint8_t payload_size; /**< Fixed size of payloads */ - bool ack_payload_available; /**< Whether there is an ack payload waiting */ - bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ - uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ - uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ - -protected: - /** - * @name Low-level internal interface. - * - * Protected methods that address the chip directly. Regular users cannot - * ever call these. They are documented for completeness and for developers who - * may want to extend this class. - */ - /**@{*/ - - /** - * Set chip select pin - * - * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data - * and best of all, we make use of the radio's FIFO buffers. A lower speed - * means we're less likely to effectively leverage our FIFOs and pay a higher - * AVR runtime cost as toll. - * - * @param mode HIGH to take this unit off the SPI bus, LOW to put it on - */ - void csn(int mode); - - /** - * Set chip enable - * - * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet - * for a much more detailed description of this pin. - */ - void ce(int level); - - /** - * Read a chunk of data in from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to put the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); - - /** - * Read single byte from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @return Current value of register @p reg - */ - uint8_t read_register(uint8_t reg); - - /** - * Write a chunk of data to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to get the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); - - /** - * Write a single byte to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param value The new value to write - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, uint8_t value); - - /** - * Write the transmit payload - * - * The size of data written is the fixed payload size, see getPayloadSize() - * - * @param buf Where to get the data - * @param len Number of bytes to be sent - * @return Current value of status register - */ - uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); - - /** - * Read the receive payload - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @param buf Where to put the data - * @param len Maximum number of bytes to read - * @return Current value of status register - */ - uint8_t read_payload(void* buf, uint8_t len); - - /** - * Empty the receive buffer - * - * @return Current value of status register - */ - uint8_t flush_rx(void); - - /** - * Empty the transmit buffer - * - * @return Current value of status register - */ - uint8_t flush_tx(void); - - /** - * Retrieve the current status of the chip - * - * @return Current value of status register - */ - uint8_t get_status(void); - - /** - * Decode and print the given status to stdout - * - * @param status Status value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_status(uint8_t status); - - /** - * Decode and print the given 'observe_tx' value to stdout - * - * @param value The observe_tx value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_observe_tx(uint8_t value); - - /** - * Print the name and value of an 8-bit register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Print the name and value of a 40-bit address register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Turn on or off the special features of the chip - * - * The chip has certain 'features' which are only available when the 'features' - * are enabled. See the datasheet for details. - */ - void toggle_features(void); - /**@}*/ - -public: - /** - * @name Primary public interface - * - * These are the main methods you need to operate the chip - */ - /**@{*/ - - /** - * Constructor - * - * Creates a new instance of this driver. Before using, you create an instance - * and send in the unique pins that this chip is connected to. - * - * @param _cepin The pin attached to Chip Enable on the RF module - * @param _cspin The pin attached to Chip Select - */ - RF24(uint8_t _cepin, uint8_t _cspin); - - /** - * Begin operation of the chip - * - * Call this in setup(), before calling any other methods. - */ - void begin(void); - - /** - * Start listening on the pipes opened for reading. - * - * Be sure to call openReadingPipe() first. Do not call write() while - * in this mode, without first calling stopListening(). Call - * isAvailable() to check for incoming traffic, and read() to get it. - */ - void startListening(void); - - /** - * Stop listening for incoming messages - * - * Do this before calling write(). - */ - void stopListening(void); - - /** - * Write to the open writing pipe - * - * Be sure to call openWritingPipe() first to set the destination - * of where to write to. - * - * This blocks until the message is successfully acknowledged by - * the receiver or the timeout/retransmit maxima are reached. In - * the current configuration, the max delay here is 60ms. - * - * The maximum size of data written is the fixed payload size, see - * getPayloadSize(). However, you can write less, and the remainder - * will just be filled with zeroes. - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast true or false. True, buffer will be multicast; ignoring retry/timeout - * @return True if the payload was delivered successfully false if not - * for multicast payloads, true only means it was transmitted. - */ - bool write( const void* buf, uint8_t len, const bool multicast=false ); - - /** - * Test whether there are bytes available to be read - * - * @return True if there is a payload available, false if none is - */ - bool available(void); - - /** - * Read the payload - * - * Return the last payload received - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @note I specifically chose 'void*' as a data type to make it easier - * for beginners to use. No casting needed. - * - * @param buf Pointer to a buffer where the data should be written - * @param len Maximum number of bytes to read into the buffer - * @return True if the payload was delivered successfully false if not - */ - bool read( void* buf, uint8_t len ); - - /** - * Open a pipe for writing - * - * Only one pipe can be open at once, but you can change the pipe - * you'll listen to. Do not call this while actively listening. - * Remember to stopListening() first. - * - * Addresses are 40-bit hex values, e.g.: - * - * @code - * openWritingPipe(0xF0F0F0F0F0); - * @endcode - * - * @param address The 40-bit address of the pipe to open. This can be - * any value whatsoever, as long as you are the only one writing to it - * and only one other radio is listening to it. Coordinate these pipe - * addresses amongst nodes on the network. - */ - void openWritingPipe(uint64_t address); - - /** - * Open a pipe for reading - * - * Up to 6 pipes can be open for reading at once. Open all the - * reading pipes, and then call startListening(). - * - * @see openWritingPipe - * - * @warning Pipes 1-5 should share the first 32 bits. - * Only the least significant byte should be unique, e.g. - * @code - * openReadingPipe(1,0xF0F0F0F0AA); - * openReadingPipe(2,0xF0F0F0F066); - * @endcode - * - * @warning Pipe 0 is also used by the writing pipe. So if you open - * pipe 0 for reading, and then startListening(), it will overwrite the - * writing pipe. Ergo, do an openWritingPipe() again before write(). - * - * @warning Pipe 0 is also used as the multicast address pipe. Pipe 1 - * is the unicast pipe address. - * - * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits - * - * @param number Which pipe# to open, 0-5. - * @param address The 40-bit address of the pipe to open. - */ - void openReadingPipe(uint8_t number, uint64_t address); - - - /** - * Close a pipe after it has been previously opened. - * Can be safely called without having previously opened a pipe. - * @param pipe Which pipe # to close, 0-5. - */ - void closeReadingPipe( uint8_t pipe ) ; - - /**@}*/ - /** - * @name Optional Configurators - * - * Methods you can use to get or set the configuration of the chip. - * None are required. Calling begin() sets up a reasonable set of - * defaults. - */ - /**@{*/ - /** - * Set the number and delay of retries upon failed submit - * - * @param delay How long to wait between each retry, in multiples of 250us, - * max is 15. 0 means 250us, 15 means 4000us. - * @param count How many retries before giving up, max 15 - */ - void setRetries(uint8_t delay, uint8_t count); - - /**@{*/ - /** - * Get delay and count values of the radio - * - * @param high and low nibbles of delay and count as currently configured on - * the radio. Valid ranges for both nibbles are 0x00-0x0f. The delay nibble - * translates as 0=250us, 15=4000us, in bit multiples of 250us. - */ - uint8_t getRetries( void ) ; - - /** - * Set RF communication channel - * - * @param channel Which RF channel to communicate on, 0-127 - */ - void setChannel(uint8_t channel); - - /** - * Get RF communication channel - * - * @param channel To which RF channel radio is current tuned, 0-127 - */ - uint8_t getChannel(void); - - /** - * Set Static Payload Size - * - * This implementation uses a pre-stablished fixed payload size for all - * transmissions. If this method is never called, the driver will always - * transmit the maximum payload size (32 bytes), no matter how much - * was sent to write(). - * - * @todo Implement variable-sized payloads feature - * - * @param size The number of bytes in the payload - */ - void setPayloadSize(uint8_t size); - - /** - * Get Static Payload Size - * - * @see setPayloadSize() - * - * @return The number of bytes in the payload - */ - uint8_t getPayloadSize(void); - - /** - * Get Dynamic Payload Size - * - * For dynamic payloads, this pulls the size of the payload off - * the chip - * - * @return Payload length of last-received dynamic payload - */ - uint8_t getDynamicPayloadSize(void); - - /** - * Enable custom payloads on the acknowledge packets - * - * Ack payloads are a handy way to return data back to senders without - * manually changing the radio modes on both units. - * - * @warning Do note, multicast payloads will not trigger ack payloads. - * - * @see examples/pingpair_pl/pingpair_pl.pde - */ - void enableAckPayload(void); - - /** - * Enable dynamically-sized payloads - * - * This way you don't always have to send large packets just to send them - * once in a while. This enables dynamic payloads on ALL pipes. - * - * @see examples/pingpair_pl/pingpair_dyn.pde - */ - void enableDynamicPayloads(void); - - /** - * Determine whether the hardware is an nRF24L01+ or not. - * - * @return true if the hardware is nRF24L01+ (or compatible) and false - * if its not. - */ - bool isPVariant(void) ; - - /** - * Enable or disable auto-acknowlede packets - * - * This is enabled by default, so it's only needed if you want to turn - * it off for some reason. - * - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck(bool enable); - - /** - * Enable or disable auto-acknowlede packets on a per pipeline basis. - * - * AA is enabled by default, so it's only needed if you want to turn - * it off/on for some reason on a per pipeline basis. - * - * @param pipe Which pipeline to modify - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck( uint8_t pipe, bool enable ) ; - - /** - * Set Power Amplifier (PA) level to one of four levels. - * Relative mnemonics have been used to allow for future PA level - * changes. According to 6.5 of the nRF24L01+ specification sheet, - * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, - * RF24_PA_HIGH=-6dBM, and RF24_PA_MAX=0dBm. - * - * @param level Desired PA level. - */ - void setPALevel( rf24_pa_dbm_e level ) ; - - /** - * Fetches the current PA level. - * - * @return Returns a value from the rf24_pa_dbm_e enum describing - * the current PA setting. Please remember, all values represented - * by the enum mnemonics are negative dBm. See setPALevel for - * return value descriptions. - */ - rf24_pa_dbm_e getPALevel( void ) ; - - /** - * Set the transmission data rate - * - * @warning setting RF24_250KBPS will fail for non-plus units - * - * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps - * @return true if the change was successful - */ - bool setDataRate(rf24_datarate_e speed); - - /** - * Fetches the transmission data rate - * - * @return Returns the hardware's currently configured datarate. The value - * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the - * rf24_datarate_e enum. - */ - rf24_datarate_e getDataRate( void ) ; - - /** - * Set the CRC length - * - * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - void setCRCLength(rf24_crclength_e length); - - /** - * Get the CRC length - * - * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - rf24_crclength_e getCRCLength(void); - - /** - * Disable CRC validation - * - */ - void disableCRC( void ) ; - - /**@}*/ - /** - * @name Advanced Operation - * - * Methods you can use to drive the chip in more advanced ways - */ - /**@{*/ - - /** - * Print a giant block of debugging information to stdout - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void printDetails(void); - - /** - * Enter low-power mode - * - * To return to normal power mode, either write() some data or - * startListening, or powerUp(). - */ - void powerDown(void); - - /** - * Leave low-power mode - making radio more responsive - * - * To return to low power mode, call powerDown(). - */ - void powerUp(void) ; - - /** - * Test whether there are bytes available to be read - * - * Use this version to discover on which pipe the message - * arrived. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - bool available(uint8_t* pipe_num); - - /** - * Non-blocking write to the open writing pipe - * - * Just like write(), but it returns immediately. To find out what happened - * to the send, catch the IRQ and then call whatHappened(). - * - * @see write() - * @see whatHappened() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast true or false. True, buffer will be multicast; ignoring retry/timeout - */ - void startWrite( const void* buf, uint8_t len, const bool multicast=false ); - - /** - * Write an ack payload for the specified pipe - * - * The next time a message is received on @p pipe, the data in @p buf will - * be sent back in the acknowledgement. - * - * @warning Do note, multicast payloads will not trigger ack payloads. - * - * @warning According to the data sheet, only three of these can be pending - * at any time. I have not tested this. - * - * @param pipe Which pipe# (typically 1-5) will get this response. - * @param buf Pointer to data that is sent - * @param len Length of the data to send, up to 32 bytes max. Not affected - * by the static payload set by setPayloadSize(). - */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); - - /** - * Determine if an ack payload was received in the most recent call to - * write(). - * - * Call read() to retrieve the ack payload. - * - * @warning Calling this function clears the internal flag which indicates - * a payload is available. If it returns true, you must read the packet - * out as the very next interaction with the radio, or the results are - * undefined. - * - * @return True if an ack payload is available. - */ - bool isAckPayloadAvailable(void); - - /** - * Call this when you get an interrupt to find out why - * - * Tells you what caused the interrupt, and clears the state of - * interrupts. - * - * @param[out] tx_ok The send was successful (TX_DS) - * @param[out] tx_fail The send failed, too many retries (MAX_RT) - * @param[out] rx_ready There is a message waiting to be read (RX_DS) - */ - void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); - - /** - * Test whether there was a carrier on the line for the - * previous listening period. - * - * Useful to check for interference on the current channel. - * - * @return true if was carrier, false if not - */ - bool testCarrier(void); - - /** - * Test whether a signal (carrier or otherwise) greater than - * or equal to -64dBm is present on the channel. Valid only - * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). - * - * Useful to check for interference on the current channel and - * channel hopping strategies. - * - * @return true if signal => -64dBm, false if not - */ - bool testRPD(void) ; - - /** - * Test whether this is a real radio, or a mock shim for - * debugging. Setting either pin to 0xff is the way to - * indicate that this is not a real radio. - * - * @return true if this is a legitimate radio - */ - bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } - - /** - * Calculate the maximum timeout in us based on current hardware - * configuration. - * - * @return us of maximum timeout; accounting for retries - */ - uint16_t getMaxTimeout(void) ; - - /**@}*/ -}; - -/** - * @example GettingStarted.pde - * - * This is an example which corresponds to my "Getting Started" blog post: - * Getting Started with nRF24L01+ on Arduino. - * - * It is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting - * with the serial monitor and sending a 'T'. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. - */ - -/** - * @example nordic_fob.pde - * - * This is an example of how to use the RF24 class to receive signals from the - * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . - * Thanks to Kirk Mower for providing test hardware. - */ - -/** - * @example led_remote.pde - * - * This is an example of how to use the RF24 class to control a remote - * bank of LED's using buttons on a remote control. - * - * Every time the buttons change on the remote, the entire state of - * buttons is send to the led board, which displays the state. - */ - -/** - * @example pingpair.pde - * - * This is an example of how to use the RF24 class. Write this sketch to two - * different nodes, connect the role_pin to ground on one. The ping node sends - * the current time to the pong node, which responds by sending the value back. - * The ping node can then see how long the whole cycle took. - */ - -/** - * @example pingpair_maple.pde - * - * This is an example of how to use the RF24 class on the Maple. For a more - * detailed explanation, see my blog post: - * nRF24L01+ Running on Maple - * - * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. - * - * Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -/** - * @example starping.pde - * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the - * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will - * respond back with a copy of the value. Each ping unit can get that response - * back, and determine how long the whole cycle took. - * - * This example requires a bit more complexity to determine which unit is which. - * The pong receiver is identified by having its role_pin tied to ground. - * The ping senders are further differentiated by a byte in eeprom. - */ - -/** - * @example pingpair_pl.pde - * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within - * the Ack packet of each transmission. Note that the payload is set BEFORE - * the sender's message arrives. - */ - -/** - * @example pingpair_irq.pde - * - * This is an example of how to user interrupts to interact with the radio. - * It builds on the pingpair_pl example, and uses ack payloads. - */ - -/** - * @example pingpair_sleepy.pde - * - * This is an example of how to use the RF24 class to create a battery- - * efficient system. It is just like the pingpair.pde example, but the - * ping node powers down the radio and sleeps the MCU after every - * ping/pong cycle. - */ - -/** - * @example scanner.pde - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -/** - * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver - * - * @section Goals Design Goals - * - * This library is designed to be... - * @li Maximally compliant with the intended operation of the chip - * @li Easy for beginners to use - * @li Consumed with a public interface that's similiar to other Arduino standard libraries - * - * @section News News - * - * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. - * Please open an issue if you find any problems using it with any version of Arduino. - * - * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the - * Maple Native, - * and should work with any Maple board. See the pingpair_maple example. - * Note that only the pingpair_maple example has been tested on Maple, although - * the others can certainly be adapted. - * - * @section Useful Useful References - * - * Please refer to: - * - * @li Documentation Main Page - * @li RF24 Class Documentation - * @li Source Code - * @li Downloads Page - * @li Chip Datasheet - * - * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or - * the SPI hardware will go into 'slave' mode. - * - * @section More More Information - * - * @subpage FAQ - * - * @section Projects Projects - * - * Stuff I have built with RF24 - * - * RF24 Getting Started - Finished Product - * - * Getting Started with nRF24L01+ on Arduino - * - * Nordic FOB and nRF24L01+ - * - * Using the Sparkfun Nordic FOB - * - * RF Duinode V3 (2V4) - * - * Low-Power Wireless Sensor Node - * - * nRF24L01+ connected to Leaf Labs Maple Native - * - * nRF24L01+ Running on Maple - */ - -#endif // __RF24_H__ -// vim:ai:cin:sts=2 sw=2 ft=cpp - diff --git a/libraries/RF24/RF24_config.h b/libraries/RF24/RF24_config.h deleted file mode 100644 index fc7397f..0000000 --- a/libraries/RF24/RF24_config.h +++ /dev/null @@ -1,65 +0,0 @@ - -/* - Copyright (C) 2011 J. Coliz - - 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 __RF24_CONFIG_H__ -#define __RF24_CONFIG_H__ - -#if ARDUINO < 100 -#include -#else -#include -#endif - -#include - -// Stuff that is normally provided by Arduino -#ifdef ARDUINO -#include -#else -#include -#include -#include -extern HardwareSPI SPI; -#define _BV(x) (1<<(x)) -#endif - -#undef SERIAL_DEBUG -#ifdef SERIAL_DEBUG -#define IF_SERIAL_DEBUG(x) ({x;}) -#else -#define IF_SERIAL_DEBUG(x) -#endif - -// Avoid spurious warnings -#if 1 -#if ! defined( NATIVE ) && defined( ARDUINO ) -#undef PROGMEM -#define PROGMEM __attribute__(( section(".progmem.data") )) -#undef PSTR -#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) -#endif -#endif - -// Progmem is Arduino-specific -#ifdef ARDUINO -#include -#define PRIPSTR "%S" -#else -typedef char const char; -typedef uint16_t prog_uint16_t; -#define PSTR(x) (x) -#define printf_P printf -#define strlen_P strlen -#define PROGMEM -#define pgm_read_word(p) (*(p)) -#define PRIPSTR "%s" -#endif - -#endif // __RF24_CONFIG_H__ -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/doxygen-custom.css b/libraries/RF24/doxygen-custom.css deleted file mode 100644 index d7d0e12..0000000 --- a/libraries/RF24/doxygen-custom.css +++ /dev/null @@ -1,835 +0,0 @@ -/* The standard CSS for doxygen */ - -body, table, div, p, dl { - font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; - font-size: 12px; -} - -/* @group Heading Levels */ - -h1 { - font-size: 150%; -} - -.title { - font-size: 150%; - font-weight: bold; - margin: 10px 2px; -} - -h2 { - font-size: 120%; -} - -h3 { - font-size: 100%; -} - -dt { - font-weight: bold; -} - -div.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; -} - -p.startli, p.startdd, p.starttd { - margin-top: 2px; -} - -p.endli { - margin-bottom: 0px; -} - -p.enddd { - margin-bottom: 4px; -} - -p.endtd { - margin-bottom: 2px; -} - -/* @end */ - -caption { - font-weight: bold; -} - -span.legend { - font-size: 70%; - text-align: center; -} - -h3.version { - font-size: 90%; - text-align: center; -} - -div.qindex, div.navtab{ - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - padding: 2px; -} - -div.qindex, div.navpath { - width: 100%; - line-height: 140%; -} - -div.navtab { - margin-right: 15px; -} - -/* @group Link Styling */ - -a { - color: #3D578C; - font-weight: normal; - text-decoration: none; -} - -.contents a:visited { - color: #4665A2; -} - -a:hover { - text-decoration: underline; -} - -a.qindex { - font-weight: bold; -} - -a.qindexHL { - font-weight: bold; - background-color: #9CAFD4; - color: #ffffff; - border: 1px double #869DCA; -} - -.contents a.qindexHL:visited { - color: #ffffff; -} - -a.el { - font-weight: bold; -} - -a.elRef { -} - -a.code { - color: #4665A2; -} - -a.codeRef { - color: #4665A2; -} - -/* @end */ - -dl.el { - margin-left: -1cm; -} - -.fragment { - font-family: monospace, fixed; - font-size: 105%; -} - -pre.fragment { - border: 1px solid #C4CFE5; - background-color: #FBFCFD; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - font-size: 9pt; - line-height: 125%; -} - -div.ah { - background-color: black; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px; - padding: 0.2em; - border: solid thin #333; - border-radius: 0.5em; - -webkit-border-radius: .5em; - -moz-border-radius: .5em; - box-shadow: 2px 2px 3px #999; - -webkit-box-shadow: 2px 2px 3px #999; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); - background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); -} - -div.groupHeader { - margin-left: 16px; - margin-top: 12px; - font-weight: bold; -} - -div.groupText { - margin-left: 16px; - font-style: italic; -} - -body { - background: white; - color: black; - margin: 0; -} - -div.contents { - margin-top: 10px; - margin-left: 10px; - margin-right: 5px; -} - -td.indexkey { - background-color: #EBEFF6; - font-weight: bold; - border: 1px solid #C4CFE5; - margin: 2px 0px 2px 0; - padding: 2px 10px; -} - -td.indexvalue { - background-color: #EBEFF6; - border: 1px solid #C4CFE5; - padding: 2px 10px; - margin: 2px 0px; -} - -tr.memlist { - background-color: #EEF1F7; -} - -p.formulaDsp { - text-align: center; -} - -img.formulaDsp { - -} - -img.formulaInl { - vertical-align: middle; -} - -div.center { - text-align: center; - margin-top: 0px; - margin-bottom: 0px; - padding: 0px; -} - -div.center img { - border: 0px; -} - -address.footer { - text-align: right; - padding-right: 12px; -} - -img.footer { - border: 0px; - vertical-align: middle; -} - -/* @group Code Colorization */ - -span.keyword { - color: #008000 -} - -span.keywordtype { - color: #604020 -} - -span.keywordflow { - color: #e08000 -} - -span.comment { - color: #800000 -} - -span.preprocessor { - color: #806020 -} - -span.stringliteral { - color: #002080 -} - -span.charliteral { - color: #008080 -} - -span.vhdldigit { - color: #ff00ff -} - -span.vhdlchar { - color: #000000 -} - -span.vhdlkeyword { - color: #700070 -} - -span.vhdllogic { - color: #ff0000 -} - -/* @end */ - -/* -.search { - color: #003399; - font-weight: bold; -} - -form.search { - margin-bottom: 0px; - margin-top: 0px; -} - -input.search { - font-size: 75%; - color: #000080; - font-weight: normal; - background-color: #e8eef2; -} -*/ - -td.tiny { - font-size: 75%; -} - -.dirtab { - padding: 4px; - border-collapse: collapse; - border: 1px solid #A3B4D7; -} - -th.dirtab { - background: #EBEFF6; - font-weight: bold; -} - -hr { - height: 0px; - border: none; - border-top: 1px solid #4A6AAA; -} - -hr.footer { - height: 1px; -} - -/* @group Member Descriptions */ - -table.memberdecls { - border-spacing: 0px; - padding: 0px; -} - -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { - background-color: #F9FAFC; - border: none; - margin: 4px; - padding: 1px 0 0 8px; -} - -.mdescLeft, .mdescRight { - padding: 0px 8px 4px 8px; - color: #555; -} - -.memItemLeft, .memItemRight, .memTemplParams { - border-top: 1px solid #C4CFE5; -} - -.memItemLeft, .memTemplItemLeft { - white-space: nowrap; -} - -.memItemRight { - width: 100%; -} - -.memTemplParams { - color: #4665A2; - white-space: nowrap; -} - -/* @end */ - -/* @group Member Details */ - -/* Styles for detailed member documentation */ - -.memtemplate { - font-size: 80%; - color: #4665A2; - font-weight: normal; - margin-left: 9px; -} - -.memnav { - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - margin-right: 15px; - padding: 2px; -} - -.mempage { - width: 100%; -} - -.memitem { - padding: 0; - margin-bottom: 10px; - margin-right: 5px; -} - -.memname { - white-space: nowrap; - font-weight: bold; - margin-left: 6px; -} - -.memproto { - border-top: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 6px 0px 6px 0px; - color: #253555; - font-weight: bold; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - /* opera specific markup */ - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - border-top-right-radius: 8px; - border-top-left-radius: 8px; - /* firefox specific markup */ - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - -moz-border-radius-topright: 8px; - -moz-border-radius-topleft: 8px; - /* webkit specific markup */ - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - -webkit-border-top-right-radius: 8px; - -webkit-border-top-left-radius: 8px; - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2E8F2; - -} - -.memdoc { - border-bottom: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 2px 5px; - background-color: #FBFCFD; - border-top-width: 0; - /* opera specific markup */ - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - /* firefox specific markup */ - -moz-border-radius-bottomleft: 8px; - -moz-border-radius-bottomright: 8px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); - /* webkit specific markup */ - -webkit-border-bottom-left-radius: 8px; - -webkit-border-bottom-right-radius: 8px; - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); -} - -.paramkey { - text-align: right; -} - -.paramtype { - white-space: nowrap; -} - -.paramname { - color: #602020; - white-space: nowrap; -} -.paramname em { - font-style: normal; -} - -.params, .retval, .exception, .tparams { - border-spacing: 6px 2px; -} - -.params .paramname, .retval .paramname { - font-weight: bold; - vertical-align: top; -} - -.params .paramtype { - font-style: italic; - vertical-align: top; -} - -.params .paramdir { - font-family: "courier new",courier,monospace; - vertical-align: top; -} - - - - -/* @end */ - -/* @group Directory (tree) */ - -/* for the tree view */ - -.ftvtree { - font-family: sans-serif; - margin: 0px; -} - -/* these are for tree view when used as main index */ - -.directory { - font-size: 9pt; - font-weight: bold; - margin: 5px; -} - -.directory h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; -} - -/* -The following two styles can be used to replace the root node title -with an image of your choice. Simply uncomment the next two styles, -specify the name of your image and be sure to set 'height' to the -proper pixel height of your image. -*/ - -/* -.directory h3.swap { - height: 61px; - background-repeat: no-repeat; - background-image: url("yourimage.gif"); -} -.directory h3.swap span { - display: none; -} -*/ - -.directory > h3 { - margin-top: 0; -} - -.directory p { - margin: 0px; - white-space: nowrap; -} - -.directory div { - display: none; - margin: 0px; -} - -.directory img { - vertical-align: -30%; -} - -/* these are for tree view when not used as main index */ - -.directory-alt { - font-size: 100%; - font-weight: bold; -} - -.directory-alt h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; -} - -.directory-alt > h3 { - margin-top: 0; -} - -.directory-alt p { - margin: 0px; - white-space: nowrap; -} - -.directory-alt div { - display: none; - margin: 0px; -} - -.directory-alt img { - vertical-align: -30%; -} - -/* @end */ - -div.dynheader { - margin-top: 8px; -} - -address { - font-style: normal; - color: #2A3D61; -} - -table.doxtable { - border-collapse:collapse; -} - -table.doxtable td, table.doxtable th { - border: 1px solid #2D4068; - padding: 3px 7px 2px; -} - -table.doxtable th { - background-color: #374F7F; - color: #FFFFFF; - font-size: 110%; - padding-bottom: 4px; - padding-top: 5px; - text-align:left; -} - -.tabsearch { - top: 0px; - left: 10px; - height: 36px; - background-image: url('tab_b.png'); - z-index: 101; - overflow: hidden; - font-size: 13px; -} - -.navpath ul -{ - font-size: 11px; - background-image:url('tab_b.png'); - background-repeat:repeat-x; - height:30px; - line-height:30px; - color:#8AA0CC; - border:solid 1px #C2CDE4; - overflow:hidden; - margin:0px; - padding:0px; -} - -.navpath li -{ - list-style-type:none; - float:left; - padding-left:10px; - padding-right:15px; - background-image:url('bc_s.png'); - background-repeat:no-repeat; - background-position:right; - color:#364D7C; -} - -.navpath li.navelem a -{ - height:32px; - display:block; - text-decoration: none; - outline: none; -} - -.navpath li.navelem a:hover -{ - color:#6884BD; -} - -.navpath li.footer -{ - list-style-type:none; - float:right; - padding-left:10px; - padding-right:15px; - background-image:none; - background-repeat:no-repeat; - background-position:right; - color:#364D7C; - font-size: 8pt; -} - - -div.summary -{ - float: right; - font-size: 8pt; - padding-right: 5px; - width: 50%; - text-align: right; -} - -div.summary a -{ - white-space: nowrap; -} - -div.ingroups -{ - font-size: 8pt; - padding-left: 5px; - width: 50%; - text-align: left; -} - -div.ingroups a -{ - white-space: nowrap; -} - -div.header -{ - background-image:url('nav_h.png'); - background-repeat:repeat-x; - background-color: #F9FAFC; - margin: 0px; - border-bottom: 1px solid #C4CFE5; -} - -div.headertitle -{ - padding: 5px 5px 5px 10px; -} - -dl -{ - padding: 0 0 0 10px; -} - -dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug -{ - border-left:4px solid; - padding: 0 0 0 6px; -} - -dl.note -{ - border-color: #D0C000; -} - -dl.warning, dl.attention -{ - border-color: #FF0000; -} - -dl.pre, dl.post, dl.invariant -{ - border-color: #00D000; -} - -dl.deprecated -{ - border-color: #505050; -} - -dl.todo -{ - border-color: #00C0E0; -} - -dl.test -{ - border-color: #3030E0; -} - -dl.bug -{ - border-color: #C08050; -} - -#projectlogo -{ - text-align: center; - vertical-align: bottom; - border-collapse: separate; -} - -#projectlogo img -{ - border: 0px none; -} - -#projectname -{ - font: 300% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 2px 0px; -} - -#projectbrief -{ - font: 120% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#projectnumber -{ - font: 50% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#titlearea -{ - padding: 0px; - margin: 0px; - width: 100%; - border-bottom: 1px solid #5373B4; -} - -.image -{ - text-align: left; -} - -.dotgraph -{ - text-align: center; -} - -.mscgraph -{ - text-align: center; -} - -.caption -{ - font-weight: bold; -} - diff --git a/libraries/RF24/examples/GettingStarted/GettingStarted.pde b/libraries/RF24/examples/GettingStarted/GettingStarted.pde deleted file mode 100644 index bf1851a..0000000 --- a/libraries/RF24/examples/GettingStarted/GettingStarted.pde +++ /dev/null @@ -1,227 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example for Getting Started with nRF24L01+ radios. - * - * This is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting - * with the serial monitor and sending a 'T'. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role = role_pong_back; - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/GettingStarted/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); - - // optionally, reduce the payload size. seems to - // improve reliability - //radio.setPayloadSize(8); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - //if ( role == role_ping_out ) - { - //radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - //else - { - //radio.openWritingPipe(pipes[1]); - //radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); - - if (ok) - printf("ok..."); - else - printf("failed.\n\r"); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - delay(20); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } - - // - // Change roles - // - - if ( Serial.available() ) - { - char c = toupper(Serial.read()); - if ( c == 'T' && role == role_pong_back ) - { - printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); - - // Become the primary transmitter (ping out) - role = role_ping_out; - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else if ( c == 'R' && role == role_ping_out ) - { - printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); - - // Become the primary receiver (pong back) - role = role_pong_back; - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/GettingStarted/Jamfile b/libraries/RF24/examples/GettingStarted/Jamfile deleted file mode 100644 index 9a5f2c4..0000000 --- a/libraries/RF24/examples/GettingStarted/Jamfile +++ /dev/null @@ -1,210 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = SPI RF24 ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 57600 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN = /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN ?= /usr/bin ; - AVR_INCLUDE ?= /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE = $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -# -# Targets -# - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Grab everything from the current dir -PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; - -# Main output executable -MAIN = $(PWD:B).elf ; - -Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -Hex $(MAIN:B).hex : $(MAIN) ; - -# Upload targets -for _p in $(PORTS) -{ - Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; -} diff --git a/libraries/RF24/examples/GettingStarted/printf.h b/libraries/RF24/examples/GettingStarted/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/libraries/RF24/examples/GettingStarted/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/led_remote/Jamfile b/libraries/RF24/examples/led_remote/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/led_remote/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/led_remote/led_remote.pde b/libraries/RF24/examples/led_remote/led_remote.pde deleted file mode 100644 index 2238845..0000000 --- a/libraries/RF24/examples/led_remote/led_remote.pde +++ /dev/null @@ -1,254 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example LED Remote - * - * This is an example of how to use the RF24 class to control a remote - * bank of LED's using buttons on a remote control. - * - * On the 'remote', connect any number of buttons or switches from - * an arduino pin to ground. Update 'button_pins' to reflect the - * pins used. - * - * On the 'led' board, connect the same number of LED's from an - * arduino pin to a resistor to ground. Update 'led_pins' to reflect - * the pins used. Also connect a separate pin to ground and change - * the 'role_pin'. This tells the sketch it's running on the LED board. - * - * Every time the buttons change on the remote, the entire state of - * buttons is send to the led board, which displays the state. - */ - -#include -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver -// Leave open to be the 'remote' transmitter -const int role_pin = A4; - -// Pins on the remote for buttons -const uint8_t button_pins[] = { 2,3,4,5,6,7 }; -const uint8_t num_button_pins = sizeof(button_pins); - -// Pins on the LED board for LED's -const uint8_t led_pins[] = { 2,3,4,5,6,7 }; -const uint8_t num_led_pins = sizeof(led_pins); - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_remote = 1, role_led } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Remote", "LED Board"}; - -// The role of the current running sketch -role_e role; - -// -// Payload -// - -uint8_t button_states[num_button_pins]; -uint8_t led_states[num_led_pins]; - -// -// Setup -// - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_remote; - else - role = role_led; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/led_remote/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipes for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_remote ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_led ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Set up buttons / LED's - // - - // Set pull-up resistors for all buttons - if ( role == role_remote ) - { - int i = num_button_pins; - while(i--) - { - pinMode(button_pins[i],INPUT); - digitalWrite(button_pins[i],HIGH); - } - } - - // Turn LED's ON until we start getting keys - if ( role == role_led ) - { - int i = num_led_pins; - while(i--) - { - pinMode(button_pins[i],OUTPUT); - led_states[i] = HIGH; - digitalWrite(led_pins[i],led_states[i]); - } - } - -} - -// -// Loop -// - -void loop(void) -{ - // - // Remote role. If the state of any button has changed, send the whole state of - // all buttons. - // - - if ( role == role_remote ) - { - // Get the current state of buttons, and - // Test if the current state is different from the last state we sent - int i = num_button_pins; - bool different = false; - while(i--) - { - uint8_t state = ! digitalRead(button_pins[i]); - if ( state != button_states[i] ) - { - different = true; - button_states[i] = state; - } - } - - // Send the state of the buttons to the LED board - if ( different ) - { - printf("Now sending..."); - bool ok = radio.write( button_states, num_button_pins ); - if (ok) - printf("ok\n\r"); - else - printf("failed\n\r"); - } - - // Try again in a short while - delay(20); - } - - // - // LED role. Receive the state of all buttons, and reflect that in the LEDs - // - - if ( role == role_led ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( button_states, num_button_pins ); - - // Spew it - printf("Got buttons\n\r"); - - // For each button, if the button now on, then toggle the LED - int i = num_led_pins; - while(i--) - { - if ( button_states[i] ) - { - led_states[i] ^= HIGH; - digitalWrite(led_pins[i],led_states[i]); - } - } - } - } - } -} -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/led_remote/printf.h b/libraries/RF24/examples/led_remote/printf.h deleted file mode 100644 index df6c46a..0000000 --- a/libraries/RF24/examples/led_remote/printf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#include "WProgram.h" - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/nordic_fob/Jamfile b/libraries/RF24/examples/nordic_fob/Jamfile deleted file mode 100644 index ec519f7..0000000 --- a/libraries/RF24/examples/nordic_fob/Jamfile +++ /dev/null @@ -1,219 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = RF24 SPI ; -PROJECT_DIRS = $(PWD) ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= stk500v1 ; -UPLOAD_SPEED ?= 115200 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN = /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN = /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -AR = $(AVR_BIN)/avr-ar rcs ; -RANLIB = ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE = $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Library -{ - LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -rule Arduino -{ - LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; - Main $(<) : $(>) ; - LinkLibraries $(<) : libs core ; - Hex $(<:B).hex : $(<) ; - for _p in $(PORTS) - { - Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; - } -} - -# -# Targets -# - -# Grab everything from the core directory -Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Main output executable -Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/libraries/RF24/examples/nordic_fob/nordic_fob.pde b/libraries/RF24/examples/nordic_fob/nordic_fob.pde deleted file mode 100644 index 5a316a0..0000000 --- a/libraries/RF24/examples/nordic_fob/nordic_fob.pde +++ /dev/null @@ -1,142 +0,0 @@ -/* - Copyright (C) 2012 J. Coliz - - 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. - */ - -/** - * Example Nordic FOB Receiver - * - * This is an example of how to use the RF24 class to receive signals from the - * Sparkfun Nordic FOB. Thanks to Kirk Mower for providing test hardware. - * - * See blog post at http://maniacbug.wordpress.com/2012/01/08/nordic-fob/ - */ - -#include -#include -#include "nRF24L01.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// -// Payload -// - -struct payload_t -{ - uint8_t buttons; - uint16_t id; - uint8_t empty; -}; - -const char* button_names[] = { "Up", "Down", "Left", "Right", "Center" }; -const int num_buttons = 5; - -// -// Forward declarations -// - -uint16_t flip_endian(uint16_t in); - -// -// Setup -// - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\r\nRF24/examples/nordic_fob/\r\n"); - - // - // Setup and configure rf radio according to the built-in parameters - // of the FOB. - // - - radio.begin(); - radio.setChannel(2); - radio.setPayloadSize(4); - radio.setAutoAck(false); - radio.setCRCLength(RF24_CRC_8); - radio.openReadingPipe(1,0xE7E7E7E7E7LL); - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -// -// Loop -// - -void loop(void) -{ - // - // Receive each packet, dump it out - // - - // if there is data ready - if ( radio.available() ) - { - // Get the packet from the radio - payload_t payload; - radio.read( &payload, sizeof(payload) ); - - // Print the ID of this message. Note that the message - // is sent 'big-endian', so we have to flip it. - printf("#%05u Buttons ",flip_endian(payload.id)); - - // Print the name of each button - int i = num_buttons; - while (i--) - { - if ( ! ( payload.buttons & _BV(i) ) ) - { - printf("%s ",button_names[i]); - } - } - - // If no buttons, print None - if ( payload.buttons == _BV(num_buttons) - 1 ) - printf("None"); - - printf("\r\n"); - } -} - -// -// Helper functions -// - -// Change a big-endian word into a little-endian -uint16_t flip_endian(uint16_t in) -{ - uint16_t low = in >> 8; - uint16_t high = in << 8; - - return high | low; -} - -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/nordic_fob/printf.h b/libraries/RF24/examples/nordic_fob/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/libraries/RF24/examples/nordic_fob/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair/Jamfile b/libraries/RF24/examples/pingpair/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/pingpair/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/pingpair/pingpair.pde b/libraries/RF24/examples/pingpair/pingpair.pde deleted file mode 100644 index b83de60..0000000 --- a/libraries/RF24/examples/pingpair/pingpair.pde +++ /dev/null @@ -1,218 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example RF Radio Ping Pair - * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -#include -#include -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(9,10); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // optionally, increase the delay between retries & # of retries - // radio.setRetries(15,15); - - // optionally, reduce the payload size. seems to - // improve reliability - // radio.setPayloadSize(8); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - // if( radio.setDataRate( RF24_250KBPS ) ) { - // printf( "Data rate 250KBPS set!\n\r" ) ; - // } else { - // printf( "Data rate 250KBPS set FAILED!!\n\r" ) ; - // } - // radio.setDataRate( RF24_2MBPS ) ; - // radio.setPALevel( RF24_PA_MAX ) ; - radio.enableDynamicPayloads() ; - radio.setAutoAck( true ) ; - radio.powerUp() ; - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 1+(radio.getMaxTimeout()/1000) ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - printf("Timeout duration: %d\n\r", (1+radio.getMaxTimeout()/1000) ) ; - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. This way, we don't delay - // the reply while we wait on serial i/o. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response %lu\n\r", got_time); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair/printf.h b/libraries/RF24/examples/pingpair/printf.h deleted file mode 100644 index 05dd088..0000000 --- a/libraries/RF24/examples/pingpair/printf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#include - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_dyn/Jamfile b/libraries/RF24/examples/pingpair_dyn/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/pingpair_dyn/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/pingpair_dyn/pingpair_dyn.pde b/libraries/RF24/examples/pingpair_dyn/pingpair_dyn.pde deleted file mode 100644 index c0662b6..0000000 --- a/libraries/RF24/examples/pingpair_dyn/pingpair_dyn.pde +++ /dev/null @@ -1,234 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example using Dynamic Payloads - * - * This is an example of how to use payloads of a varying (dynamic) size. - */ - -#include -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -//RF24 radio(8,9); -RF24 radio(22,23); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xEEFDFDFDECLL, 0xEEFDFDF0DFLL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Payload -// - -const int min_payload_size = 1; -const int max_payload_size = 32; -const int payload_size_increments_by = 1; -int next_payload_size = min_payload_size; - -char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_dyn/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // enable dynamic payloads - radio.setCRCLength( RF24_CRC_16 ) ; - radio.enableDynamicPayloads(); - - // optionally, increase the delay between retries & # of retries - radio.setAutoAck( true ) ; - radio.setPALevel( RF24_PA_HIGH ) ; - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // The payload will always be the same, what will change is how much of it we send. - static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - printf("Now sending length %i...",next_payload_size); - radio.write( send_payload, next_payload_size, false ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 1 + radio.getMaxTimeout()/1000 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - uint8_t len = radio.getDynamicPayloadSize(); - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got response size=%i value=%s\n\r",len,receive_payload); - } - - // Update size for next time. - next_payload_size += payload_size_increments_by; - if ( next_payload_size > max_payload_size ) - next_payload_size = min_payload_size; - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - uint8_t len; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - len = radio.getDynamicPayloadSize(); - done = radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got payload size=%i value=%s\n\r",len,receive_payload); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.(write receive_payload, len ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_dyn/printf.h b/libraries/RF24/examples/pingpair_dyn/printf.h deleted file mode 100644 index 1b853db..0000000 --- a/libraries/RF24/examples/pingpair_dyn/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_irq/Jamfile b/libraries/RF24/examples/pingpair_irq/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/pingpair_irq/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/pingpair_irq/pingpair_irq.pde b/libraries/RF24/examples/pingpair_irq/pingpair_irq.pde deleted file mode 100644 index 58eec33..0000000 --- a/libraries/RF24/examples/pingpair_irq/pingpair_irq.pde +++ /dev/null @@ -1,217 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example of using interrupts - * - * This is an example of how to user interrupts to interact with the radio. - * It builds on the pingpair_pl example, and uses ack payloads. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 7; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -// Interrupt handler, check the radio because we got an IRQ -void check_radio(void); - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_irq/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipe for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Attach interrupt handler to interrupt #0 (using pin 2) - // on BOTH the sender and receiver - // - - attachInterrupt(0, check_radio, FALLING); -} - -static uint32_t message_count = 0; - -void loop(void) -{ - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender) - { - // Take the time, and send it. - unsigned long time = millis(); - printf("Now sending %lu\n\r",time); - radio.powerUp() ; - radio.startWrite( &time, sizeof(unsigned long) ); - - // Try again soon - delay(1000); - } - - // - // Receiver role: Does nothing! All the work is in IRQ - // - -} - -void check_radio(void) -{ - // What happened? - bool tx,fail,rx; - radio.whatHappened(tx,fail,rx); - - // Have we successfully transmitted? - if ( tx ) - { - if ( role == role_sender ) - printf("Send:OK\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Sent\n\r"); - } - - // Have we failed to transmit? - if ( fail ) - { - if ( role == role_sender ) - printf("Send:Failed\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Failed\n\r"); - } - - // Transmitter can power down for now, because - // the transmission is done. - if ( ( tx || fail ) && ( role == role_sender ) ) - radio.powerDown(); - - // Did we receive a message? - if ( rx ) - { - // If we're the sender, we've received an ack payload - if ( role == role_sender ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu\n\r",message_count); - } - - // If we're the receiver, we've received a time message - if ( role == role_receiver ) - { - // Get this payload and dump it - static unsigned long got_time; - radio.read( &got_time, sizeof(got_time) ); - printf("Got payload %lu\n\r",got_time); - - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); - ++message_count; - } - } -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_irq/printf.h b/libraries/RF24/examples/pingpair_irq/printf.h deleted file mode 100644 index ef29df7..0000000 --- a/libraries/RF24/examples/pingpair_irq/printf.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_maple/Jamfile b/libraries/RF24/examples/pingpair_maple/Jamfile deleted file mode 100644 index 798096c..0000000 --- a/libraries/RF24/examples/pingpair_maple/Jamfile +++ /dev/null @@ -1,182 +0,0 @@ -MCU = cortex-m3 ; -CHIP = STM32F103ZE ; -BOARD = maple_native ; - -#CHIP = at91sam3u4 ; -#BOARD = sam3u-ek ; - -if ! $(TOOLSET) -{ - TOOLSET = devkit ; - Echo "Assuming TOOLSET=devkit" ; -} - -if $(TOOLSET) = yagarto -{ - TOOLS_PATH = ~/Source/yagarto-4.6.2/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} -if $(TOOLSET) = yagarto-install -{ - TOOLS_PATH = ~/Source/yagarto/install/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} -else if $(TOOLSET) = devkit -{ - TOOLS_PATH = /opt/devkitARM/bin ; - TOOLS_ARCH = arm-eabi- ; -} -else if $(TOOLSET) = maple -{ - TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} -else if $(TOOLSET) = ports -{ - TOOLS_PATH = /opt/local/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} - -CC = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc ; -C++ = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; -AS = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc -c ; -LINK = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; -OBJCOPY = $(TOOLS_PATH)/$(TOOLS_ARCH)objcopy ; -DFU = dfu-util ; - -DEFINES += VECT_TAB_FLASH BOARD_$(BOARD) MCU_$(CHIP) ERROR_LED_PORT=GPIOC ERROR_LED_PIN=15 STM32_HIGH_DENSITY MAPLE_IDE ; -OPTIM = -Os ; -MFLAGS = cpu=$(MCU) thumb arch=armv7-m ; -CCFLAGS = -Wall -m$(MFLAGS) -g -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections ; -C++FLAGS = $(CCFLAGS) -fno-rtti -fno-exceptions ; -LINKFLAGS += -m$(MFLAGS) -Xlinker --gc-sections ; -DFUFLAGS = -a1 -d 0x1eaf:0x0003 -R ; - -MAPLE_DIR = $(HOME)/Source/SAM3U/libmaple ; -MAPLE_LIBS = Servo LiquidCrystal Wire FreeRTOS ; -MAPLE_SUBDIRS = wirish wirish/comm wirish/boards libmaple libmaple/usb libmaple/usb/usb_lib ; - -SKETCH_DIR = $(HOME)/Source/Arduino ; -SKETCH_LIBS = RF24 ; - -MODULE_DIRS = . $(MAPLE_DIR)/$(MAPLE_SUBDIRS) $(MAPLE_DIR)/libraries/$(MAPLE_LIBS) $(SKETCH_DIR)/libraries/$(SKETCH_LIBS) ; -HDRS = $(MODULE_DIRS) ; -LOCATE_TARGET = out/$(TOOLSET) ; -LOCATE_SOURCE = $(LOCATE_TARGET) ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex $(>) $(<) -} - -rule Binary -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends binary : $(<) ; - Clean clean : $(<) ; -} - -actions Binary -{ - $(OBJCOPY) -O binary $(>) $(<) -} - -rule UserObject -{ - switch $(>:S) - { - case .S : As $(<) : $(>) ; - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Upload -{ - Depends up : $(<) ; - NotFile up ; - Always $(<) ; - Always up ; -} - -actions Upload -{ - $(DFU) $(DFUFLAGS) -D $(<) -} - -# Override base objects rule, so all output can go in the output dir -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -# Override base main rule, so all output can go in the output dir -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -# Modules -MODULES = [ GLOB $(MODULE_DIRS) : *.pde *.c *.cpp *.S ] ; - -# Main output executable -MAIN = $(PWD:B).elf ; - -# Linker script -LINK_DIR = $(MAPLE_DIR)/support/ld ; -LINKSCRIPT = $(LINK_DIR)/$(BOARD)/flash.ld ; - -# Bring in the map and link script -LINKFLAGS += -Wl,-Map=$(LOCATE_TARGET)/$(MAIN:B).map -T$(LINKSCRIPT) -L$(LINK_DIR) ; - -Main $(MAIN) : $(MODULES) ; -Binary $(MAIN:B).bin : $(MAIN) ; -Upload $(MAIN:B).bin ; diff --git a/libraries/RF24/examples/pingpair_maple/main.cpp b/libraries/RF24/examples/pingpair_maple/main.cpp deleted file mode 100644 index b4f976d..0000000 --- a/libraries/RF24/examples/pingpair_maple/main.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifdef MAPLE_IDE - -#include -#include "wirish.h" - -extern void setup(void); -extern void loop(void); - -void board_start(const char* program_name) -{ - // Set up the LED to steady on - pinMode(BOARD_LED_PIN, OUTPUT); - digitalWrite(BOARD_LED_PIN, HIGH); - - // Setup the button as input - pinMode(BOARD_BUTTON_PIN, INPUT); - digitalWrite(BOARD_BUTTON_PIN, HIGH); - - SerialUSB.begin(); - SerialUSB.println("Press BUT"); - - // Wait for button press - while ( !isButtonPressed() ) - { - } - - SerialUSB.println("Welcome!"); - SerialUSB.println(program_name); - - int i = 11; - while (i--) - { - toggleLED(); - delay(50); - } -} - -/** - * Custom version of _write, which will print to the USB. - * In order to use it you MUST ADD __attribute__((weak)) - * to _write in libmaple/syscalls.c -*/ -extern "C" int _write (int file, char * ptr, int len) -{ - if ( (file != 1) && (file != 2) ) - return 0; - else - SerialUSB.write(ptr,len); - return len; -} - -/** - * Re-entrant version of _write. Yagarto and Devkit now use - * the re-entrant newlib, so these get called instead of the - * non_r versions. - */ -extern "C" int _write_r (void*, int file, char * ptr, int len) -{ - return _write( file, ptr, len); -} - -__attribute__((constructor)) __attribute__ ((weak)) void premain() -{ - init(); -} - -__attribute__((weak)) void setup(void) -{ - board_start("No program defined"); -} - -__attribute__((weak)) void loop(void) -{ -} - -__attribute__((weak)) int main(void) -{ - setup(); - - while (true) - { - loop(); - } - return 0; -} -#endif // ifdef MAPLE_IDE -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_maple/pingpair_maple.pde b/libraries/RF24/examples/pingpair_maple/pingpair_maple.pde deleted file mode 100644 index 2d3925b..0000000 --- a/libraries/RF24/examples/pingpair_maple/pingpair_maple.pde +++ /dev/null @@ -1,242 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example RF Radio Ping Pair ... for Maple - * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -#include "WProgram.h" -#include -#include "nRF24L01.h" -#include "RF24.h" - -// -// Maple specific setup. Other than this section, the sketch is the same on Maple as on -// Arduino -// - -#ifdef MAPLE_IDE - -// External startup function -extern void board_start(const char* program_name); - -// Use SPI #2. -HardwareSPI SPI(2); - -#else -#define board_startup printf -#define toggleLED(x) (x) -#endif - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 7 & 6 -// (This works for the Getting Started board plugged into the -// Maple Native backwards.) - -RF24 radio(7,6); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 10; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - board_start("\n\rRF24/examples/pingpair/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); - - // optionally, reduce the payload size. seems to - // improve reliability - radio.setPayloadSize(8); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - toggleLED(); - - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); - - if (ok) - printf("ok...\r\n"); - else - printf("failed.\r\n"); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\r\n"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\r\n",got_time,millis()-got_time); - } - - toggleLED(); - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - delay(20); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\r\n"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_multi_dyn/Jamfile b/libraries/RF24/examples/pingpair_multi_dyn/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/pingpair_multi_dyn/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.pde b/libraries/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.pde deleted file mode 100644 index 8809023..0000000 --- a/libraries/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.pde +++ /dev/null @@ -1,253 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example using Dynamic Payloads - * - * This is an example of how to use payloads of a varying (dynamic) size. - */ - -#include -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 -RF24 radio(8,9); - -// Use multicast? -// sets the multicast behavior this unit in hardware. Connect to GND to use unicast -// Leave open (default) to use multicast. -const int multicast_pin = 6 ; - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; -bool multicast = true ; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xEEFAFDFDEELL, 0xEEFDFAF50DFLL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Payload -// - -const int min_payload_size = 1; -const int max_payload_size = 32; -const int payload_size_increments_by = 1; -int next_payload_size = min_payload_size; - -char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char - -void setup(void) -{ - // - // Multicast - // - pinMode(multicast_pin, INPUT); - digitalWrite(multicast_pin,HIGH); - delay( 20 ) ; - - // read multicast role, LOW for unicast - if( digitalRead( multicast_pin ) ) - multicast = true ; - else - multicast = false ; - - - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay( 20 ); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_multi_dyn/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("MULTICAST: %s\r\n",(multicast?"true (unreliable)":"false (reliable)")); - // - // Setup and configure rf radio - // - - radio.begin(); - - // enable dynamic payloads - radio.enableDynamicPayloads(); - radio.setCRCLength( RF24_CRC_16 ) ; - - // optionally, increase the delay between retries & # of retries - radio.setRetries( 15, 5 ) ; - radio.setAutoAck( true ) ; - //radio.setPALevel( RF24_PA_LOW ) ; - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - radio.powerUp() ; - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // The payload will always be the same, what will change is how much of it we send. - static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - printf("Now sending length %i...",next_payload_size); - radio.write( send_payload, next_payload_size, multicast ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 1 + radio.getMaxTimeout()/1000 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - uint8_t len = radio.getDynamicPayloadSize(); - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got response size=%i value=%s\n\r",len,receive_payload); - } - - // Update size for next time. - next_payload_size += payload_size_increments_by; - if ( next_payload_size > max_payload_size ) - next_payload_size = min_payload_size; - - // Try again 1s later - delay(250); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - uint8_t len; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - len = radio.getDynamicPayloadSize(); - done = radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got payload size=%i value=%s\n\r",len,receive_payload); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( receive_payload, len, multicast ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_multi_dyn/printf.h b/libraries/RF24/examples/pingpair_multi_dyn/printf.h deleted file mode 100644 index 1b853db..0000000 --- a/libraries/RF24/examples/pingpair_multi_dyn/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_pl/Jamfile b/libraries/RF24/examples/pingpair_pl/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/pingpair_pl/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/pingpair_pl/pingpair_pl.pde b/libraries/RF24/examples/pingpair_pl/pingpair_pl.pde deleted file mode 100644 index 8980293..0000000 --- a/libraries/RF24/examples/pingpair_pl/pingpair_pl.pde +++ /dev/null @@ -1,180 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example of using Ack Payloads - * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within - * the Ack packet of each transmission. Note that the payload is set BEFORE - * the sender's message arrives. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 7; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_pl/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipes for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - static uint32_t message_count = 0; - - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender) - { - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - if ( radio.isAckPayloadAvailable() ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack: [%lu] ",message_count); - } - printf("OK\n\r"); - - // Try again soon - delay(2000); - } - - // - // Receiver role. Receive each packet, dump it out, add ack payload for next time - // - - if ( role == role_receiver ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - static unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu\n",got_time); - } - - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); - ++message_count; - } - } -} -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_pl/printf.h b/libraries/RF24/examples/pingpair_pl/printf.h deleted file mode 100644 index df6c46a..0000000 --- a/libraries/RF24/examples/pingpair_pl/printf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#include "WProgram.h" - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_sleepy/Jamfile b/libraries/RF24/examples/pingpair_sleepy/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/pingpair_sleepy/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.pde b/libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.pde deleted file mode 100644 index 0acf623..0000000 --- a/libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.pde +++ /dev/null @@ -1,288 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example RF Radio Ping Pair which Sleeps between Sends - * - * This is an example of how to use the RF24 class to create a battery- - * efficient system. It is just like the pingpair.pde example, but the - * ping node powers down the radio and sleeps the MCU after every - * ping/pong cycle. - * - * As with the pingpair.pde example, write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. - */ - -#include -#include -#include -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Sleep declarations -// - -typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e; - -void setup_watchdog(uint8_t prescalar); -void do_sleep(void); - -const short sleep_cycles_per_transmission = 4; -volatile short sleep_cycles_remaining = sleep_cycles_per_transmission; - -// -// Normal operation -// - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Prepare sleep parameters - // - - // Only the ping out role sleeps. Wake up every 4s to send a ping - if ( role == role_ping_out ) - setup_watchdog(wdt_1s); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // - // Shut down the system - // - - // Experiment with some delay here to see if it has an effect - delay(500); - - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - radio.powerDown(); - - // Sleep the MCU. The watchdog timer will awaken in a short while, and - // continue execution here. - while( sleep_cycles_remaining ) - do_sleep(); - - sleep_cycles_remaining = sleep_cycles_per_transmission; - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - // This is untouched from the pingpair example. - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it. Include our time, because the ping_out millis counter is unreliable - // due to it sleeping - printf("Got payload %lu @ %lu...",got_time,millis()); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} - -// -// Sleep helpers -// - -// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms -// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec - -void setup_watchdog(uint8_t prescalar) -{ - prescalar = min(9,prescalar); - uint8_t wdtcsr = prescalar & 7; - if ( prescalar & 8 ) - wdtcsr |= _BV(WDP3); - - MCUSR &= ~_BV(WDRF); - WDTCSR = _BV(WDCE) | _BV(WDE); - WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); -} - -ISR(WDT_vect) -{ - --sleep_cycles_remaining; -} - -void do_sleep(void) -{ - set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here - sleep_enable(); - - sleep_mode(); // System sleeps here - - sleep_disable(); // System continues execution here when watchdog timed out - radio.powerUp() ; -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_sleepy/printf.h b/libraries/RF24/examples/pingpair_sleepy/printf.h deleted file mode 100644 index 1b853db..0000000 --- a/libraries/RF24/examples/pingpair_sleepy/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/scanner/Jamfile b/libraries/RF24/examples/scanner/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/libraries/RF24/examples/scanner/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/scanner/output/core.a b/libraries/RF24/examples/scanner/output/core.a new file mode 100644 index 0000000000000000000000000000000000000000..6ae105d90023b9ce818bcb755ee9245f24c98185 GIT binary patch literal 187630 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 NkD<2iO@I%8{}ZTDOPT-x literal 0 HcmV?d00001 diff --git a/libraries/RF24/examples/scanner/output/scanner.cpp b/libraries/RF24/examples/scanner/output/scanner.cpp new file mode 100644 index 0000000..5eb1627 --- /dev/null +++ b/libraries/RF24/examples/scanner/output/scanner.cpp @@ -0,0 +1,128 @@ +#include +#line 1 "scanner.pde" + +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// +// Channel info +// + +const short num_channels = 128; +short values[num_channels]; + +// +// Setup +// + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/scanner/\n\r"); + + // + // Setup and configure rf radio + // + + radio.begin(); + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + // Print out header, high then low digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n\r"); + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n\r"); +} + +// +// Loop +// + +const short num_reps = 100; + +void loop(void) +{ + // Clear measurement values + memset(values,0,num_channels); + + // Scan all channels num_reps times + int rep_counter = num_reps; + while (rep_counter--) + { + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(128); + radio.stopListening(); + + // Did we get a carrier? + if ( radio.testCarrier() ) + ++values[i]; + } + } + + // Print out channel measurements, clamped to a single hex digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,values[i]&0xf)); + ++i; + } + printf("\n\r"); +} + + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/scanner/output/scanner.elf b/libraries/RF24/examples/scanner/output/scanner.elf new file mode 100644 index 0000000000000000000000000000000000000000..5e0f4b153bcd3684fc9008dac0bf38d4a5fbfd97 GIT binary patch literal 100436 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 ZC5_yqj3YXVcR0Mu?yd|2_9q0v{|C_~feiov literal 0 HcmV?d00001 diff --git a/libraries/RF24/examples/scanner/output/scanner.hex b/libraries/RF24/examples/scanner/output/scanner.hex new file mode 100644 index 0000000..45c37db --- /dev/null +++ b/libraries/RF24/examples/scanner/output/scanner.hex @@ -0,0 +1,325 @@ +:100000000C9463000C948B000C948B000C948B006C +:100010000C948B000C948B000C948B000C948B0034 +:100020000C948B000C948B000C948B000C948B0024 +:100030000C948B000C948B000C948B000C948B0014 +:100040000C948F050C948B000C9423060C948B005D +:100050000C948B000C948B000C948B000C948B00F4 +:100060000C948B000C948B000000000024002700EF +:100070002A0000000000250028002B0000000000DE +:1000800023002600290004040404040404040202DA +:100090000202020203030303030301020408102007 +:1000A0004080010204081020010204081020000012 +:1000B0000007000201000003040600000000000029 +:1000C000000090043B0711241FBECFEFD8E0DEBF35 +:1000D000CDBF11E0A0E0B1E0E6EFF3E102C0059092 +:1000E0000D92AA33B107D9F712E0AAE3B1E001C03B +:1000F0001D92A13FB107E1F710E0C6ECD0E004C0CB +:100100002297FE010E94F509C23CD107C9F70E945F +:100110001C060C94F9090C9400000F931F93CF93C5 +:10012000DF938C01EB01009731F46115710519F42F +:1001300020E030E038C081E090E06EE070E00E94A6 +:10014000CB02FC019C01009771F180E8838320972A +:1001500071F0D387C28781E883838091E702909111 +:10016000E802892B21F4F093E802E093E7020115FD +:100170001105C9F011870087838182608383809194 +:10018000E9029091EA02892B71F4F093EA02E0937C +:10019000E9028091EB029091EC02892B21F4F0931B +:1001A000EC02E093EB02C901DF91CF911F910F9117 +:1001B0000895A0E0B0E0EFEDF0E00C94CC09FE0172 +:1001C0003596619171918091E9029091EA02AF01B7 +:1001D0000E94EE002096E2E00C94E809ABE0B0E06B +:1001E000E4EFF0E00C94BC093C012B015A01FC0146 +:1001F00017821682838181FD03C06FEF7FEFC6C136 +:100200009AE0892E1E010894211C311CF3012381E0 +:10021000F20123FD859123FF81912F01882309F4A9 +:10022000B2C1853239F423FD859123FF81912F01DD +:10023000853229F490E0B3010E940604E7CF982F9D +:10024000FF24EE249924FFE1FF15D0F09B3269F0E2 +:100250009C3228F4903259F0933291F40EC09D32C2 +:1002600049F0903369F441E024C052E0F52A84E07B +:10027000F82A28C098E0F92A25C0E0E1FE2A22C029 +:10028000F7FC29C0892F80538A3070F4F6FE05C030 +:10029000989C902C1124980E15C0E89CE02C1124F9 +:1002A000E80EF0E2FF2A0EC09E3229F4F6FC6BC184 +:1002B00040E4F42A07C09C3619F450E8F52A02C03D +:1002C000983649F4F20123FD959123FF91912F0176 +:1002D000992309F0B8CF892F8554833018F08052C4 +:1002E000833038F444E050E0A40EB51E5FE3598338 +:1002F0000FC0933631F0933779F0933509F056C03B +:1003000020C0F5018081898342E050E0A40EB51E33 +:10031000610101E010E012C0F501C080D180F6FC5F +:1003200003C06FEF7FEF02C0692D70E042E050E044 +:10033000A40EB51EC6010E94FB038C015FE7F522E7 +:1003400014C0F501C080D180F6FC03C06FEF7FEFD1 +:1003500002C0692D70E042E050E0A40EB51EC60157 +:100360000E94E9038C0150E8F52AF3FE07C01AC089 +:1003700080E290E0B3010E940604EA948E2D90E0A2 +:1003800008171907A8F30EC0F601F7FC8591F7FED0 +:1003900081916F0190E0B3010E940604E110EA949C +:1003A000015010400115110579F7EAC0943611F09B +:1003B000993669F5F7FE08C0F50120813181428147 +:1003C000538184E090E00AC0F501808191819C0115 +:1003D000442737FD4095542F82E090E0A80EB91EC7 +:1003E0009FE6F92257FF09C0509540953095219519 +:1003F0003F4F4F4F5F4FE0E8FE2ACA01B901A1010C +:100400002AE030E00E943204D82ED21840C095373E +:1004100029F41F2D1F7E2AE030E01DC01F2D197FFB +:100420009F3661F0903720F4983509F0ACC00FC0CA +:10043000903739F0983709F0A6C004C028E030E0C2 +:100440000AC0106114FD146020E130E004C014FD06 +:10045000166020E132E017FF08C0F501608171816C +:100460008281938144E050E008C0F5018081918150 +:10047000BC0180E090E042E050E0A40EB51EA10176 +:100480000E943204D82ED2188FE7F82EF122F6FE01 +:100490000BC05EEFF522D91438F4F4FE07C0F2FC6D +:1004A00005C08FEEF82202C01D2D01C0192DF4FEEB +:1004B0000DC0FE01ED0DF11D8081803319F499EE20 +:1004C000F92208C01F5FF2FE05C003C08F2D867899 +:1004D00009F01F5F0F2DF3FC14C0F0FE0FC01E15B6 +:1004E00010F09D2C0BC09D2C9E0C911A1E2D06C049 +:1004F00080E290E0B3010E9406041F5F1E15C0F366 +:1005000004C01E1510F4E11A01C0EE2404FF0FC050 +:1005100080E390E0B3010E94060402FF1DC001FDCC +:1005200003C088E790E00EC088E590E00BC0802F04 +:10053000867891F001FF02C08BE201C080E2F7FCF7 +:100540008DE290E0B3010E94060406C080E390E0D3 +:10055000B3010E9406049A94D914C0F3DA94F1010D +:10056000ED0DF11D808190E0B3010E940604DD20B5 +:10057000A9F706C080E290E0B3010E940604EA9465 +:10058000EE20C1F743CEF30166817781CB012B9634 +:10059000E2E10C94D8090F931F93CF93DF93689FE8 +:1005A0008001699F100D789F100D1124C8010E94D1 +:1005B000E702EC01009729F060E070E0A8010E94DA +:1005C000F403CE01DF91CF911F910F910895CF9346 +:1005D000DF93BC018230910510F462E070E0A091DD +:1005E000EF02B091F002ED01E0E0F0E040E050E019 +:1005F00021C0888199818617970769F48A819B8138 +:10060000309719F09383828304C09093F002809313 +:10061000EF02FE0134C06817790738F4411551051F +:1006200019F08417950708F4AC01FE018A819B81BB +:100630009C01E9012097E9F641155105A9F1CA018C +:10064000861B970B049708F4BA01E0E0F0E02AC09B +:100650008D919C91119784179507F9F4641775078C +:1006600081F412968D919C911397309719F0938392 +:10067000828304C09093F0028093EF02FD013296D2 +:100680004CC0CA01861B970BFD01E80FF91F61934F +:10069000719302978D939C9340C0FD018281938159 +:1006A0009C01D9011097A1F68091ED029091EE0284 +:1006B000892B41F480912301909124019093EE02C3 +:1006C0008093ED024091250150912601411551057D +:1006D00041F44DB75EB78091210190912201481BF2 +:1006E000590B2091ED023091EE02CA01821B930B4F +:1006F0008617970780F0AB014E5F5F4F8417950711 +:1007000050F0420F531F5093EE024093ED02F90157 +:100710006193719302C0E0E0F0E0CF01DF91CF91EF +:100720000895CF93DF93009709F450C0EC0122970E +:100730001B821A82A091EF02B091F002109709F18A +:1007400040E050E0AC17BD0708F1BB83AA83FE016F +:1007500021913191E20FF31FAE17BF0779F48D910C +:100760009C911197280F391F2E5F3F4F39832883A3 +:1007700012968D919C9113979B838A834115510505 +:1007800071F4D093F002C093EF0220C012968D91C5 +:100790009C911397AD01009711F0DC01D3CFFA01C2 +:1007A000D383C28321913191E20FF31FCE17DF076C +:1007B00069F488819981280F391F2E5F3F4FFA0114 +:1007C000318320838A819B8193838283DF91CF91C0 +:1007D0000895FC010590615070400110D8F7809594 +:1007E00090958E0F9F1F0895DC0101C06D934150BD +:1007F0005040E0F70895FC016150704001900110F5 +:10080000D8F7809590958E0F9F1F08950F931F9393 +:10081000CF93DF938C01EB018B8181FF1BC082FFA3 +:100820000DC02E813F818C819D812817390764F48A +:10083000E881F9810193F983E88306C0E885F985A9 +:10084000802F0995892B31F48E819F8101969F839A +:100850008E8302C00FEF1FEFC801DF91CF911F9170 +:100860000F910895FA01AA27283051F1203181F122 +:10087000E8946F936E7F6E5F7F4F8F4F9F4FAF4FA8 +:10088000B1E03ED0B4E03CD0670F781F891F9A1FBB +:10089000A11D680F791F8A1F911DA11D6A0F711D6F +:1008A000811D911DA11D20D009F468943F912AE07B +:1008B000269F11243019305D3193DEF6CF01089563 +:1008C000462F4770405D4193B3E00FD0C9F7F6CF94 +:1008D000462F4F70405D4A3318F0495D31FD40525C +:1008E000419302D0A9F7EACFB4E0A69597958795F2 +:1008F00077956795BA95C9F70097610571050895D1 +:100900009B01AC010A2E069457954795379527957C +:10091000BA95C9F7620F731F841F951FA01D089514 +:100920008AE391E068E049E00E9475070895FF922C +:100930000F931F93CF93DF9380E8E7E4F1E0DF01AB +:100940001D928A95E9F704E610E021C08AE391E060 +:100950006F2D0E94F2078AE391E00E94840880E8EC +:1009600090E00E94D7058AE391E00E947C078AE329 +:1009700091E00E944608882329F088819981019698 +:10098000998388832297FA94BFEFFB16F9F60150FA +:100990001040EFEF0F3F1E0729F0C5E4D2E08FE7CC +:1009A000F82ED4CFC7E4D1E000E011E000D000D0B1 +:1009B000ADB7BEB712961C930E931197899199917A +:1009C0008F70907014969C938E9313970E94D90009 +:1009D0000F900F900F900F90B2E0C734DB0731F704 +:1009E00000D083E091E0EDB7FEB7928381830E944F +:1009F000D9000F900F90DF91CF911F910F91FF9031 +:100A0000089580E895E060E070E00E948D00089510 +:100A10000F931F93CF93DF9384ED92E040E051EE6C +:100A200060E070E00E9454060E94010500D086E05C +:100A300091E0EDB7FEB7928381830E94D9000F90B9 +:100A40000F908AE391E00E944B088AE391E060E016 +:100A50000E94E8078AE391E00E9484088AE391E01B +:100A60000E947C07C0E0D0E000E011E000D000D0A0 +:100A7000EDB7FEB712830183CE0124E095958795EB +:100A80002A95E1F7948383830E94D90021960F90E1 +:100A90000F900F900F90C038D10541F700D083E040 +:100AA00091E0EDB7FEB7928381830E94D900C0E048 +:100AB000D0E00F900F9000D000D0EDB7FEB70183CB +:100AC0001283CE018F709070948383830E94D9002B +:100AD00021960F900F900F900F90C038D10559F7C5 +:100AE00000D083E091E0EDB7FEB7928381830E944E +:100AF000D9000F900F90DF91CF911F910F91089522 +:100B00000F931F93082F84ED92E0602F0E94280717 +:100B1000112707FD1095C8011F910F9108951F928D +:100B20000F920FB60F9211242F933F938F939F93A1 +:100B3000AF93BF9380914B0290914C02A0914D02D4 +:100B4000B0914E0230914F020196A11DB11D232F8D +:100B50002D5F2D3720F02D570196A11DB11D20933B +:100B60004F0280934B0290934C02A0934D02B0939E +:100B70004E028091470290914802A0914902B091A3 +:100B80004A020196A11DB11D80934702909348022D +:100B9000A0934902B0934A02BF91AF919F918F9168 +:100BA0003F912F910F900FBE0F901F9018950197B6 +:100BB00039F0880F991F880F991F02970197F1F755 +:100BC0000895789484B5826084BD84B5816084BDC5 +:100BD00085B5826085BD85B5816085BDEEE6F0E0B6 +:100BE000808181608083E1E8F0E010828081826012 +:100BF0008083808181608083E0E8F0E08081816093 +:100C00008083E1EBF0E0808184608083E0EBF0E0C2 +:100C1000808181608083EAE7F0E080818460808366 +:100C20008081826080838081816080838081806810 +:100C300080831092C10008950E94E1050E9408057A +:100C40000E949704FDCF1F920F920FB60F921124AE +:100C50002F933F934F938F939F93EF93FF934091E5 +:100C6000C600E091D002F091D10231969F012F771A +:100C7000307031978091D2029091D30228173907B2 +:100C800039F0E05BFD4F40833093D1022093D002D6 +:100C9000FF91EF919F918F914F913F912F910F90E5 +:100CA0000FBE0F901F901895AF92BF92DF92EF92F8 +:100CB000FF920F931F93CF93DF93EC017A018B0187 +:100CC000DD24403081EE580780E0680780E0780737 +:100CD00011F0DD24D39491E0A92EB12CE885F9859B +:100CE000DD2069F0C5010A8802C0880F991F0A94A7 +:100CF000E2F7808360E079E08DE390E005C0108248 +:100D000060E874E88EE190E0A80197010E949A09DA +:100D10002150304040405040569547953795279593 +:100D200080E12030380720F0DD2011F0DD24D6CF1F +:100D3000EC81FD813083EE81FF812083EA85FB8594 +:100D4000208141E050E0CA010E8402C0880F991F43 +:100D50000A94E2F7282B2083EA85FB852081CA01CB +:100D60000F8402C0880F991F0A94E2F7282B208372 +:100D7000EA85FB858081088802C0440F551F0A94CC +:100D8000E2F7842B8083DF91CF911F910F91FF9029 +:100D9000EF90DF90BF90AF900895DC011296ED9137 +:100DA000FC911397E058FF4F2191319180819181FF +:100DB000281B390B2F773070C9010895DC0112967A +:100DC000ED91FC911397EE57FF4F20813181929165 +:100DD0008291E058F0408217930719F42FEF3FEF0C +:100DE00005C0E20FF31F8081282F30E0C90108956C +:100DF000DC011296ED91FC911397DF01AE57BF4FC6 +:100E00002D913C911197E058FF4F80819181E058DE +:100E1000F0408217930719F42FEF3FEF0BC0E20F5A +:100E2000F31F80812F5F3F4F2F7730702D933C93BE +:100E3000282F30E0C9010895DC011296ED91FC9154 +:100E40001397EE57FF4F808191819293829308957B +:100E5000FC01A085B18521898C9190E0022E02C011 +:100E6000959587950A94E2F780FFF6CF0484F5857F +:100E7000E02D608308958BE291E09093D5028093FA +:100E8000D40280E592E09093D7028093D60285EC5D +:100E900090E09093D9028093D80284EC90E09093F4 +:100EA000DB028093DA0280EC90E09093DD02809385 +:100EB000DC0281EC90E09093DF028093DE0286EC0E +:100EC00090E09093E1028093E00284E08093E2025C +:100ED00083E08093E30287E08093E40285E08093DF +:100EE000E50281E08093E6020895FC01608341837E +:100EF00080E2828313820895FC01808160E00E9479 +:100F0000CD080895FF920F931F938C01F62E80E079 +:100F10000E94650985E00E946A09F80181816F2DB0 +:100F20000E94CD081F910F91FF9008951F93CF93BA +:100F3000DF93EC0160E070E00E94820781EE8EBDDD +:100F40000DB407FEFDCF1EB5CE0161E070E00E943A +:100F50008207812FDF91CF911F9108951F93CF9327 +:100F6000DF93EC0160E070E00E94820782EE8EBDAC +:100F70000DB407FEFDCF1EB5CE0161E070E00E940A +:100F80008207812FDF91CF911F9108950F931F93B7 +:100F9000CF93DF93EC01162F042F60E070E00E94E6 +:100FA00082071F7110621EBD0DB407FEFDCF1EB576 +:100FB0000EBD0DB407FEFDCF8EB5CE0161E070E031 +:100FC0000E948207812FDF91CF911F910F91089589 +:100FD000662319F061E04FE302C061E040E00E9447 +:100FE000C6070895462F603808F04FE765E00E9475 +:100FF000C6070895EF92FF920F931F93CF93DF934D +:101000007C01162FEA01022F60E070E00E94820747 +:101010001F7110621EBD0DB407FEFDCF1EB508C0C6 +:1010200088818EBD0DB407FEFDCF21968EB501508F +:101030000023B1F7C70161E070E00E948207812FB1 +:10104000DF91CF911F910F91FF90EF9008951F9323 +:10105000CF93DF93EC01162F60E070E00E948207CF +:101060001F711EBD0DB407FEFDCF8EB58FEF8EBD77 +:101070000DB407FEFDCF1EB5CE0161E070E00E9409 +:101080008207812FDF91CF911F91089569E00E941F +:101090002708817008950F931F938C01FC018081B4 +:1010A00061E00E94A708F801818161E00E94A70821 +:1010B000F801808160E00E94CD08C80161E070E025 +:1010C0000E9482070E947B0981E00E945C0980E007 +:1010D0000E94650985E00E946A09C80164E04FEF3B +:1010E0000E94C607C80167E040E70E94C607C80122 +:1010F0000E94AE07C8010E949607C80161E00E94E5 +:10110000F2071F910F9108950F931F938C0160E0D8 +:101110004BE00E94C607C80167E040E70E94C6078F +:10112000A8014B5F5F4FC8016AE025E00E94FA0703 +:10113000C8010E94AE07F801808161E00E94CD08DD +:1011400082E890E00E94D7051F910F910895482FE3 +:1011500050E0CA0186569F4FFC0124914A575F4FC9 +:10116000FA0184918823C1F0E82FF0E0EE0FFF1F11 +:10117000E859FF4FA591B491662341F49FB7F894C5 +:101180008C91209582238C939FBF08959FB7F894EC +:101190008C91822B8C939FBF0895482F50E0CA01F9 +:1011A00082559F4FFC012491CA0186569F4FFC0136 +:1011B00034914A575F4FFA019491992309F444C03E +:1011C000222351F1233071F0243028F42130A1F092 +:1011D000223011F514C02630B1F02730C1F0243090 +:1011E000D9F404C0809180008F7703C08091800083 +:1011F0008F7D8093800010C084B58F7702C084B546 +:101200008F7D84BD09C08091B0008F7703C080912D +:10121000B0008F7D8093B000E92FF0E0EE0FFF1F4C +:10122000EE58FF4FA591B491662341F49FB7F8940F +:101230008C91309583238C939FBF08959FB7F8942A +:101240008C91832B8C939FBF08950F931F93CF9303 +:10125000DF938C01EB0109C02196D801ED91FC913F +:101260000190F081E02DC801099568816623A1F7FE +:10127000DF91CF911F910F910895EF92FF920F93FD +:101280001F93CF93DF938C017B01EA010CC0D70140 +:101290006D917D01D801ED91FC910190F081E02DDF +:1012A000C80109952197209791F7DF91CF911F9160 +:1012B0000F91FF90EF900895882319F48CB5806208 +:1012C00002C08CB58F7D8CBD08959CB5937F982B03 +:1012D0009CBD08952CB5382F33702C7F322B3CBD2C +:1012E0002DB590E0959587959595879581702E7F82 +:1012F000822B8DBD08958DE061E00E94A7088BE0F0 +:1013000061E00E94A7088AE061E00E94A7088DE0E2 +:1013100060E00E94CD088BE060E00E94CD088AE08A +:1013200061E00E94CD088CB580618CBD8CB5806475 +:101330008CBD0895A1E21A2EAA1BBB1BFD010DC096 +:10134000AA1FBB1FEE1FFF1FA217B307E407F50775 +:1013500020F0A21BB30BE40BF50B661F771F881F51 +:10136000991F1A9469F760957095809590959B01E7 +:10137000AC01BD01CF0108952F923F924F925F9231 +:101380006F927F928F929F92AF92BF92CF92DF9295 +:10139000EF92FF920F931F93CF93DF93CDB7DEB7FA +:1013A000CA1BDB0B0FB6F894DEBF0FBECDBF09948E +:1013B0002A88398848885F846E847D848C849B84E5 +:1013C000AA84B984C884DF80EE80FD800C811B81F3 +:1013D000AA81B981CE0FD11D0FB6F894DEBF0FBE22 +:1013E000CDBFED010895EE0FFF1F0590F491E02DA4 +:0613F0000994F894FFCF00 +:1013F6002578000A0D000A0D524632342F657861B1 +:101406006D706C65732F7363616E6E65722F0A0D56 +:10141600002000F102000000000000280725093D19 +:0A14260009CD06F806DE061C0700DB +:00000001FF diff --git a/libraries/RF24/examples/scanner/printf.h b/libraries/RF24/examples/scanner/printf.h deleted file mode 100644 index 1b853db..0000000 --- a/libraries/RF24/examples/scanner/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/scanner/scanner.pde b/libraries/RF24/examples/scanner/scanner.pde deleted file mode 100644 index 8f93174..0000000 --- a/libraries/RF24/examples/scanner/scanner.pde +++ /dev/null @@ -1,138 +0,0 @@ - -/* - Copyright (C) 2011 James Coliz, Jr. - Copyright (c) 2012 Greg Copeland - - 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. - */ - -/** - * Channel scanner - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -#include -#include "RF24.h" -#include "printf.h" - -// Only display active frequencies -static const bool activeOnly = true ; - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(9,10); - -// -// Channel info -// - -const short num_channels = 128; -short values[num_channels]; -uint8_t signalMeter[55] ; - -// -// Setup -// - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(115200); - printf_begin(); - printf("\n\rRF24/examples/scanner/\n\r"); - - // - // Setup and configure rf radio - // - - radio.begin(); - radio.powerUp() ; - radio.setAutoAck(false); - - // Get into standby mode - radio.openReadingPipe( 0, 0xFFFFFFFFFFULL ) ; - // radio.setDataRate( RF24_250KBPS ) ; // may fallback to 1Mbps - radio.setDataRate( RF24_1MBPS ) ; // may fallback to 1Mbps - radio.startListening() ; - radio.stopListening() ; -} - -// -// Loop -// -void loop(void) -{ - // Clear measurement values - memset( values, 0x00, num_channels ) ; - printf( "Scanning all available frequencies..." ) ; - - // Repeatedly scan multiple channels - for( int channel=0 ; channel < num_channels; channel++ ) { - radio.setChannel( channel ) ; - - // Amplify the signal based on carrier bandwidth - int ampFactor ; - for( int amp=0; amp <= 300; amp++ ) { - // Alternate data rates - ampFactor = amp%3 ; - switch( ampFactor ) { - case 0: - radio.setDataRate( RF24_250KBPS ) ; - break ; - - case 1: - radio.setDataRate( RF24_1MBPS ) ; - break ; - - default: - radio.setDataRate( RF24_2MBPS ) ; - break ; - } - - // Listen for carrier - ampFactor++ ; - radio.startListening() ; - delayMicroseconds( 6 - ampFactor ) ; - radio.stopListening() ; - - // Was carrier detected? If so, signal level based on bandwidth - if( radio.testRPD() ) { - values[channel] += ampFactor ; - } - } - } - - // Now display our results - printf( "Scan completed.\r\n" ) ; - for( int channel=0 ; channel < num_channels; channel++ ) { - if( !activeOnly || (activeOnly && values[channel] > 0) ) { - memset( signalMeter, '*', min( values[channel], 54 ) ) ; - signalMeter[min(values[channel], 54)] = 0x00 ; - printf( "%03d (%4dMhz): %02d - %s\r\n", - channel, - 2400+channel, - values[channel], - signalMeter ) ; - - // Reset the scanned value since its already beend displayed - values[channel] = 0 ; - } - } -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/starping/Jamfile b/libraries/RF24/examples/starping/Jamfile deleted file mode 100644 index de9b1f6..0000000 --- a/libraries/RF24/examples/starping/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = EEPROM SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/examples/starping/printf.h b/libraries/RF24/examples/starping/printf.h deleted file mode 100644 index 1b853db..0000000 --- a/libraries/RF24/examples/starping/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/starping/starping.pde b/libraries/RF24/examples/starping/starping.pde deleted file mode 100644 index ac5197a..0000000 --- a/libraries/RF24/examples/starping/starping.pde +++ /dev/null @@ -1,293 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Example RF Radio Ping Star Group - * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the - * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will - * respond back with a copy of the value. Each ping unit can get that response - * back, and determine how long the whole cycle took. - * - * This example requires a bit more complexity to determine which unit is which. - * The pong receiver is identified by having its role_pin tied to ground. - * The ping senders are further differentiated by a byte in eeprom. - */ - -#include -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'pong' receiver. -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the nodes to communicate. Only ping nodes need -// dedicated pipes in this topology. Each ping node has a talking pipe -// that it will ping into, and a listening pipe that it will listen for -// the pong. The pong node listens on all the ping node talking pipes -// and sends the pong back on the sending node's specific listening pipe. - -const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; -const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_invalid = 0, role_ping_out, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Address management -// - -// Where in EEPROM is the address stored? -const uint8_t address_at_eeprom_location = 0; - -// What is our address (SRAM cache of the address from EEPROM) -// Note that zero is an INVALID address. The pong back unit takes address -// 1, and the rest are 2-6 -uint8_t node_address; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Address - // - - if ( role == role_pong_back ) - node_address = 1; - else - { - // Read the address from EEPROM - uint8_t reading = EEPROM.read(address_at_eeprom_location); - - // If it is in a valid range for node addresses, it is our - // address. - if ( reading >= 2 && reading <= 6 ) - node_address = reading; - - // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' - else - { - node_address = 0; - role = role_invalid; - } - } - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/starping/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("ADDRESS: %i\n\r",node_address); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // The pong node listens on all the ping node talking pipes - // and sends the pong back on the sending node's specific listening pipe. - if ( role == role_pong_back ) - { - radio.openReadingPipe(1,talking_pipes[0]); - radio.openReadingPipe(2,talking_pipes[1]); - radio.openReadingPipe(3,talking_pipes[2]); - radio.openReadingPipe(4,talking_pipes[3]); - radio.openReadingPipe(5,talking_pipes[4]); - } - - // Each ping node has a talking pipe that it will ping into, and a listening - // pipe that it will listen for the pong. - if ( role == role_ping_out ) - { - // Write on our talking pipe - radio.openWritingPipe(talking_pipes[node_address-2]); - // Listen on our listening pipe - radio.openReadingPipe(1,listening_pipes[node_address-2]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Prompt the user to assign a node address if we don't have one - // - - if ( role == role_invalid ) - { - printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); - } -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - uint8_t pipe_num; - if ( radio.available(&pipe_num) ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu from node %i...",got_time,pipe_num+1); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Open the correct pipe for writing - radio.openWritingPipe(listening_pipes[pipe_num-1]); - - // Retain the low 2 bytes to identify the pipe for the spew - uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response to %04x.\n\r",pipe_id); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } - - // - // Listen for serial input, which is how we set the address - // - if (Serial.available()) - { - // If the character on serial input is in a valid range... - char c = Serial.read(); - if ( c >= '1' && c <= '6' ) - { - // It is our address - EEPROM.write(address_at_eeprom_location,c-'0'); - - // And we are done right now (no easy way to soft reset) - printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); - while(1) ; - } - } -} -// vim:ai:ci sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/keywords.txt b/libraries/RF24/keywords.txt deleted file mode 100644 index 79987eb..0000000 --- a/libraries/RF24/keywords.txt +++ /dev/null @@ -1,26 +0,0 @@ - RF24 KEYWORD1 - begin KEYWORD2 - setDataRate KEYWORD2 - getDataRate KEYWORD2 - powerUp KEYWORD2 - powerDown KEYWORD2 - whatHappened KEYWORD2 - writeAckPayload KEYWORD2 - setChannel KEYWORD2 - setPayloadSize KEYWORD2 - getPayloadSize KEYWORD2 - printDetails KEYWORD2 - startListening KEYWORD2 - stopListening KEYWORD2 - write KEYWORD2 - startWrite KEYWORD2 - available KEYWORD2 - read KEYWORD2 - openWritingPipe KEYWORD2 - openReadingPipe KEYWORD2 - closeReadingPipe KEYWORD2 - enableDynamicPayloads KEYWORD2 - enableAckPayload KEYWORD2 - setAutoAck KEYWORD2 - setCRCLength KEYWORD2 - getCRCLength KEYWORD2 diff --git a/libraries/RF24/nRF24L01.h b/libraries/RF24/nRF24L01.h deleted file mode 100644 index 9943c3a..0000000 --- a/libraries/RF24/nRF24L01.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright (c) 2007 Stefan Engelke - Portions Copyright (C) 2011 Greg Copeland - - 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. -*/ - -/* Memory Map */ -#define CONFIG 0x00 -#define EN_AA 0x01 -#define EN_RXADDR 0x02 -#define SETUP_AW 0x03 -#define SETUP_RETR 0x04 -#define RF_CH 0x05 -#define RF_SETUP 0x06 -#define STATUS 0x07 -#define OBSERVE_TX 0x08 -#define CD 0x09 -#define RX_ADDR_P0 0x0A -#define RX_ADDR_P1 0x0B -#define RX_ADDR_P2 0x0C -#define RX_ADDR_P3 0x0D -#define RX_ADDR_P4 0x0E -#define RX_ADDR_P5 0x0F -#define TX_ADDR 0x10 -#define RX_PW_P0 0x11 -#define RX_PW_P1 0x12 -#define RX_PW_P2 0x13 -#define RX_PW_P3 0x14 -#define RX_PW_P4 0x15 -#define RX_PW_P5 0x16 -#define FIFO_STATUS 0x17 -#define DYNPD 0x1C -#define FEATURE 0x1D - -/* Bit Mnemonics */ -#define MASK_RX_DR 6 -#define MASK_TX_DS 5 -#define MASK_MAX_RT 4 -#define EN_CRC 3 -#define CRCO 2 -#define PWR_UP 1 -#define PRIM_RX 0 -#define ENAA_P5 5 -#define ENAA_P4 4 -#define ENAA_P3 3 -#define ENAA_P2 2 -#define ENAA_P1 1 -#define ENAA_P0 0 -#define ERX_P5 5 -#define ERX_P4 4 -#define ERX_P3 3 -#define ERX_P2 2 -#define ERX_P1 1 -#define ERX_P0 0 -#define AW 0 -#define ARD 4 -#define ARC 0 -#define PLL_LOCK 4 -#define RF_DR 3 -#define RF_PWR 6 -#define RX_DR 6 -#define TX_DS 5 -#define MAX_RT 4 -#define RX_P_NO 1 -#define TX_FULL 0 -#define PLOS_CNT 4 -#define ARC_CNT 0 -#define TX_REUSE 6 -#define FIFO_FULL 5 -#define TX_EMPTY 4 -#define RX_FULL 1 -#define RX_EMPTY 0 -#define DPL_P5 5 -#define DPL_P4 4 -#define DPL_P3 3 -#define DPL_P2 2 -#define DPL_P1 1 -#define DPL_P0 0 -#define EN_DPL 2 -#define EN_ACK_PAY 1 -#define EN_DYN_ACK 0 - -/* Instruction Mnemonics */ -#define R_REGISTER 0x00 -#define W_REGISTER 0x20 -#define REGISTER_MASK 0x1F -#define ACTIVATE 0x50 -#define R_RX_PL_WID 0x60 -#define R_RX_PAYLOAD 0x61 -#define W_TX_PAYLOAD 0xA0 -#define W_ACK_PAYLOAD 0xA8 -#define FLUSH_TX 0xE1 -#define FLUSH_RX 0xE2 -#define REUSE_TX_PL 0xE3 -#define NOP 0xFF - -/* Non-P omissions */ -#define LNA_HCURR 0 - -/* P model memory Map */ -#define RPD 0x09 -#define W_TX_PAYLOAD_NO_ACK 0xB0 - -/* P model bit Mnemonics */ -#define RF_DR_LOW 5 -#define RF_DR_HIGH 3 -#define RF_PWR_LOW 1 -#define RF_PWR_HIGH 2 diff --git a/libraries/RF24/tests/README b/libraries/RF24/tests/README deleted file mode 100644 index 43ceaf5..0000000 --- a/libraries/RF24/tests/README +++ /dev/null @@ -1,7 +0,0 @@ -The sketches in this directory are intended to be checkin tests. -No code should be pushed to github without these tests passing. - -See "runtests.sh" script inside each sketch dir. This script is fully compatible with -git bisest. - -Note that this requires python and py-serial diff --git a/libraries/RF24/tests/native/Jamfile b/libraries/RF24/tests/native/Jamfile deleted file mode 100644 index 10d0336..0000000 --- a/libraries/RF24/tests/native/Jamfile +++ /dev/null @@ -1,300 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -SKETCH_DIR = $(HOME)/Source/Arduino ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(SKETCH_DIR)/libraries ; -AVR_AS = $(AVR_TOOLS_PATH)/avr-as ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H HAL=1 ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -ASFLAGS = -mmcu=$(MCU) ; -CFLAGS = -Os -Wall -Wextra $(ASFLAGS) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; -HDRS += [ GLOB $(HDRS) : utility ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -# GitVersion version.h ; - -rule AvrAsm -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrAsm -{ - $(AVR_AS) $(ASFLAGS) -o $(<) $(>) -} - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule AvrAsmFromC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrAsmFromC++ -{ - $(AVR_CXX) -S -fverbose-asm -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .S : AvrAsm $(<) : $(>) ; - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - -# -# Native -# - -OUT_DIR_NATIVE = out_native ; -OUT_NATIVE = $(OUT_DIR_NATIVE)/$(PROJECT_NAME) ; -NATIVE_CORE = $(SKETCH_DIR)/hardware/native ; -HDRS = $(NATIVE_CORE) $(HDRS) ; -NATIVE_CORE_MODULES = [ GLOB $(NATIVE_CORE) : *.c *.cpp ] ; -NATIVE_MODULES = ; -DEFINES += NATIVE ; - -rule NativePde -{ - local _CPP = $(OUT_DIR_NATIVE)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>) - { - case *.pde : NativePde $(<) : $(>) ; - } -} - -rule Objects -{ - for _I in $(<) - { - local _O = $(OUT_DIR_NATIVE)/$(_I:B).o ; - Object $(_O) : $(_I) ; - } -} - -rule Main -{ - MainFromObjects $(<) : $(OUT_DIR_NATIVE)/$(>:B).o ; - Objects $(>) ; -} - -actions C++ -{ - c++ -c -o $(<) $(CCHDRS) $(CCDEFS) $(>) -} - -actions Link -{ - c++ -o $(<) $(>) -} - - - -MkDir $(OUT_DIR_NATIVE) ; -Depends $(OUT_NATIVE) : $(OUT_DIR_NATIVE) ; -Main $(OUT_NATIVE) : $(NATIVE_CORE_MODULES) $(NATIVE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; - -Depends native : $(OUT_NATIVE) ; - diff --git a/libraries/RF24/tests/native/pingpair_irq.pde b/libraries/RF24/tests/native/pingpair_irq.pde deleted file mode 100644 index 99c2cdf..0000000 --- a/libraries/RF24/tests/native/pingpair_irq.pde +++ /dev/null @@ -1,223 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Interrupt-driven test for native target - * - * This example is the friendliest for the native target because it doesn't do - * any polling. Made a slight change to call done() at the end of setup. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 7; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -// Interrupt handler, check the radio because we got an IRQ -void check_radio(void); - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_irq/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipe for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Attach interrupt handler to interrupt #0 (using pin 2) - // on BOTH the sender and receiver - // - - attachInterrupt(0, check_radio, FALLING); - - // - // On the native target, this is as far as we get - // -#if NATIVE - done(); -#endif -} - -static uint32_t message_count = 0; - -void loop(void) -{ - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender) - { - // Take the time, and send it. - unsigned long time = millis(); - printf("Now sending %lu\n\r",time); - radio.startWrite( &time, sizeof(unsigned long) ); - - // Try again soon - delay(2000); - } - - // - // Receiver role: Does nothing! All the work is in IRQ - // - -} - -void check_radio(void) -{ - // What happened? - bool tx,fail,rx; - radio.whatHappened(tx,fail,rx); - - // Have we successfully transmitted? - if ( tx ) - { - if ( role == role_sender ) - printf("Send:OK\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Sent\n\r"); - } - - // Have we failed to transmit? - if ( fail ) - { - if ( role == role_sender ) - printf("Send:Failed\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Failed\n\r"); - } - - // Transmitter can power down for now, because - // the transmission is done. - if ( ( tx || fail ) && ( role == role_sender ) ) - radio.powerDown(); - - // Did we receive a message? - if ( rx ) - { - // If we're the sender, we've received an ack payload - if ( role == role_sender ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu\n\r",(unsigned long)message_count); - } - - // If we're the receiver, we've received a time message - if ( role == role_receiver ) - { - // Get this payload and dump it - static unsigned long got_time; - radio.read( &got_time, sizeof(got_time) ); - printf("Got payload %lu\n\r",got_time); - - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); - ++message_count; - } - } -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/tests/native/printf.h b/libraries/RF24/tests/native/printf.h deleted file mode 100644 index df6c46a..0000000 --- a/libraries/RF24/tests/native/printf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#include "WProgram.h" - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/tests/pingpair_blocking/Jamfile b/libraries/RF24/tests/pingpair_blocking/Jamfile deleted file mode 100644 index 98ec12f..0000000 --- a/libraries/RF24/tests/pingpair_blocking/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Werror -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde b/libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde deleted file mode 100644 index 504b8c8..0000000 --- a/libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde +++ /dev/null @@ -1,268 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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 -#include "RF24.h" -#include "printf.h" - -// -// Test version of RF24, exposes some protected interface -// - -class RF24Test: public RF24 -{ -public: RF24Test(int a, int b): RF24(a,b) {} -}; - - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24Test radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Test state -// - -bool done; //*< Are we done with the test? */ -bool passed; //*< Have we passed the test? */ -bool notified; //*< Have we notified the user we're done? */ -const int num_needed = 10; //*< How many success/failures until we're done? */ -int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ -int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ -int interval = 100; //*< ms to wait between sends */ - -char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ - -void one_ok(void) -{ - // Have we received enough yet? - if ( ! --receives_remaining ) - { - done = true; - passed = true; - } -} - -void one_failed(void) -{ - // Have we failed enough yet? - if ( ! --failures_remaining ) - { - done = true; - passed = false; - } -} - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/tests/pingpair_blocking/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // get test config - // - - printf("+READY press any key to start\n\r\n\r"); - - while (! Serial.available() ) {} - configuration = Serial.read(); - printf("Configuration\t = %c\n\r",configuration); - - // - // Setup and configure rf radio - // - - radio.begin(); - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout - unsigned long started_waiting_at = micros(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (micros() - started_waiting_at > radio.getMaxTimeout() ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - one_failed(); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - one_ok(); - } - - // Try again later - delay(250); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - delay(20); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - - } - } - - // - // Stop the test if we're done and report results - // - if ( done && ! notified ) - { - notified = true; - - printf("\n\r+OK "); - if ( passed ) - printf("PASS\n\r\n\r"); - else - printf("FAIL\n\r\n\r"); - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/tests/pingpair_blocking/printf.h b/libraries/RF24/tests/pingpair_blocking/printf.h deleted file mode 100644 index 1b853db..0000000 --- a/libraries/RF24/tests/pingpair_blocking/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/tests/pingpair_blocking/runtest.py b/libraries/RF24/tests/pingpair_blocking/runtest.py deleted file mode 100644 index 45fb65c..0000000 --- a/libraries/RF24/tests/pingpair_blocking/runtest.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/opt/local/bin/python - -import sys,serial - -def read_until(token): - while 1: - line = ser.readline(None,"\r") - sys.stdout.write(line) - - if (line.startswith(token)): - break - return line - - -ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) - -read_until("+READY") -ser.write(sys.argv[2]) - -line = read_until("+OK") -ser.close() -if (line.find("PASS") != -1): - sys.exit(0) -else: - sys.exit(1) diff --git a/libraries/RF24/tests/pingpair_blocking/runtests.sh b/libraries/RF24/tests/pingpair_blocking/runtests.sh deleted file mode 100644 index 53b16f9..0000000 --- a/libraries/RF24/tests/pingpair_blocking/runtests.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# Connect p6 to receiver, p4 to sender - -jam p4 p6 || exit 1 -./runtest.py /dev/tty.usbserial-A600eHIs 1 & -./runtest.py /dev/tty.usbserial-A40081RP 1 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -exit 0 diff --git a/libraries/RF24/tests/pingpair_blocking/test.ex b/libraries/RF24/tests/pingpair_blocking/test.ex deleted file mode 100644 index ea992ad..0000000 --- a/libraries/RF24/tests/pingpair_blocking/test.ex +++ /dev/null @@ -1,11 +0,0 @@ -#/usr/bin/expect - -set timeout 100 -spawn picocom -b 57600 /dev/ttyUSB0 -expect "+READY" -send "1" -expect "+OK" -spawn picocom -b 57600 /dev/ttyUSB1 -expect "+READY" -send "1" -expect "+OK" diff --git a/libraries/RF24/tests/pingpair_test/Jamfile b/libraries/RF24/tests/pingpair_test/Jamfile deleted file mode 100644 index 98ec12f..0000000 --- a/libraries/RF24/tests/pingpair_test/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Werror -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/libraries/RF24/tests/pingpair_test/pingpair_test.pde b/libraries/RF24/tests/pingpair_test/pingpair_test.pde deleted file mode 100644 index 403c195..0000000 --- a/libraries/RF24/tests/pingpair_test/pingpair_test.pde +++ /dev/null @@ -1,395 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Full test on single RF pair - * - * This sketches uses as many RF24 methods as possible in a single test. - * - * To operate: - * Upload this sketch on two nodes, each with IRQ -> pin 2 - * One node needs pin 7 -> GND, the other NC. That's the receiving node - * Monitor the sending node's serial output - * Look for "+OK PASS" or "+OK FAIL" - */ - -#include -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 7; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -// Interrupt handler, check the radio because we got an IRQ -void check_radio(void); - -// -// Payload -// - -const int min_payload_size = 4; -const int max_payload_size = 32; -int payload_size_increments_by = 2; -int next_payload_size = min_payload_size; - -char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char - -// -// Test state -// - -bool done; //*< Are we done with the test? */ -bool passed; //*< Have we passed the test? */ -bool notified; //*< Have we notified the user we're done? */ -const int num_needed = 10; //*< How many success/failures until we're done? */ -int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ -int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ -int interval = 100; //*< ms to wait between sends */ - -char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ - -void one_ok(void) -{ - // Have we received enough yet? - if ( ! --receives_remaining ) - { - done = true; - passed = true; - } -} - -void one_failed(void) -{ - // Have we failed enough yet? - if ( ! --failures_remaining ) - { - done = true; - passed = false; - } -} - -// -// Setup -// - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/tests/pingpair_test/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Read configuration from serial - // - // It would be a much better test if this program could accept configuration - // from the serial port. Then it would be possible to run the same test under - // lots of different circumstances. - // - // The idea is that we will print "+READY" at this point. The python script - // will wait for it, and then send down a configuration script that we - // execute here and then run with. - // - // The test controller will need to configure the receiver first, then go run - // the test on the sender. - // - - printf("+READY press any key to start\n\r\n\r"); - - while (! Serial.available() ) {} - configuration = Serial.read(); - printf("Configuration\t = %c\n\r",configuration); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // Config 2 is special radio config - if (configuration=='2') - { - radio.setCRCLength(RF24_CRC_8); - radio.setDataRate(RF24_250KBPS); - radio.setChannel(10); - } - else - { - //Otherwise, default radio config - - // Optional: Increase CRC length for improved reliability - radio.setCRCLength(RF24_CRC_16); - - // Optional: Decrease data rate for improved reliability - radio.setDataRate(RF24_1MBPS); - - // Optional: Pick a high channel - radio.setChannel(90); - } - - // Config 3 is static payloads only - if (configuration == '3') - { - next_payload_size = 16; - payload_size_increments_by = 0; - radio.setPayloadSize(next_payload_size); - } - else - { - // enable dynamic payloads - radio.enableDynamicPayloads(); - } - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipe for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - radio.printDetails(); - - // - // Attach interrupt handler to interrupt #0 (using pin 2) - // on BOTH the sender and receiver - // - delay(40) ; - attachInterrupt(0, check_radio, FALLING); -} - -// -// Loop -// - -static uint32_t message_count = 0; -static uint32_t last_message_count = 0; - -void loop(void) -{ - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender && !done) - { - // The payload will always be the same, what will change is how much of it we send. - static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Send it. This will block until complete - radio.powerUp() ; - printf("\n\rNow sending length %i...",next_payload_size); - radio.startWrite( send_payload, next_payload_size ); - - // Update size for next time. - next_payload_size += payload_size_increments_by; - if ( next_payload_size > max_payload_size ) - next_payload_size = min_payload_size; - - // Try again soon - interval = 1 + (radio.getMaxTimeout()/1000) ; - delay(interval); - - // Timeout if we have not received anything back ever - if ( ! last_message_count && millis() > interval * 10 ) - { - printf("No responses received. Are interrupts connected??\n\r"); - done = true; - } - } - - // - // Receiver role: Does nothing! All the work is in IRQ - // - - // - // Stop the test if we're done and report results - // - if ( done && ! notified ) - { - notified = true; - - printf("\n\r+OK "); - if ( passed ) - printf("PASS\n\r\n\r"); - else - printf("FAIL\n\r\n\r"); - } - - // - // -} - -void check_radio(void) -{ - // What happened? - bool tx,fail,rx; - radio.whatHappened(tx,fail,rx); - - // Have we successfully transmitted? - if ( tx ) - { - if ( role == role_sender ) - printf("Send:OK "); - - if ( role == role_receiver ) - printf("Ack Payload:Sent\n\r"); - } - - // Have we failed to transmit? - if ( fail ) - { - if ( role == role_sender ) - { - printf("Send:Failed "); - - // log status of this line - one_failed(); - } - - if ( role == role_receiver ) - printf("Ack Payload:Failed\n\r"); - } - - // Transmitter can power down for now, because - // the transmission is done. - if ( ( tx || fail ) && ( role == role_sender ) ) - radio.powerDown(); - - // Did we receive a message? - if ( rx ) - { - // If we're the sender, we've received an ack payload - if ( role == role_sender ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu ",message_count); - - // is this ack what we were expecting? to account - // for failures, we simply want to make sure we get a - // DIFFERENT ack every time. - if ( ( message_count != last_message_count ) || ( configuration=='3' && message_count == 16 ) ) - { - printf("OK "); - one_ok(); - } - else - { - printf("FAILED "); - one_failed(); - } - last_message_count = message_count; - } - - // If we're the receiver, we've received a time message - if ( role == role_receiver ) - { - // Get this payload and dump it - size_t len = max_payload_size; - memset(receive_payload,0,max_payload_size); - - if ( configuration == '3' ) - len = next_payload_size; - else - len = radio.getDynamicPayloadSize(); - - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got payload size=%i value=%s strlen=%u\n\r",len,receive_payload,strlen(receive_payload)); - - // Add an ack packet for the next time around. - // Here we will report back how many bytes we got this time. - radio.writeAckPayload( 1, &len, sizeof(len) ); - ++message_count; - } - } -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/tests/pingpair_test/printf.h b/libraries/RF24/tests/pingpair_test/printf.h deleted file mode 100644 index 1b853db..0000000 --- a/libraries/RF24/tests/pingpair_test/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/libraries/RF24/tests/pingpair_test/runtest.py b/libraries/RF24/tests/pingpair_test/runtest.py deleted file mode 100644 index 45fb65c..0000000 --- a/libraries/RF24/tests/pingpair_test/runtest.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/opt/local/bin/python - -import sys,serial - -def read_until(token): - while 1: - line = ser.readline(None,"\r") - sys.stdout.write(line) - - if (line.startswith(token)): - break - return line - - -ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) - -read_until("+READY") -ser.write(sys.argv[2]) - -line = read_until("+OK") -ser.close() -if (line.find("PASS") != -1): - sys.exit(0) -else: - sys.exit(1) diff --git a/libraries/RF24/tests/pingpair_test/runtests.sh b/libraries/RF24/tests/pingpair_test/runtests.sh deleted file mode 100644 index 76f8f77..0000000 --- a/libraries/RF24/tests/pingpair_test/runtests.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# Connect p6 to receiver, p4 to sender -# WARNING: Test config 2 only works with PLUS units. - -jam p4 p6 || exit 1 -./runtest.py /dev/tty.usbserial-A600eHIs 1 & -./runtest.py /dev/tty.usbserial-A40081RP 1 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -./runtest.py /dev/tty.usbserial-A600eHIs 2 & -./runtest.py /dev/tty.usbserial-A40081RP 2 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -./runtest.py /dev/tty.usbserial-A600eHIs 3 & -./runtest.py /dev/tty.usbserial-A40081RP 3 || ( kill `jobs -p` && exit 1 ) -kill `jobs -p` -exit 0 diff --git a/libraries/RF24/tests/pingpair_test/test.ex b/libraries/RF24/tests/pingpair_test/test.ex deleted file mode 100644 index a14ffef..0000000 --- a/libraries/RF24/tests/pingpair_test/test.ex +++ /dev/null @@ -1,11 +0,0 @@ -#/usr/bin/expect - -set timeout 100 -spawn picocom -b 57600 /dev/ttyUSB0 -expect "+READY" -send [lindex $argv 0] -expect "+OK" -spawn picocom -b 57600 /dev/ttyUSB1 -expect "+READY" -send [lindex $argv 0] -expect "+OK" diff --git a/libraries/RF24/wikidoc.xslt b/libraries/RF24/wikidoc.xslt deleted file mode 100644 index b94d3ef..0000000 --- a/libraries/RF24/wikidoc.xslt +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - === === - - - - - '''' - -Parameters: - - - - * '''': - - - - - -Returns: - -* - - -Warning: - - - - <pre> </pre> - - - - - - - From de0a374699aed8b62760751be0768b465c1ff395 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 30 Apr 2014 23:57:29 +0200 Subject: [PATCH 232/326] remove old rf24 --- libraries/RF24/examples/scanner/output/core.a | Bin 187630 -> 0 bytes .../RF24/examples/scanner/output/scanner.cpp | 128 ------- .../RF24/examples/scanner/output/scanner.elf | Bin 100436 -> 0 bytes .../RF24/examples/scanner/output/scanner.hex | 325 ------------------ 4 files changed, 453 deletions(-) delete mode 100644 libraries/RF24/examples/scanner/output/core.a delete mode 100644 libraries/RF24/examples/scanner/output/scanner.cpp delete mode 100644 libraries/RF24/examples/scanner/output/scanner.elf delete mode 100644 libraries/RF24/examples/scanner/output/scanner.hex diff --git a/libraries/RF24/examples/scanner/output/core.a b/libraries/RF24/examples/scanner/output/core.a deleted file mode 100644 index 6ae105d90023b9ce818bcb755ee9245f24c98185..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187630 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 NkD<2iO@I%8{}ZTDOPT-x diff --git a/libraries/RF24/examples/scanner/output/scanner.cpp b/libraries/RF24/examples/scanner/output/scanner.cpp deleted file mode 100644 index 5eb1627..0000000 --- a/libraries/RF24/examples/scanner/output/scanner.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#line 1 "scanner.pde" - -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Channel scanner - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// -// Channel info -// - -const short num_channels = 128; -short values[num_channels]; - -// -// Setup -// - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/scanner/\n\r"); - - // - // Setup and configure rf radio - // - - radio.begin(); - radio.setAutoAck(false); - - // Get into standby mode - radio.startListening(); - radio.stopListening(); - - // Print out header, high then low digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",i>>4); - ++i; - } - printf("\n\r"); - i = 0; - while ( i < num_channels ) - { - printf("%x",i&0xf); - ++i; - } - printf("\n\r"); -} - -// -// Loop -// - -const short num_reps = 100; - -void loop(void) -{ - // Clear measurement values - memset(values,0,num_channels); - - // Scan all channels num_reps times - int rep_counter = num_reps; - while (rep_counter--) - { - int i = num_channels; - while (i--) - { - // Select this channel - radio.setChannel(i); - - // Listen for a little - radio.startListening(); - delayMicroseconds(128); - radio.stopListening(); - - // Did we get a carrier? - if ( radio.testCarrier() ) - ++values[i]; - } - } - - // Print out channel measurements, clamped to a single hex digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",min(0xf,values[i]&0xf)); - ++i; - } - printf("\n\r"); -} - - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/scanner/output/scanner.elf b/libraries/RF24/examples/scanner/output/scanner.elf deleted file mode 100644 index 5e0f4b153bcd3684fc9008dac0bf38d4a5fbfd97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100436 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 ZC5_yqj3YXVcR0Mu?yd|2_9q0v{|C_~feiov diff --git a/libraries/RF24/examples/scanner/output/scanner.hex b/libraries/RF24/examples/scanner/output/scanner.hex deleted file mode 100644 index 45c37db..0000000 --- a/libraries/RF24/examples/scanner/output/scanner.hex +++ /dev/null @@ -1,325 +0,0 @@ -:100000000C9463000C948B000C948B000C948B006C -:100010000C948B000C948B000C948B000C948B0034 -:100020000C948B000C948B000C948B000C948B0024 -:100030000C948B000C948B000C948B000C948B0014 -:100040000C948F050C948B000C9423060C948B005D -:100050000C948B000C948B000C948B000C948B00F4 -:100060000C948B000C948B000000000024002700EF -:100070002A0000000000250028002B0000000000DE -:1000800023002600290004040404040404040202DA -:100090000202020203030303030301020408102007 -:1000A0004080010204081020010204081020000012 -:1000B0000007000201000003040600000000000029 -:1000C000000090043B0711241FBECFEFD8E0DEBF35 -:1000D000CDBF11E0A0E0B1E0E6EFF3E102C0059092 -:1000E0000D92AA33B107D9F712E0AAE3B1E001C03B -:1000F0001D92A13FB107E1F710E0C6ECD0E004C0CB -:100100002297FE010E94F509C23CD107C9F70E945F -:100110001C060C94F9090C9400000F931F93CF93C5 -:10012000DF938C01EB01009731F46115710519F42F -:1001300020E030E038C081E090E06EE070E00E94A6 -:10014000CB02FC019C01009771F180E8838320972A -:1001500071F0D387C28781E883838091E702909111 -:10016000E802892B21F4F093E802E093E7020115FD -:100170001105C9F011870087838182608383809194 -:10018000E9029091EA02892B71F4F093EA02E0937C -:10019000E9028091EB029091EC02892B21F4F0931B -:1001A000EC02E093EB02C901DF91CF911F910F9117 -:1001B0000895A0E0B0E0EFEDF0E00C94CC09FE0172 -:1001C0003596619171918091E9029091EA02AF01B7 -:1001D0000E94EE002096E2E00C94E809ABE0B0E06B -:1001E000E4EFF0E00C94BC093C012B015A01FC0146 -:1001F00017821682838181FD03C06FEF7FEFC6C136 -:100200009AE0892E1E010894211C311CF3012381E0 -:10021000F20123FD859123FF81912F01882309F4A9 -:10022000B2C1853239F423FD859123FF81912F01DD -:10023000853229F490E0B3010E940604E7CF982F9D -:10024000FF24EE249924FFE1FF15D0F09B3269F0E2 -:100250009C3228F4903259F0933291F40EC09D32C2 -:1002600049F0903369F441E024C052E0F52A84E07B -:10027000F82A28C098E0F92A25C0E0E1FE2A22C029 -:10028000F7FC29C0892F80538A3070F4F6FE05C030 -:10029000989C902C1124980E15C0E89CE02C1124F9 -:1002A000E80EF0E2FF2A0EC09E3229F4F6FC6BC184 -:1002B00040E4F42A07C09C3619F450E8F52A02C03D -:1002C000983649F4F20123FD959123FF91912F0176 -:1002D000992309F0B8CF892F8554833018F08052C4 -:1002E000833038F444E050E0A40EB51E5FE3598338 -:1002F0000FC0933631F0933779F0933509F056C03B -:1003000020C0F5018081898342E050E0A40EB51E33 -:10031000610101E010E012C0F501C080D180F6FC5F -:1003200003C06FEF7FEF02C0692D70E042E050E044 -:10033000A40EB51EC6010E94FB038C015FE7F522E7 -:1003400014C0F501C080D180F6FC03C06FEF7FEFD1 -:1003500002C0692D70E042E050E0A40EB51EC60157 -:100360000E94E9038C0150E8F52AF3FE07C01AC089 -:1003700080E290E0B3010E940604EA948E2D90E0A2 -:1003800008171907A8F30EC0F601F7FC8591F7FED0 -:1003900081916F0190E0B3010E940604E110EA949C -:1003A000015010400115110579F7EAC0943611F09B -:1003B000993669F5F7FE08C0F50120813181428147 -:1003C000538184E090E00AC0F501808191819C0115 -:1003D000442737FD4095542F82E090E0A80EB91EC7 -:1003E0009FE6F92257FF09C0509540953095219519 -:1003F0003F4F4F4F5F4FE0E8FE2ACA01B901A1010C -:100400002AE030E00E943204D82ED21840C095373E -:1004100029F41F2D1F7E2AE030E01DC01F2D197FFB -:100420009F3661F0903720F4983509F0ACC00FC0CA -:10043000903739F0983709F0A6C004C028E030E0C2 -:100440000AC0106114FD146020E130E004C014FD06 -:10045000166020E132E017FF08C0F501608171816C -:100460008281938144E050E008C0F5018081918150 -:10047000BC0180E090E042E050E0A40EB51EA10176 -:100480000E943204D82ED2188FE7F82EF122F6FE01 -:100490000BC05EEFF522D91438F4F4FE07C0F2FC6D -:1004A00005C08FEEF82202C01D2D01C0192DF4FEEB -:1004B0000DC0FE01ED0DF11D8081803319F499EE20 -:1004C000F92208C01F5FF2FE05C003C08F2D867899 -:1004D00009F01F5F0F2DF3FC14C0F0FE0FC01E15B6 -:1004E00010F09D2C0BC09D2C9E0C911A1E2D06C049 -:1004F00080E290E0B3010E9406041F5F1E15C0F366 -:1005000004C01E1510F4E11A01C0EE2404FF0FC050 -:1005100080E390E0B3010E94060402FF1DC001FDCC -:1005200003C088E790E00EC088E590E00BC0802F04 -:10053000867891F001FF02C08BE201C080E2F7FCF7 -:100540008DE290E0B3010E94060406C080E390E0D3 -:10055000B3010E9406049A94D914C0F3DA94F1010D -:10056000ED0DF11D808190E0B3010E940604DD20B5 -:10057000A9F706C080E290E0B3010E940604EA9465 -:10058000EE20C1F743CEF30166817781CB012B9634 -:10059000E2E10C94D8090F931F93CF93DF93689FE8 -:1005A0008001699F100D789F100D1124C8010E94D1 -:1005B000E702EC01009729F060E070E0A8010E94DA -:1005C000F403CE01DF91CF911F910F910895CF9346 -:1005D000DF93BC018230910510F462E070E0A091DD -:1005E000EF02B091F002ED01E0E0F0E040E050E019 -:1005F00021C0888199818617970769F48A819B8138 -:10060000309719F09383828304C09093F002809313 -:10061000EF02FE0134C06817790738F4411551051F -:1006200019F08417950708F4AC01FE018A819B81BB -:100630009C01E9012097E9F641155105A9F1CA018C -:10064000861B970B049708F4BA01E0E0F0E02AC09B -:100650008D919C91119784179507F9F4641775078C -:1006600081F412968D919C911397309719F0938392 -:10067000828304C09093F0028093EF02FD013296D2 -:100680004CC0CA01861B970BFD01E80FF91F61934F -:10069000719302978D939C9340C0FD018281938159 -:1006A0009C01D9011097A1F68091ED029091EE0284 -:1006B000892B41F480912301909124019093EE02C3 -:1006C0008093ED024091250150912601411551057D -:1006D00041F44DB75EB78091210190912201481BF2 -:1006E000590B2091ED023091EE02CA01821B930B4F -:1006F0008617970780F0AB014E5F5F4F8417950711 -:1007000050F0420F531F5093EE024093ED02F90157 -:100710006193719302C0E0E0F0E0CF01DF91CF91EF -:100720000895CF93DF93009709F450C0EC0122970E -:100730001B821A82A091EF02B091F002109709F18A -:1007400040E050E0AC17BD0708F1BB83AA83FE016F -:1007500021913191E20FF31FAE17BF0779F48D910C -:100760009C911197280F391F2E5F3F4F39832883A3 -:1007700012968D919C9113979B838A834115510505 -:1007800071F4D093F002C093EF0220C012968D91C5 -:100790009C911397AD01009711F0DC01D3CFFA01C2 -:1007A000D383C28321913191E20FF31FCE17DF076C -:1007B00069F488819981280F391F2E5F3F4FFA0114 -:1007C000318320838A819B8193838283DF91CF91C0 -:1007D0000895FC010590615070400110D8F7809594 -:1007E00090958E0F9F1F0895DC0101C06D934150BD -:1007F0005040E0F70895FC016150704001900110F5 -:10080000D8F7809590958E0F9F1F08950F931F9393 -:10081000CF93DF938C01EB018B8181FF1BC082FFA3 -:100820000DC02E813F818C819D812817390764F48A -:10083000E881F9810193F983E88306C0E885F985A9 -:10084000802F0995892B31F48E819F8101969F839A -:100850008E8302C00FEF1FEFC801DF91CF911F9170 -:100860000F910895FA01AA27283051F1203181F122 -:10087000E8946F936E7F6E5F7F4F8F4F9F4FAF4FA8 -:10088000B1E03ED0B4E03CD0670F781F891F9A1FBB -:10089000A11D680F791F8A1F911DA11D6A0F711D6F -:1008A000811D911DA11D20D009F468943F912AE07B -:1008B000269F11243019305D3193DEF6CF01089563 -:1008C000462F4770405D4193B3E00FD0C9F7F6CF94 -:1008D000462F4F70405D4A3318F0495D31FD40525C -:1008E000419302D0A9F7EACFB4E0A69597958795F2 -:1008F00077956795BA95C9F70097610571050895D1 -:100900009B01AC010A2E069457954795379527957C -:10091000BA95C9F7620F731F841F951FA01D089514 -:100920008AE391E068E049E00E9475070895FF922C -:100930000F931F93CF93DF9380E8E7E4F1E0DF01AB -:100940001D928A95E9F704E610E021C08AE391E060 -:100950006F2D0E94F2078AE391E00E94840880E8EC -:1009600090E00E94D7058AE391E00E947C078AE329 -:1009700091E00E944608882329F088819981019698 -:10098000998388832297FA94BFEFFB16F9F60150FA -:100990001040EFEF0F3F1E0729F0C5E4D2E08FE7CC -:1009A000F82ED4CFC7E4D1E000E011E000D000D0B1 -:1009B000ADB7BEB712961C930E931197899199917A -:1009C0008F70907014969C938E9313970E94D90009 -:1009D0000F900F900F900F90B2E0C734DB0731F704 -:1009E00000D083E091E0EDB7FEB7928381830E944F -:1009F000D9000F900F90DF91CF911F910F91FF9031 -:100A0000089580E895E060E070E00E948D00089510 -:100A10000F931F93CF93DF9384ED92E040E051EE6C -:100A200060E070E00E9454060E94010500D086E05C -:100A300091E0EDB7FEB7928381830E94D9000F90B9 -:100A40000F908AE391E00E944B088AE391E060E016 -:100A50000E94E8078AE391E00E9484088AE391E01B -:100A60000E947C07C0E0D0E000E011E000D000D0A0 -:100A7000EDB7FEB712830183CE0124E095958795EB -:100A80002A95E1F7948383830E94D90021960F90E1 -:100A90000F900F900F90C038D10541F700D083E040 -:100AA00091E0EDB7FEB7928381830E94D900C0E048 -:100AB000D0E00F900F9000D000D0EDB7FEB70183CB -:100AC0001283CE018F709070948383830E94D9002B -:100AD00021960F900F900F900F90C038D10559F7C5 -:100AE00000D083E091E0EDB7FEB7928381830E944E -:100AF000D9000F900F90DF91CF911F910F91089522 -:100B00000F931F93082F84ED92E0602F0E94280717 -:100B1000112707FD1095C8011F910F9108951F928D -:100B20000F920FB60F9211242F933F938F939F93A1 -:100B3000AF93BF9380914B0290914C02A0914D02D4 -:100B4000B0914E0230914F020196A11DB11D232F8D -:100B50002D5F2D3720F02D570196A11DB11D20933B -:100B60004F0280934B0290934C02A0934D02B0939E -:100B70004E028091470290914802A0914902B091A3 -:100B80004A020196A11DB11D80934702909348022D -:100B9000A0934902B0934A02BF91AF919F918F9168 -:100BA0003F912F910F900FBE0F901F9018950197B6 -:100BB00039F0880F991F880F991F02970197F1F755 -:100BC0000895789484B5826084BD84B5816084BDC5 -:100BD00085B5826085BD85B5816085BDEEE6F0E0B6 -:100BE000808181608083E1E8F0E010828081826012 -:100BF0008083808181608083E0E8F0E08081816093 -:100C00008083E1EBF0E0808184608083E0EBF0E0C2 -:100C1000808181608083EAE7F0E080818460808366 -:100C20008081826080838081816080838081806810 -:100C300080831092C10008950E94E1050E9408057A -:100C40000E949704FDCF1F920F920FB60F921124AE -:100C50002F933F934F938F939F93EF93FF934091E5 -:100C6000C600E091D002F091D10231969F012F771A -:100C7000307031978091D2029091D30228173907B2 -:100C800039F0E05BFD4F40833093D1022093D002D6 -:100C9000FF91EF919F918F914F913F912F910F90E5 -:100CA0000FBE0F901F901895AF92BF92DF92EF92F8 -:100CB000FF920F931F93CF93DF93EC017A018B0187 -:100CC000DD24403081EE580780E0680780E0780737 -:100CD00011F0DD24D39491E0A92EB12CE885F9859B -:100CE000DD2069F0C5010A8802C0880F991F0A94A7 -:100CF000E2F7808360E079E08DE390E005C0108248 -:100D000060E874E88EE190E0A80197010E949A09DA -:100D10002150304040405040569547953795279593 -:100D200080E12030380720F0DD2011F0DD24D6CF1F -:100D3000EC81FD813083EE81FF812083EA85FB8594 -:100D4000208141E050E0CA010E8402C0880F991F43 -:100D50000A94E2F7282B2083EA85FB852081CA01CB -:100D60000F8402C0880F991F0A94E2F7282B208372 -:100D7000EA85FB858081088802C0440F551F0A94CC -:100D8000E2F7842B8083DF91CF911F910F91FF9029 -:100D9000EF90DF90BF90AF900895DC011296ED9137 -:100DA000FC911397E058FF4F2191319180819181FF -:100DB000281B390B2F773070C9010895DC0112967A -:100DC000ED91FC911397EE57FF4F20813181929165 -:100DD0008291E058F0408217930719F42FEF3FEF0C -:100DE00005C0E20FF31F8081282F30E0C90108956C -:100DF000DC011296ED91FC911397DF01AE57BF4FC6 -:100E00002D913C911197E058FF4F80819181E058DE -:100E1000F0408217930719F42FEF3FEF0BC0E20F5A -:100E2000F31F80812F5F3F4F2F7730702D933C93BE -:100E3000282F30E0C9010895DC011296ED91FC9154 -:100E40001397EE57FF4F808191819293829308957B -:100E5000FC01A085B18521898C9190E0022E02C011 -:100E6000959587950A94E2F780FFF6CF0484F5857F -:100E7000E02D608308958BE291E09093D5028093FA -:100E8000D40280E592E09093D7028093D60285EC5D -:100E900090E09093D9028093D80284EC90E09093F4 -:100EA000DB028093DA0280EC90E09093DD02809385 -:100EB000DC0281EC90E09093DF028093DE0286EC0E -:100EC00090E09093E1028093E00284E08093E2025C -:100ED00083E08093E30287E08093E40285E08093DF -:100EE000E50281E08093E6020895FC01608341837E -:100EF00080E2828313820895FC01808160E00E9479 -:100F0000CD080895FF920F931F938C01F62E80E079 -:100F10000E94650985E00E946A09F80181816F2DB0 -:100F20000E94CD081F910F91FF9008951F93CF93BA -:100F3000DF93EC0160E070E00E94820781EE8EBDDD -:100F40000DB407FEFDCF1EB5CE0161E070E00E943A -:100F50008207812FDF91CF911F9108951F93CF9327 -:100F6000DF93EC0160E070E00E94820782EE8EBDAC -:100F70000DB407FEFDCF1EB5CE0161E070E00E940A -:100F80008207812FDF91CF911F9108950F931F93B7 -:100F9000CF93DF93EC01162F042F60E070E00E94E6 -:100FA00082071F7110621EBD0DB407FEFDCF1EB576 -:100FB0000EBD0DB407FEFDCF8EB5CE0161E070E031 -:100FC0000E948207812FDF91CF911F910F91089589 -:100FD000662319F061E04FE302C061E040E00E9447 -:100FE000C6070895462F603808F04FE765E00E9475 -:100FF000C6070895EF92FF920F931F93CF93DF934D -:101000007C01162FEA01022F60E070E00E94820747 -:101010001F7110621EBD0DB407FEFDCF1EB508C0C6 -:1010200088818EBD0DB407FEFDCF21968EB501508F -:101030000023B1F7C70161E070E00E948207812FB1 -:10104000DF91CF911F910F91FF90EF9008951F9323 -:10105000CF93DF93EC01162F60E070E00E948207CF -:101060001F711EBD0DB407FEFDCF8EB58FEF8EBD77 -:101070000DB407FEFDCF1EB5CE0161E070E00E9409 -:101080008207812FDF91CF911F91089569E00E941F -:101090002708817008950F931F938C01FC018081B4 -:1010A00061E00E94A708F801818161E00E94A70821 -:1010B000F801808160E00E94CD08C80161E070E025 -:1010C0000E9482070E947B0981E00E945C0980E007 -:1010D0000E94650985E00E946A09C80164E04FEF3B -:1010E0000E94C607C80167E040E70E94C607C80122 -:1010F0000E94AE07C8010E949607C80161E00E94E5 -:10110000F2071F910F9108950F931F938C0160E0D8 -:101110004BE00E94C607C80167E040E70E94C6078F -:10112000A8014B5F5F4FC8016AE025E00E94FA0703 -:10113000C8010E94AE07F801808161E00E94CD08DD -:1011400082E890E00E94D7051F910F910895482FE3 -:1011500050E0CA0186569F4FFC0124914A575F4FC9 -:10116000FA0184918823C1F0E82FF0E0EE0FFF1F11 -:10117000E859FF4FA591B491662341F49FB7F894C5 -:101180008C91209582238C939FBF08959FB7F894EC -:101190008C91822B8C939FBF0895482F50E0CA01F9 -:1011A00082559F4FFC012491CA0186569F4FFC0136 -:1011B00034914A575F4FFA019491992309F444C03E -:1011C000222351F1233071F0243028F42130A1F092 -:1011D000223011F514C02630B1F02730C1F0243090 -:1011E000D9F404C0809180008F7703C08091800083 -:1011F0008F7D8093800010C084B58F7702C084B546 -:101200008F7D84BD09C08091B0008F7703C080912D -:10121000B0008F7D8093B000E92FF0E0EE0FFF1F4C -:10122000EE58FF4FA591B491662341F49FB7F8940F -:101230008C91309583238C939FBF08959FB7F8942A -:101240008C91832B8C939FBF08950F931F93CF9303 -:10125000DF938C01EB0109C02196D801ED91FC913F -:101260000190F081E02DC801099568816623A1F7FE -:10127000DF91CF911F910F910895EF92FF920F93FD -:101280001F93CF93DF938C017B01EA010CC0D70140 -:101290006D917D01D801ED91FC910190F081E02DDF -:1012A000C80109952197209791F7DF91CF911F9160 -:1012B0000F91FF90EF900895882319F48CB5806208 -:1012C00002C08CB58F7D8CBD08959CB5937F982B03 -:1012D0009CBD08952CB5382F33702C7F322B3CBD2C -:1012E0002DB590E0959587959595879581702E7F82 -:1012F000822B8DBD08958DE061E00E94A7088BE0F0 -:1013000061E00E94A7088AE061E00E94A7088DE0E2 -:1013100060E00E94CD088BE060E00E94CD088AE08A -:1013200061E00E94CD088CB580618CBD8CB5806475 -:101330008CBD0895A1E21A2EAA1BBB1BFD010DC096 -:10134000AA1FBB1FEE1FFF1FA217B307E407F50775 -:1013500020F0A21BB30BE40BF50B661F771F881F51 -:10136000991F1A9469F760957095809590959B01E7 -:10137000AC01BD01CF0108952F923F924F925F9231 -:101380006F927F928F929F92AF92BF92CF92DF9295 -:10139000EF92FF920F931F93CF93DF93CDB7DEB7FA -:1013A000CA1BDB0B0FB6F894DEBF0FBECDBF09948E -:1013B0002A88398848885F846E847D848C849B84E5 -:1013C000AA84B984C884DF80EE80FD800C811B81F3 -:1013D000AA81B981CE0FD11D0FB6F894DEBF0FBE22 -:1013E000CDBFED010895EE0FFF1F0590F491E02DA4 -:0613F0000994F894FFCF00 -:1013F6002578000A0D000A0D524632342F657861B1 -:101406006D706C65732F7363616E6E65722F0A0D56 -:10141600002000F102000000000000280725093D19 -:0A14260009CD06F806DE061C0700DB -:00000001FF From 1897409bded76b66250b43c8e582e4a630a9176a Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 1 May 2014 00:12:42 +0200 Subject: [PATCH 233/326] git tree messed up --- RF24/.gitignore | 14 - RF24/Doxyfile | 1551 ----------------- RF24/FAQ | 53 - RF24/README.md | 41 - RF24/RF24.cpp | 1220 ------------- RF24/RF24.h | 1124 ------------ RF24/RF24_config.h | 129 -- RF24/RPi/RF24/Makefile | 55 - RF24/RPi/RF24/RF24.cpp | 1145 ------------ RF24/RPi/RF24/RF24.h | 1041 ----------- RF24/RPi/RF24/RF24_config.h | 34 - RF24/RPi/RF24/bcm2835.c | 1399 --------------- RF24/RPi/RF24/bcm2835.h | 1353 -------------- RF24/RPi/RF24/examples/Makefile | 40 - RF24/RPi/RF24/examples/extra/Makefile | 40 - RF24/RPi/RF24/examples/extra/rpi-hub.cpp | 134 -- RF24/RPi/RF24/examples/extra/scanner.cpp | 147 -- RF24/RPi/RF24/examples/gettingstarted.cpp | 187 -- .../examples/gettingstarted_call_response.cpp | 137 -- RF24/RPi/RF24/examples/pingpair_dyn.cpp | 181 -- RF24/RPi/RF24/examples/transfer.cpp | 148 -- RF24/RPi/RF24/nRF24L01.h | 127 -- RF24/RPi/RF24/readme.md | 117 -- RF24/RPi/readme.md | 117 -- RF24/doxygen-custom.css | 835 --------- .../GettingStarted/GettingStarted.ino | 136 -- RF24/examples/GettingStarted/printf.h | 37 - .../GettingStarted_CallResponse.ino | 124 -- .../GettingStarted_CallResponse/printf.h | 37 - RF24/examples/Transfer/Transfer.ino | 144 -- RF24/examples/Transfer/printf.h | 37 - .../TransferTimeouts/TransferTimeouts.ino | 189 -- RF24/examples/TransferTimeouts/printf.h | 37 - RF24/examples/Usage/led_remote/Jamfile | 206 --- RF24/examples/Usage/led_remote/led_remote.pde | 255 --- RF24/examples/Usage/led_remote/printf.h | 37 - RF24/examples/Usage/nordic_fob/Jamfile | 219 --- RF24/examples/Usage/nordic_fob/nordic_fob.pde | 142 -- RF24/examples/Usage/nordic_fob/printf.h | 37 - RF24/examples/Usage/pingpair_maple/Jamfile | 182 -- RF24/examples/Usage/pingpair_maple/main.cpp | 87 - .../Usage/pingpair_maple/pingpair_maple.pde | 242 --- RF24/examples/pingpair_ack/pingpair_ack.ino | 123 -- RF24/examples/pingpair_ack/printf.h | 37 - RF24/examples/pingpair_dyn/Jamfile | 206 --- RF24/examples/pingpair_dyn/pingpair_dyn.ino | 232 --- RF24/examples/pingpair_dyn/printf.h | 37 - RF24/examples/pingpair_irq/pingpair_irq.ino | 137 -- RF24/examples/pingpair_irq/printf.h | 37 - .../pingpair_sleepy/pingpair_sleepy.ino | 225 --- RF24/examples/pingpair_sleepy/printf.h | 37 - RF24/examples/scanner/Jamfile | 210 --- RF24/examples/scanner/output/core.a | Bin 187630 -> 0 bytes RF24/examples/scanner/output/scanner.cpp | 128 -- RF24/examples/scanner/output/scanner.elf | Bin 100436 -> 0 bytes RF24/examples/scanner/output/scanner.hex | 325 ---- RF24/examples/scanner/printf.h | 31 - RF24/examples/scanner/scanner.ino | 126 -- RF24/examples/starping/Jamfile | 206 --- RF24/examples/starping/printf.h | 37 - RF24/examples/starping/starping.pde | 293 ---- RF24/keywords.txt | 16 - RF24/nRF24L01.h | 127 -- RF24/tests/README | 7 - RF24/tests/native/Jamfile | 300 ---- RF24/tests/native/pingpair_irq.pde | 223 --- RF24/tests/native/printf.h | 33 - RF24/tests/pingpair_blocking/Jamfile | 219 --- .../pingpair_blocking/pingpair_blocking.pde | 273 --- RF24/tests/pingpair_blocking/printf.h | 37 - RF24/tests/pingpair_blocking/runtest.py | 25 - RF24/tests/pingpair_blocking/runtests.sh | 5 - RF24/tests/pingpair_blocking/test.ex | 11 - RF24/tests/pingpair_test/Jamfile | 219 --- RF24/tests/pingpair_test/pingpair_test.pde | 435 ----- RF24/tests/pingpair_test/printf.h | 37 - RF24/tests/pingpair_test/runtest.py | 25 - RF24/tests/pingpair_test/runtests.sh | 21 - RF24/tests/pingpair_test/test.ex | 11 - RF24/wikidoc.xslt | 41 - 80 files changed, 18009 deletions(-) delete mode 100644 RF24/.gitignore delete mode 100644 RF24/Doxyfile delete mode 100644 RF24/FAQ delete mode 100644 RF24/README.md delete mode 100644 RF24/RF24.cpp delete mode 100644 RF24/RF24.h delete mode 100644 RF24/RF24_config.h delete mode 100644 RF24/RPi/RF24/Makefile delete mode 100644 RF24/RPi/RF24/RF24.cpp delete mode 100644 RF24/RPi/RF24/RF24.h delete mode 100644 RF24/RPi/RF24/RF24_config.h delete mode 100644 RF24/RPi/RF24/bcm2835.c delete mode 100644 RF24/RPi/RF24/bcm2835.h delete mode 100644 RF24/RPi/RF24/examples/Makefile delete mode 100644 RF24/RPi/RF24/examples/extra/Makefile delete mode 100644 RF24/RPi/RF24/examples/extra/rpi-hub.cpp delete mode 100644 RF24/RPi/RF24/examples/extra/scanner.cpp delete mode 100644 RF24/RPi/RF24/examples/gettingstarted.cpp delete mode 100644 RF24/RPi/RF24/examples/gettingstarted_call_response.cpp delete mode 100644 RF24/RPi/RF24/examples/pingpair_dyn.cpp delete mode 100644 RF24/RPi/RF24/examples/transfer.cpp delete mode 100644 RF24/RPi/RF24/nRF24L01.h delete mode 100644 RF24/RPi/RF24/readme.md delete mode 100644 RF24/RPi/readme.md delete mode 100644 RF24/doxygen-custom.css delete mode 100644 RF24/examples/GettingStarted/GettingStarted.ino delete mode 100644 RF24/examples/GettingStarted/printf.h delete mode 100644 RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino delete mode 100644 RF24/examples/GettingStarted_CallResponse/printf.h delete mode 100644 RF24/examples/Transfer/Transfer.ino delete mode 100644 RF24/examples/Transfer/printf.h delete mode 100644 RF24/examples/TransferTimeouts/TransferTimeouts.ino delete mode 100644 RF24/examples/TransferTimeouts/printf.h delete mode 100644 RF24/examples/Usage/led_remote/Jamfile delete mode 100644 RF24/examples/Usage/led_remote/led_remote.pde delete mode 100644 RF24/examples/Usage/led_remote/printf.h delete mode 100644 RF24/examples/Usage/nordic_fob/Jamfile delete mode 100644 RF24/examples/Usage/nordic_fob/nordic_fob.pde delete mode 100644 RF24/examples/Usage/nordic_fob/printf.h delete mode 100644 RF24/examples/Usage/pingpair_maple/Jamfile delete mode 100644 RF24/examples/Usage/pingpair_maple/main.cpp delete mode 100644 RF24/examples/Usage/pingpair_maple/pingpair_maple.pde delete mode 100644 RF24/examples/pingpair_ack/pingpair_ack.ino delete mode 100644 RF24/examples/pingpair_ack/printf.h delete mode 100644 RF24/examples/pingpair_dyn/Jamfile delete mode 100644 RF24/examples/pingpair_dyn/pingpair_dyn.ino delete mode 100644 RF24/examples/pingpair_dyn/printf.h delete mode 100644 RF24/examples/pingpair_irq/pingpair_irq.ino delete mode 100644 RF24/examples/pingpair_irq/printf.h delete mode 100644 RF24/examples/pingpair_sleepy/pingpair_sleepy.ino delete mode 100644 RF24/examples/pingpair_sleepy/printf.h delete mode 100644 RF24/examples/scanner/Jamfile delete mode 100644 RF24/examples/scanner/output/core.a delete mode 100644 RF24/examples/scanner/output/scanner.cpp delete mode 100755 RF24/examples/scanner/output/scanner.elf delete mode 100644 RF24/examples/scanner/output/scanner.hex delete mode 100644 RF24/examples/scanner/printf.h delete mode 100644 RF24/examples/scanner/scanner.ino delete mode 100644 RF24/examples/starping/Jamfile delete mode 100644 RF24/examples/starping/printf.h delete mode 100644 RF24/examples/starping/starping.pde delete mode 100644 RF24/keywords.txt delete mode 100644 RF24/nRF24L01.h delete mode 100644 RF24/tests/README delete mode 100644 RF24/tests/native/Jamfile delete mode 100644 RF24/tests/native/pingpair_irq.pde delete mode 100644 RF24/tests/native/printf.h delete mode 100644 RF24/tests/pingpair_blocking/Jamfile delete mode 100644 RF24/tests/pingpair_blocking/pingpair_blocking.pde delete mode 100644 RF24/tests/pingpair_blocking/printf.h delete mode 100755 RF24/tests/pingpair_blocking/runtest.py delete mode 100755 RF24/tests/pingpair_blocking/runtests.sh delete mode 100755 RF24/tests/pingpair_blocking/test.ex delete mode 100644 RF24/tests/pingpair_test/Jamfile delete mode 100644 RF24/tests/pingpair_test/pingpair_test.pde delete mode 100644 RF24/tests/pingpair_test/printf.h delete mode 100755 RF24/tests/pingpair_test/runtest.py delete mode 100755 RF24/tests/pingpair_test/runtests.sh delete mode 100755 RF24/tests/pingpair_test/test.ex delete mode 100644 RF24/wikidoc.xslt diff --git a/RF24/.gitignore b/RF24/.gitignore deleted file mode 100644 index ac32040..0000000 --- a/RF24/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -*.bak -*.o -.*.swp -*.orig -.swp -docs/ -output/ -ojam/ -out/ -16000000/ -8000000/ -out_native/ -version.h -Session.vim diff --git a/RF24/Doxyfile b/RF24/Doxyfile deleted file mode 100644 index 4604afc..0000000 --- a/RF24/Doxyfile +++ /dev/null @@ -1,1551 +0,0 @@ -# Doxyfile 1.6.3 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = RF24 - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = v1 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = docs - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then 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. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = YES - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -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. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. 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 that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip 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. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -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 only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -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 (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -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 (the default) these declarations will be included in the -# documentation. - -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 (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = 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. - -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. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -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. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED 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. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be 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. - -INPUT = . - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. 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. - -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 pattern (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 *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.h FAQ - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = examples - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = YES - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -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. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = NO - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = doxygen-custom.css - -# 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. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = 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. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# 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, 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. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, 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). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, 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. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -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 -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -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. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# 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. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = 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. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# 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 very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = 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. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -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. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# 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 them the man command -# would be unable to find the correct page. The default is NO. - -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 captures the structure of -# the code including all documentation. - -GENERATE_XML = YES - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# 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. - -XML_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 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. - -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 file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# 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. - -PERLMOD_LATEX = NO - -# 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 size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -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 (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all 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. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that 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. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -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 listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# 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. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need 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 setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# 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. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. 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. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. 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. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# 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 by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# 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 root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES 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. - -DOT_MULTI_TARGETS = YES - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/RF24/FAQ b/RF24/FAQ deleted file mode 100644 index eccd03a..0000000 --- a/RF24/FAQ +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @page FAQ Frequently Asked Questions - * - * @ref starting - * - * @ref hardware - * - * @ref range - * - * @ref issues - * - * @ref ram - * - * @ref tests - * - * @section starting Where do I start? - * - * See my blog post: - * Getting Started with nRF24L01+ on Arduino - * - * @section hardware Where can I buy some hardware? - * - * @li iTeadStudio sells the basic 2.4G Wireless nRF24L01+ Module for $4. Such a deal! - * @li MDfly.com sells the same unit, 2.4Ghz Wireless nRF24L01+ Transceiver Module for $6.95, but it ships from the US so it gets there a lot faster. Great place to get a few units and get started quickly. - * @li MDfly.com also has the nRF24L01 2.4GHz Transceiver Module w/ Power Amplifier for $13.95, which increases range dramatically and uses a chip antenna - * @li MDfly.com also has the 2.4GHz Transceiver Module w/ Power Amplifier with an external antenna for $19.95 - * - * @section range What is the range of these units? - * - * Here are some results from measurements I have taken, using the basic $4 iTeadStudio units. - * I recommend that everyone take their own measurements in their particular circumstances. - * - * @li non-plus unit, 2MBps (worst case), 41+ ft line of sight indoors, immediate dropoff with any deviation from LOS. (41 ft is as far as I can go in my house without turning a corner) - * @li Plus unit, 250kbps (best case), 46 ft around two corners indoors, 49 ft around one corner. More importantly, at 250k, packet loss is almost negligible through almost all of that range. - * @li Both units at 1MBps, plus unit gets about 10% range improvement over non-plus in almost all situations. - * - * @section issues What should I do if I find a problem? - * - * Please open an issue on github if you find any problems using it with any version of Arduino or Maple. - * - * @section ram What is the RAM footprint of this library? - * - * 16 bytes. A single radio object consumes 16 bytes of RAM, and the library - * does not use any other RAM statically. - * - * @section tests Why are the examples in the 'tests' directory failing? - * - * The sketches in the 'tests' directory are not for general use. - * Please use the examples in the 'examples' directory instead. - * - * The 'tests' directory is only for people making changes to the library - * to ensure that their changes do not break anything. - */ diff --git a/RF24/README.md b/RF24/README.md deleted file mode 100644 index d0f2c0e..0000000 --- a/RF24/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Optimized High Speed Arduino driver for nRF24L01 2.4GHz Wireless Transceiver - -Design Goals: This library is designed to be... - -* More compliant with the manufacturer specified operation of the chip, while allowing advanced users -* to work outside the reccommended operation. -* Utilize the capabilities of the radio to their full potential via Arduino -* More reliable and feature rich -* Easy for beginners to use, with well documented examples and features -* Consumed with a public interface that's similiar to other Arduino standard libraries -* Built against the standard SPI library. - -April 2014: Official Release: Still some work to do, but most benefits have been realized - -* The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds -* Changes to read() and available () functionality have increased reliability and response -* Extended timeout periods have been added to aid in noisy or otherwise unreliable environments -* Delays have been removed where possible to ensure maximum efficiency -* Full Due support with extended SPI functions -* ATTiny 24/44/84 25/45/85 now supported. -* More! See the links below and class documentation for more info. - -Please refer to: - -* [Documentation Main Page](http://tmrh20.github.io/) -* [Class Reference]( http://tmrh20.github.io/RF24/classRF24.html) -* [Source Code](https://github.com/tmrh20/RF24) -* [Downloads](https://github.com/TMRh20/RF24/archive/master.zip) -* [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) - -This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or -the SPI hardware will go into 'slave' mode. - -Supported Boards: - -* Uno, Nano, etc (328 based boards) -* Mega Types (2560, 1280, etc) -* ARM (Arduino Due) via extended SPI methods -* ATTiny 24/44/84 25/45/85 -* Raspberry Pi -* See the [documentation](http://tmrh20.github.io/) for more info \ No newline at end of file diff --git a/RF24/RF24.cpp b/RF24/RF24.cpp deleted file mode 100644 index ebfbf81..0000000 --- a/RF24/RF24.cpp +++ /dev/null @@ -1,1220 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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 "nRF24L01.h" -#include "RF24_config.h" -#include "RF24.h" - -/****************************************************************************/ - -void RF24::csn(bool mode) -{ - // Minimum ideal SPI bus speed is 2x data rate - // If we assume 2Mbs data rate and 16Mhz clock, a - // divider of 4 is the minimum we want. - // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz -#ifdef ARDUINO - #if !defined( __AVR_ATtiny85__ ) && !defined( __AVR_ATtiny84__) && !defined (__arm__) - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV2); - #endif -#endif - -#ifndef __arm__ - digitalWrite(csn_pin,mode); -#endif - -} - -/****************************************************************************/ - -void RF24::ce(bool level) -{ - digitalWrite(ce_pin,level); -} - -/****************************************************************************/ - -uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) -{ - uint8_t status; - -#if defined (__arm__) - status = SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); - while ( len-- > 1 ){ - *buf++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); - } - *buf++ = SPI.transfer(csn_pin,0xff); - -#else - csn(LOW); - status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); - while ( len-- ){ - *buf++ = SPI.transfer(0xff); - } - csn(HIGH); - -#endif - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::read_register(uint8_t reg) -{ - - #if defined (__arm__) - SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ) , SPI_CONTINUE); - uint8_t result = SPI.transfer(csn_pin,0xff); - #else - csn(LOW); - SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); - uint8_t result = SPI.transfer(0xff); - - csn(HIGH); - #endif - - return result; -} - -/****************************************************************************/ - -uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) -{ - uint8_t status; - - #if defined (__arm__) - status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); - while ( len-- > 1){ - SPI.transfer(csn_pin,*buf++, SPI_CONTINUE); - } - SPI.transfer(csn_pin,*buf++); - #else - - csn(LOW); - status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); - while ( len-- ) - SPI.transfer(*buf++); - - csn(HIGH); - - #endif - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::write_register(uint8_t reg, uint8_t value) -{ - uint8_t status; - - IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); - - #if defined (__arm__) - status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE); - SPI.transfer(csn_pin,value); - #else - - csn(LOW); - status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); - SPI.transfer(value); - csn(HIGH); - - #endif - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::write_payload(const void* buf, uint8_t data_len, const uint8_t writeType) -{ - uint8_t status; - const uint8_t* current = reinterpret_cast(buf); - - if(data_len > payload_size) data_len = payload_size; - uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - - //printf("[Writing %u bytes %u blanks]",data_len,blank_len); - - #if defined (__arm__) - - status = SPI.transfer(csn_pin, writeType , SPI_CONTINUE); - - if(blank_len){ - while ( data_len--){ - SPI.transfer(csn_pin,*current++, SPI_CONTINUE); - } - while ( --blank_len ){ - SPI.transfer(csn_pin,0, SPI_CONTINUE); - } - SPI.transfer(csn_pin,0); - }else{ - while( --data_len ){ - SPI.transfer(csn_pin,*current++, SPI_CONTINUE); - } - SPI.transfer(csn_pin,*current); - } - - #else - - csn(LOW); - status = SPI.transfer( writeType ); - while ( data_len-- ) { - SPI.transfer(*current++); - } - while ( blank_len-- ) { - SPI.transfer(0); - } - csn(HIGH); - - #endif - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::read_payload(void* buf, uint8_t data_len) -{ - uint8_t status; - uint8_t* current = reinterpret_cast(buf); - - if(data_len > payload_size) data_len = payload_size; - uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - - //printf("[Reading %u bytes %u blanks]",data_len,blank_len); - - - #if defined (__arm__) - - status = SPI.transfer(csn_pin, R_RX_PAYLOAD, SPI_CONTINUE ); - - if( blank_len ){ - - while ( data_len-- ){ - *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); - } - while ( --blank_len ){ - SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); - } - SPI.transfer(csn_pin,0xFF); - }else{ - while ( --data_len ){ - *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); - } - *current = SPI.transfer(csn_pin,0xFF); - } - - #else - - csn(LOW); - status = SPI.transfer( R_RX_PAYLOAD ); - while ( data_len-- ) { - *current++ = SPI.transfer(0xFF); - } - while ( blank_len-- ) { - SPI.transfer(0xff); - } - csn(HIGH); - - #endif - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::flush_rx(void) -{ - return spiTrans( FLUSH_RX ); -} - -/****************************************************************************/ - -uint8_t RF24::flush_tx(void) -{ - return spiTrans( FLUSH_TX ); -} - -/****************************************************************************/ - -uint8_t RF24::spiTrans(uint8_t cmd){ - - uint8_t status; - #if defined (__arm__) - status = SPI.transfer(csn_pin, cmd ); - #else - - csn(LOW); - status = SPI.transfer( cmd ); - csn(HIGH); - #endif - return status; - -} - -/****************************************************************************/ - -uint8_t RF24::get_status(void) -{ - return spiTrans(NOP); -} - -/****************************************************************************/ - -void RF24::print_status(uint8_t status) -{ - printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), - status, - (status & _BV(RX_DR))?1:0, - (status & _BV(TX_DS))?1:0, - (status & _BV(MAX_RT))?1:0, - ((status >> RX_P_NO) & B111), - (status & _BV(TX_FULL))?1:0 - ); -} - -/****************************************************************************/ - -void RF24::print_observe_tx(uint8_t value) -{ - printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), - value, - (value >> PLOS_CNT) & B1111, - (value >> ARC_CNT) & B1111 - ); -} - -/****************************************************************************/ - -void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) -{ - char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); - while (qty--) - printf_P(PSTR(" 0x%02x"),read_register(reg++)); - printf_P(PSTR("\r\n")); -} - -/****************************************************************************/ - -void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) -{ - char extra_tab = strlen_P(name) < 8 ? '\t' : 0; - printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); - - while (qty--) - { - uint8_t buffer[5]; - read_register(reg++,buffer,sizeof buffer); - - printf_P(PSTR(" 0x")); - uint8_t* bufptr = buffer + sizeof buffer; - while( --bufptr >= buffer ) - printf_P(PSTR("%02x"),*bufptr); - } - - printf_P(PSTR("\r\n")); -} - -/****************************************************************************/ - -RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false), - payload_size(32), dynamic_payloads_enabled(false), - pipe0_reading_address(0) -{ -} - -/****************************************************************************/ - -void RF24::setChannel(uint8_t channel) -{ - // TODO: This method could take advantage of the 'wide_band' calculation - // done in setChannel() to require certain channel spacing. - - const uint8_t max_channel = 127; - write_register(RF_CH,min(channel,max_channel)); -} - -/****************************************************************************/ - -void RF24::setPayloadSize(uint8_t size) -{ - payload_size = min(size,32); -} - -/****************************************************************************/ - -uint8_t RF24::getPayloadSize(void) -{ - return payload_size; -} - -/****************************************************************************/ - -#if !defined (MINIMAL) - -static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; -static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; -static const char rf24_datarate_e_str_2[] PROGMEM = "250KBPS"; -static const char * const rf24_datarate_e_str_P[] PROGMEM = { - rf24_datarate_e_str_0, - rf24_datarate_e_str_1, - rf24_datarate_e_str_2, -}; -static const char rf24_model_e_str_0[] PROGMEM = "nRF24L01"; -static const char rf24_model_e_str_1[] PROGMEM = "nRF24L01+"; -static const char * const rf24_model_e_str_P[] PROGMEM = { - rf24_model_e_str_0, - rf24_model_e_str_1, -}; -static const char rf24_crclength_e_str_0[] PROGMEM = "Disabled"; -static const char rf24_crclength_e_str_1[] PROGMEM = "8 bits"; -static const char rf24_crclength_e_str_2[] PROGMEM = "16 bits" ; -static const char * const rf24_crclength_e_str_P[] PROGMEM = { - rf24_crclength_e_str_0, - rf24_crclength_e_str_1, - rf24_crclength_e_str_2, -}; -static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; -static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; -static const char rf24_pa_dbm_e_str_2[] PROGMEM = "PA_HIGH"; -static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_MAX"; -static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { - rf24_pa_dbm_e_str_0, - rf24_pa_dbm_e_str_1, - rf24_pa_dbm_e_str_2, - rf24_pa_dbm_e_str_3, -}; - -void RF24::printDetails(void) -{ - print_status(get_status()); - - print_address_register(PSTR("RX_ADDR_P0-1"),RX_ADDR_P0,2); - print_byte_register(PSTR("RX_ADDR_P2-5"),RX_ADDR_P2,4); - print_address_register(PSTR("TX_ADDR"),TX_ADDR); - - print_byte_register(PSTR("RX_PW_P0-6"),RX_PW_P0,6); - print_byte_register(PSTR("EN_AA"),EN_AA); - print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); - print_byte_register(PSTR("RF_CH"),RF_CH); - print_byte_register(PSTR("RF_SETUP"),RF_SETUP); - print_byte_register(PSTR("CONFIG"),CONFIG); - print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); - -#if defined(__arm__) - printf_P(PSTR("Data Rate\t = %s\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); - printf_P(PSTR("Model\t\t = %s\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); - printf_P(PSTR("CRC Length\t = %s\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); - printf_P(PSTR("PA Power\t = %s\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); -#else - printf_P(PSTR("Data Rate\t = %S\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); - printf_P(PSTR("Model\t\t = %S\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); - printf_P(PSTR("CRC Length\t = %S\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); - printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); -#endif -} - -#endif -/****************************************************************************/ - -void RF24::begin(void) -{ - // Initialize pins - pinMode(ce_pin,OUTPUT); - - #if defined(__arm__) - SPI.begin(csn_pin); // Using the extended SPI features of the DUE - SPI.setClockDivider(csn_pin, 9); // Set the bus speed to 8.4mhz on Due - SPI.setBitOrder(csn_pin,MSBFIRST); // Set the bit order and mode specific to this device - SPI.setDataMode(csn_pin,SPI_MODE0); - ce(LOW); - //csn(HIGH); - #else - pinMode(csn_pin,OUTPUT); - SPI.begin(); - ce(LOW); - csn(HIGH); - #endif - - // Must allow the radio time to settle else configuration bits will not necessarily stick. - // This is actually only required following power up but some settling time also appears to - // be required after resets too. For full coverage, we'll always assume the worst. - // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped. - // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure. - // WARNING: Delay is based on P-variant whereby non-P *may* require different timing. - delay( 5 ) ; - - // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier - // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet - // sizes must never be used. See documentation for a more complete explanation. - setRetries(5,15); - - // Reset value is MAX - //setPALevel( RF24_PA_MAX ) ; - - // Determine if this is a p or non-p RF24 module and then - // reset our data rate back to default value. This works - // because a non-P variant won't allow the data rate to - // be set to 250Kbps. - if( setDataRate( RF24_250KBPS ) ) - { - p_variant = true ; - } - - // Then set the data rate to the slowest (and most reliable) speed supported by all - // hardware. - setDataRate( RF24_1MBPS ) ; - - // Initialize CRC and request 2-byte (16bit) CRC - setCRCLength( RF24_CRC_16 ) ; - - // Disable dynamic payloads, to match dynamic_payloads_enabled setting - Reset value is 0 - //write_register(DYNPD,0); - - // Reset current status - // Notice reset and flush is the last thing we do - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Set up default configuration. Callers can always change it later. - // This channel should be universally safe and not bleed over into adjacent - // spectrum. - setChannel(76); - - // Flush buffers - flush_rx(); - flush_tx(); - - powerUp(); //Power up by default when begin() is called - - // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) - // PTX should use only 22uA of power - write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); - - -} - -/****************************************************************************/ - -void RF24::startListening(void) -{ - powerUp(); - write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); - write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Restore the pipe0 adddress, if exists - if (pipe0_reading_address){ - write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); - } - - // Flush buffers - flush_rx(); - flush_tx(); - - // Go! - ce(HIGH); - -} - -/****************************************************************************/ - -void RF24::stopListening(void) -{ - ce(LOW); - flush_tx(); - flush_rx(); - write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); - -} - -/****************************************************************************/ - -void RF24::powerDown(void) -{ - ce(LOW); // Guarantee CE is low on powerDown - write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); -} - -/****************************************************************************/ - -//Power up now. Radio will not power down unless instructed by MCU for config changes etc. -void RF24::powerUp(void) -{ - uint8_t cfg = read_register(CONFIG); - - // if not powered up then power up and wait for the radio to initialize - if (!(cfg & _BV(PWR_UP))){ - write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); - - // For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode. - // There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode before - // the CEis set high. - Tpd2stby can be up to 5ms per the 1.0 datasheet - delay(5); - - } -} - -/******************************************************************/ - -//Similar to the previous write, clears the interrupt flags -bool RF24::write( const void* buf, uint8_t len, const bool multicast ) -{ - //Start Writing - startWrite(buf,len,multicast); - - //Wait until complete or failed - //ACK payloads that are handled improperly will cause this to hang - //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload - while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } - - uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - //Max retries exceeded - if( status & _BV(MAX_RT)){ - flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush - return 0; - } - //TX OK 1 or 0 - return 1; -} - -bool RF24::write( const void* buf, uint8_t len ){ - return write(buf,len,0); -} -/****************************************************************************/ - -//For general use, the interrupt flags are not important to clear -bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) -{ - //Block until the FIFO is NOT full. - //Keep track of the MAX retries and set auto-retry if seeing failures - //This way the FIFO will fill up and allow blocking until packets go through - //The radio will auto-clear everything in the FIFO as long as CE remains high - - uint32_t timer = millis(); //Get the time that the payload transmission started - - while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout - - if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached - reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag - if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 - } - - } - - //Start Writing - startFastWrite(buf,len,0); //Write the payload if a buffer is clear - - return 1; //Return 1 to indicate successful transmission -} - -/****************************************************************************/ - -void RF24::reUseTX(){ - write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag - spiTrans( REUSE_TX_PL ); - ce(LOW); //Re-Transfer packet - ce(HIGH); -} - -/****************************************************************************/ - -bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) -{ - //Block until the FIFO is NOT full. - //Keep track of the MAX retries and set auto-retry if seeing failures - //Return 0 so the user can control the retrys and set a timer or failure counter if required - //The radio will auto-clear everything in the FIFO as long as CE remains high - - while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail - - if( get_status() & _BV(MAX_RT)){ - //reUseTX(); //Set re-transmit - write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag - return 0; //Return 0. The previous payload has been retransmitted - //From the user perspective, if you get a 0, just keep trying to send the same payload - } - - } - //Start Writing - startFastWrite(buf,len,multicast); - - return 1; -} - -bool RF24::writeFast( const void* buf, uint8_t len ){ - return writeFast(buf,len,0); -} - -/****************************************************************************/ - -//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high -//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) -//Otherwise we enter Standby-II mode, which is still faster than standby mode -//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data - -void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 - - //write_payload( buf,len); - write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; - ce(HIGH); - -} - - -//Added the original startWrite back in so users can still use interrupts, ack payloads, etc -//Allows the library to pass all tests -void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){ - - // Send the payload - - //write_payload( buf, len ); - write_payload( buf, len,multicast? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; - - ce(HIGH); - ce(LOW); - - -} - -bool RF24::txStandBy(){ - - while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ - if( get_status() & _BV(MAX_RT)){ - write_register(STATUS,_BV(MAX_RT) ); - ce(LOW); - flush_tx(); //Non blocking, flush the data - return 0; - } - } - - ce(LOW); //Set STANDBY-I mode - return 1; -} - -bool RF24::txStandBy(uint32_t timeout){ - - uint32_t start = millis(); - - while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ - if( get_status() & _BV(MAX_RT)){ - write_register(STATUS,_BV(MAX_RT) ); - ce(LOW); //Set re-transmit - ce(HIGH); - if(millis() - start >= timeout){ - ce(LOW); flush_tx(); return 0; - } - } - } - ce(LOW); //Set STANDBY-I mode - return 1; - -} -/****************************************************************************/ - -void RF24::maskIRQ(bool tx, bool fail, bool rx){ - - write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); -} - -/****************************************************************************/ - -uint8_t RF24::getDynamicPayloadSize(void) -{ - uint8_t result = 0; - - #if defined (__arm__) - SPI.transfer(csn_pin, R_RX_PL_WID, SPI_CONTINUE ); - result = SPI.transfer(csn_pin,0xff); - #else - csn(LOW); - SPI.transfer( R_RX_PL_WID ); - result = SPI.transfer(0xff); - csn(HIGH); - - #endif - - if(result > 32) { flush_rx(); return 0; } - return result; -} - -/****************************************************************************/ - -bool RF24::available(void) -{ - return available(NULL); -} - -/****************************************************************************/ - -bool RF24::available(uint8_t* pipe_num) -{ - - //Check the FIFO buffer to see if data is waitng to be read - - if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ - - // If the caller wants the pipe number, include that - if ( pipe_num ){ - uint8_t status = get_status(); - *pipe_num = ( status >> RX_P_NO ) & B111; - } - return 1; - } - - return 0; - - - -} - -/****************************************************************************/ - -void RF24::read( void* buf, uint8_t len ){ - - // Fetch the payload - read_payload( buf, len ); - - //Clear the two possible interrupt flags with one command - write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); - -} - -/****************************************************************************/ - -void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) -{ - // Read the status & reset the status in one easy call - // Or is that such a good idea? - uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Report to the user what happened - tx_ok = status & _BV(TX_DS); - tx_fail = status & _BV(MAX_RT); - rx_ready = status & _BV(RX_DR); -} - -/****************************************************************************/ - -void RF24::openWritingPipe(uint64_t value) -{ - // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) - // expects it LSB first too, so we're good. - - write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); - write_register(TX_ADDR, reinterpret_cast(&value), 5); - - //const uint8_t max_payload_size = 32; - //write_register(RX_PW_P0,min(payload_size,max_payload_size)); - write_register(RX_PW_P0,payload_size); -} - -/****************************************************************************/ - -static const uint8_t child_pipe[] PROGMEM = -{ - RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 -}; -static const uint8_t child_payload_size[] PROGMEM = -{ - RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 -}; -static const uint8_t child_pipe_enable[] PROGMEM = -{ - ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 -}; - -void RF24::openReadingPipe(uint8_t child, uint64_t address) -{ - // If this is pipe 0, cache the address. This is needed because - // openWritingPipe() will overwrite the pipe 0 address, so - // startListening() will have to restore it. - if (child == 0) - pipe0_reading_address = address; - - if (child <= 6) - { - // For pipes 2-5, only write the LSB - if ( child < 2 ) - write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); - else - write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); - - write_register(pgm_read_byte(&child_payload_size[child]),payload_size); - - // Note it would be more efficient to set all of the bits for all open - // pipes at once. However, I thought it would make the calling code - // more simple to do it this way. - write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); - } -} - -/****************************************************************************/ - -void RF24::toggle_features(void) -{ - - #if defined (__arm__) - SPI.transfer(csn_pin, ACTIVATE, SPI_CONTINUE ); - SPI.transfer(csn_pin, 0x73 ); - #else - csn(LOW); - SPI.transfer( ACTIVATE ); - SPI.transfer( 0x73 ); - csn(HIGH); - #endif -} - -/****************************************************************************/ - -void RF24::enableDynamicPayloads(void) -{ - // Enable dynamic payload throughout the system - - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); - - - IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); - - // Enable dynamic payload on all pipes - // - // Not sure the use case of only having dynamic payload on certain - // pipes, so the library does not support it. - write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); - - dynamic_payloads_enabled = true; -} - -/****************************************************************************/ - -void RF24::enableAckPayload(void) -{ - // - // enable ack payload and dynamic payload features - // - - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); - - IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); - - // - // Enable dynamic payload on pipes 0 & 1 - // - - write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); -} - -/****************************************************************************/ - -void RF24::enableDynamicAck(void){ - // - // enable dynamic ack features - // - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); - - IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); - - -} - -/****************************************************************************/ - -void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) -{ - const uint8_t* current = reinterpret_cast(buf); - - uint8_t data_len = min(len,32); - - #if defined (__arm__) - SPI.transfer(csn_pin, W_ACK_PAYLOAD | ( pipe & B111 ), SPI_CONTINUE); - while ( data_len-- > 1 ){ - SPI.transfer(csn_pin,*current++, SPI_CONTINUE); - } - SPI.transfer(csn_pin,*current++); - - #else - csn(LOW); - SPI.transfer(W_ACK_PAYLOAD | ( pipe & B111 ) ); - - while ( data_len-- ) - SPI.transfer(*current++); - - csn(HIGH); - - #endif -} - -/****************************************************************************/ - -bool RF24::isAckPayloadAvailable(void) -{ - return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); -} - -/****************************************************************************/ - -bool RF24::isPVariant(void) -{ - return p_variant ; -} - -/****************************************************************************/ - -void RF24::setAutoAck(bool enable) -{ - if ( enable ) - write_register(EN_AA, B111111); - else - write_register(EN_AA, 0); -} - -/****************************************************************************/ - -void RF24::setAutoAck( uint8_t pipe, bool enable ) -{ - if ( pipe <= 6 ) - { - uint8_t en_aa = read_register( EN_AA ) ; - if( enable ) - { - en_aa |= _BV(pipe) ; - } - else - { - en_aa &= ~_BV(pipe) ; - } - write_register( EN_AA, en_aa ) ; - } -} - -/****************************************************************************/ - -bool RF24::testCarrier(void) -{ - return ( read_register(CD) & 1 ); -} - -/****************************************************************************/ - -bool RF24::testRPD(void) -{ - return ( read_register(RPD) & 1 ) ; -} - -/****************************************************************************/ - -void RF24::setPALevel(uint8_t level) -{ - - uint8_t setup = read_register(RF_SETUP) & 0b11111000; - - if(level > 3){ // If invalid level, go to max PA - level = RF24_PA_MAX << 1 + 1; // +1 to support the SI24R1 chip extra bit - }else{ - level = (level << 1) + 1; // Else set level as requested - } - - - write_register( RF_SETUP, setup |= level ) ; // Write it to the chip -} - -/****************************************************************************/ - -uint8_t RF24::getPALevel(void) -{ - - return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; -} - -/****************************************************************************/ - -bool RF24::setDataRate(rf24_datarate_e speed) -{ - bool result = false; - uint8_t setup = read_register(RF_SETUP) ; - - // HIGH and LOW '00' is 1Mbs - our default - wide_band = false ; - setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; - if( speed == RF24_250KBPS ) - { - // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 - // Making it '10'. - wide_band = false ; - setup |= _BV( RF_DR_LOW ) ; - } - else - { - // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 - // Making it '01' - if ( speed == RF24_2MBPS ) - { - wide_band = true ; - setup |= _BV(RF_DR_HIGH); - } - else - { - // 1Mbs - wide_band = false ; - } - } - write_register(RF_SETUP,setup); - - // Verify our result - if ( read_register(RF_SETUP) == setup ) - { - result = true; - } - else - { - wide_band = false; - } - - return result; -} - -/****************************************************************************/ - -rf24_datarate_e RF24::getDataRate( void ) -{ - rf24_datarate_e result ; - uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); - - // switch uses RAM (evil!) - // Order matters in our case below - if ( dr == _BV(RF_DR_LOW) ) - { - // '10' = 250KBPS - result = RF24_250KBPS ; - } - else if ( dr == _BV(RF_DR_HIGH) ) - { - // '01' = 2MBPS - result = RF24_2MBPS ; - } - else - { - // '00' = 1MBPS - result = RF24_1MBPS ; - } - return result ; -} - -/****************************************************************************/ - -void RF24::setCRCLength(rf24_crclength_e length) -{ - uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - - // switch uses RAM (evil!) - if ( length == RF24_CRC_DISABLED ) - { - // Do nothing, we turned it off above. - } - else if ( length == RF24_CRC_8 ) - { - config |= _BV(EN_CRC); - } - else - { - config |= _BV(EN_CRC); - config |= _BV( CRCO ); - } - write_register( CONFIG, config ) ; -} - -/****************************************************************************/ - -rf24_crclength_e RF24::getCRCLength(void) -{ - rf24_crclength_e result = RF24_CRC_DISABLED; - uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; - - if ( config & _BV(EN_CRC ) ) - { - if ( config & _BV(CRCO) ) - result = RF24_CRC_16; - else - result = RF24_CRC_8; - } - - return result; -} - -/****************************************************************************/ - -void RF24::disableCRC( void ) -{ - uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; - write_register( CONFIG, disable ) ; -} - -/****************************************************************************/ -void RF24::setRetries(uint8_t delay, uint8_t count) -{ - write_register(SETUP_RETR,(delay&0xf)< - - 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. - */ - -/** - * @file RF24.h - * - * Class declaration for RF24 and helper enums - */ - -#ifndef __RF24_H__ -#define __RF24_H__ - -#include - -/** - * Power Amplifier level. - * - * For use with setPALevel() - */ -typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; - -/** - * Data rate. How fast data moves through the air. - * - * For use with setDataRate() - */ -typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; - -/** - * CRC Length. How big (if any) of a CRC is included. - * - * For use with setCRCLength() - */ -typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; - -/** - * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver - */ - -class RF24 -{ -private: - uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ - uint8_t csn_pin; /**< SPI Chip select */ - bool wide_band; /* 2Mbs data rate in use? */ - bool p_variant; /* False for RF24L01 and true for RF24L01P */ - uint8_t payload_size; /**< Fixed size of payloads */ - bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ - uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ - -public: - - /** - * @name Primary public interface - * - * These are the main methods you need to operate the chip - */ - /**@{*/ - - /** - * Constructor - * - * Creates a new instance of this driver. Before using, you create an instance - * and send in the unique pins that this chip is connected to. - * - * @param _cepin The pin attached to Chip Enable on the RF module - * @param _cspin The pin attached to Chip Select - */ - RF24(uint8_t _cepin, uint8_t _cspin); - - /** - * Begin operation of the chip - * - * Call this in setup(), before calling any other methods. - * - * @note Optimization: The radio is partially configured in PTX mode - * when begin is called to make the transition to PTX mode simpler. - */ - void begin(void); - - /** - * Start listening on the pipes opened for reading. - * - * Be sure to call openReadingPipe() first. Do not call write() while - * in this mode, without first calling stopListening(). Call - * isAvailable() to check for incoming traffic, and read() to get it. - */ - void startListening(void); - - /** - * Stop listening for incoming messages - * - * Do this before calling write(). - * - * @note Optimization: The radio will be taken out of PRX mode as soon - * as listening is stopped. Enables quicker and simpler engaging of - * primary transmitter (PTX) mode. - */ - void stopListening(void); - - /** - * Test whether there are bytes available to be read - * - * @note Optimization: The available functino now checks the FIFO - * buffers directly for data instead of relying of interrupt flags. - * - * @note Interrupt flags will not be cleared until a payload is - * actually read from the FIFO - * - * @see txStandBy() - * @see startWrite() - * @see write() - * @see writeFast() - * - * @return True if there is a payload available, false if none is - */ - bool available(void); - - /** - * Read the payload - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @note I specifically chose 'void*' as a data type to make it easier - * for beginners to use. No casting needed. - * - * @note Optimization: No longer boolean. Use available to - * determine if packets are available. Interrupt flags are now cleared - * during reads instead of when calling available(). - * - * @param buf Pointer to a buffer where the data should be written - * @param len Maximum number of bytes to read into the buffer - * @return No return value. Use available(). - */ - void read( void* buf, uint8_t len ); - - /** - * @note Optimization: Improved performance slightly - * Write to the open writing pipe - * - * Be sure to call openWritingPipe() first to set the destination - * of where to write to. - * - * This blocks until the message is successfully acknowledged by - * the receiver or the timeout/retransmit maxima are reached. In - * the current configuration, the max delay here is 60ms. - * - * The maximum size of data written is the fixed payload size, see - * getPayloadSize(). However, you can write less, and the remainder - * will just be filled with zeroes. - * - * TX/RX/RT interrupt flags will be cleared every time write is called - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - bool write( const void* buf, uint8_t len ); - - /** - * Open a pipe for writing - * - * Only one pipe can be open at once, but you can change the pipe - * you'll listen to. Do not call this while actively listening. - * Remember to stopListening() first. - * - * Addresses are 40-bit hex values, e.g.: - * - * @code - * openWritingPipe(0xF0F0F0F0F0); - * @endcode - * - * @param address The 40-bit address of the pipe to open. This can be - * any value whatsoever, as long as you are the only one writing to it - * and only one other radio is listening to it. Coordinate these pipe - * addresses amongst nodes on the network. - */ - void openWritingPipe(uint64_t address); - - /** - * Open a pipe for reading - * - * Up to 6 pipes can be open for reading at once. Open all the - * reading pipes, and then call startListening(). - * - * @see openWritingPipe - * - * @warning Pipes 1-5 should share the first 32 bits. - * Only the least significant byte should be unique, e.g. - * @code - * openReadingPipe(1,0xF0F0F0F0AA); - * openReadingPipe(2,0xF0F0F0F066); - * @endcode - * - * @warning Pipe 0 is also used by the writing pipe. So if you open - * pipe 0 for reading, and then startListening(), it will overwrite the - * writing pipe. Ergo, do an openWritingPipe() again before write(). - * - * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits - * - * @param number Which pipe# to open, 0-5. - * @param address The 40-bit address of the pipe to open. - */ - void openReadingPipe(uint8_t number, uint64_t address); - - /**@}*/ - /** - * @name Advanced Operation - * - * Methods you can use to drive the chip in more advanced ways - */ - /**@{*/ - - /** - * Print a giant block of debugging information to stdout - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void printDetails(void); - - /** - * Test whether there are bytes available to be read in the - * FIFO buffers. This optimized version does not rely on interrupt - * flags, but checks the actual FIFO buffers. - * - * @note Optimization: Interrupt flags are no longer cleared when available is called, - * but will be reset only when the data is read from the FIFO buffers. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - bool available(uint8_t* pipe_num); - - /** - * Enter low-power mode - * - * To return to normal power mode, either write() some data or - * startListening, or powerUp(). - * - * @note Optimization: The radio will never enter power down unless instructed - * by the MCU via this command. - */ - void powerDown(void); - - /** - * Leave low-power mode - making radio more responsive - * - * To return to low power mode, call powerDown(). - */ - void powerUp(void) ; - - /** - * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. - * - * @note enableDynamicAck() must be called to enable this feature - * - * Can be used with enableAckPayload() to request a response - * @see enableDynamicAck() - * @see setAutoAck() - * @see write() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0), NOACK (1) - */ - bool write( const void* buf, uint8_t len, const bool multicast ); - - /** - * @note Optimization: New Command * - * This will not block until the 3 FIFO buffers are filled with data. - * Once the FIFOs are full, writeFast will simply wait for success or - * timeout, and return 1 or 0 respectively. From a user perspective, just - * keep trying to send the same data. The library will keep auto retrying - * the current payload using the built in functionality. - * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto - * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO - * to clear by issuing txStandBy() or ensure appropriate time between transmissions. - * - * ONLY max retry interrupt flags will be cleared when writeFast is called - * - * @code - * Example (Partial blocking): - * - * radio.writeFast(&buf,32); // Writes 1 payload to the buffers - * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. - * - * radio.writeFast(&buf,32); // Writes 1 payload to the buffers - * txStandBy(1000); // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0. - * @endcode - * - * @see txStandBy() - * @see write() - * @see writeBlocking() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - bool writeFast( const void* buf, uint8_t len ); - - /** - * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. - * - * @note enableDynamicAck() must be called to enable this feature - * @see enableDynamicAck() - * @see setAutoAck() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0) or NOACK (1) - */ - bool writeFast( const void* buf, uint8_t len, const bool multicast ); - - /** - * @note Optimization: New Command - * This function extends the auto-retry mechanism to any specified duration. - * It will not block until the 3 FIFO buffers are filled with data. - * If so the library will auto retry until a new payload is written - * or the user specified timeout period is reached. - * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto - * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO - * to clear by issuing txStandBy() or ensure appropriate time between transmissions. - * - * ONLY max retry interrupt flags will be cleared when writeBlocking is called - * @code - * Example (Full blocking): - * - * radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers - * txStandBy(1000); //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed. - * //Blocks only until user timeout or success. Data flushed on fail. - * @endcode - * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). - * @see txStandBy() - * @see write() - * @see writeFast() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param timeout User defined timeout in milliseconds. - * @return True if the payload was loaded into the buffer successfully false if not - */ - bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); - - /** - * @note Optimization: New Command - * This function should be called as soon as transmission is finished to - * drop the radio back to STANDBY-I mode. If not issued, the radio will - * remain in STANDBY-II mode which, per the data sheet, is not a recommended - * operating mode. - * - * @note When transmitting data in rapid succession, it is still recommended by - * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is - * time enough between sends for the FIFOs to empty. - * - * Relies on built-in auto retry functionality. - * - * @code - * Example (Partial blocking): - * - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); //Fills the FIFO buffers up - * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. - * //Blocks only until MAX_RT timeout or success. Data flushed on fail. - * @endcode - * @see txStandBy(unsigned long timeout) - * @return True if transmission is successful - * - */ - bool txStandBy(); - - /** - * @note Optimization: New Command - * - * This function allows extended blocking and auto-retries per a user defined timeout - * @code - * Fully Blocking Example: - * - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); //Fills the FIFO buffers up - * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. - * //Blocks only until user defined timeout or success. Data flushed on fail. - * @endcode - * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). - * @param timeout Number of milliseconds to retry failed payloads - * @return True if transmission is successful - * - */ - bool txStandBy(uint32_t timeout); - - /** - * Write an ack payload for the specified pipe - * - * The next time a message is received on @p pipe, the data in @p buf will - * be sent back in the acknowledgement. - * - * @warning According to the data sheet, only three of these can be pending - * at any time as there are only 3 FIFO buffers. - * - * @param pipe Which pipe# (typically 1-5) will get this response. - * @param buf Pointer to data that is sent - * @param len Length of the data to send, up to 32 bytes max. Not affected - * by the static payload set by setPayloadSize(). - */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); - - /** - * Enable dynamic ACKs (single write multicasting) for chosen messages - * - * @note To enable full multicasting or per-pipe multicast, use setAutoAck() - * - * @warning This MUST be called prior to attempting single write NOACK calls - * @code - * radio.enableDynamicAck(); - * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested - * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK - * @endcode - */ - void enableDynamicAck(); - - /** - * Determine if an ack payload was received in the most recent call to - * write(). - * - * Call read() to retrieve the ack payload. - * - * @note Optimization: Calling this function NO LONGER clears the interrupt - * flag. The new functionality checks the RX FIFO buffer for an ACK payload - * instead of relying on interrupt flags.Reading the payload will clear the flags. - * - * @return True if an ack payload is available. - */ - bool isAckPayloadAvailable(void); - - /** - * Call this when you get an interrupt to find out why - * - * Tells you what caused the interrupt, and clears the state of - * interrupts. - * - * @param[out] tx_ok The send was successful (TX_DS) - * @param[out] tx_fail The send failed, too many retries (MAX_RT) - * @param[out] rx_ready There is a message waiting to be read (RX_DS) - */ - void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); - - /** - * Non-blocking write to the open writing pipe used for buffered writes - * - * @note Optimization: This function now leaves the CE pin high, so the radio - * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. - * This allows the chip to be used to its full potential in TX mode. - * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto - * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO - * to clear by issuing txStandBy() or ensure appropriate time between transmissions. - * - * @see write() - * @see writeFast() - * @see startWrite() - * @see writeBlocking() - * - * For single noAck writes see: - * @see enableDynamicAck() - * @see setAutoAck() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0) or NOACK (1) - * @return True if the payload was delivered successfully false if not - */ - void startFastWrite( const void* buf, uint8_t len, const bool multicast ); - - /** - * Non-blocking write to the open writing pipe - * - * Just like write(), but it returns immediately. To find out what happened - * to the send, catch the IRQ and then call whatHappened(). - * - * @note Optimization: This function again behaves as it did previously for backwards-compatibility. - * with user code. The library uses startFastWrite() internally. - * This is mainly used for single-payload transactions. - * - * @see write() - * @see writeFast() - * @see startFastWrite() - * @see whatHappened() - * - * For single noAck writes see: - * @see enableDynamicAck() - * @see setAutoAck() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0) or NOACK (1) - * - */ - void startWrite( const void* buf, uint8_t len, const bool multicast ); - - /** - * Optimization: New Command - * - * This function is mainly used internally to take advantage of the auto payload - * re-use functionality of the chip, but can be beneficial to users as well. - * - * The function will instruct the radio to re-use the data in the FIFO buffers, - * and instructs the radio to re-send once the timeout limit has been reached. - * Used by writeFast and writeBlocking to initiate retries when a TX failure - * occurs. Retries are automatically initiated except with the standard write(). - * This way, data is not flushed from the buffer until switching between modes. - * - * @note This is to be used AFTER auto-retry fails if wanting to resend - * using the built-in payload reuse features. - * After issuing reUseTX(), it will keep reending the same payload forever or until - * a payload is written to the FIFO, or a flush_tx command is given. - */ - void reUseTX(); - - /** - * Empty the transmit buffer - * - * @return Current value of status register - */ - uint8_t flush_tx(void); - - /** - * Test whether there was a carrier on the line for the - * previous listening period. - * - * Useful to check for interference on the current channel. - * - * @return true if was carrier, false if not - */ - bool testCarrier(void); - - /** - * Test whether a signal (carrier or otherwise) greater than - * or equal to -64dBm is present on the channel. Valid only - * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). - * - * Useful to check for interference on the current channel and - * channel hopping strategies. - * - * @return true if signal => -64dBm, false if not - */ - bool testRPD(void) ; - - /** - * Test whether this is a real radio, or a mock shim for - * debugging. Setting either pin to 0xff is the way to - * indicate that this is not a real radio. - * - * @return true if this is a legitimate radio - */ - bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } - - /** - * The radio will generate interrupt signals when a transmission is complete, - * a transmission fails, or a payload is received. This allows users to mask - * those interrupts to prevent them from generating a signal on the interrupt - * pin. - * - * @code - * Mask all interrupts except the receive interrupt: - * - * radio.maskIRQ(1,1,0); - * @endcode - * - * @param tx_ok Mask transmission complete interrupts - * @param tx_fail Mask transmit failure interrupts - * @param rx_ready Mask payload received interrupts - */ - void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); - - /**@}*/ - - /**@}*/ - /** - * @name Optional Configurators - * - * Methods you can use to get or set the configuration of the chip. - * None are required. Calling begin() sets up a reasonable set of - * defaults. - */ - /**@{*/ - - /** - * Set the number and delay of retries upon failed submit - * - * @param delay How long to wait between each retry, in multiples of 250us, - * max is 15. 0 means 250us, 15 means 4000us. - * @param count How many retries before giving up, max 15 - */ - void setRetries(uint8_t delay, uint8_t count); - - /** - * Set RF communication channel - * - * @param channel Which RF channel to communicate on, 0-127 - */ - void setChannel(uint8_t channel); - - /** - * Set Static Payload Size - * - * This implementation uses a pre-stablished fixed payload size for all - * transmissions. If this method is never called, the driver will always - * transmit the maximum payload size (32 bytes), no matter how much - * was sent to write(). - * - * @todo Implement variable-sized payloads feature - * - * @param size The number of bytes in the payload - */ - void setPayloadSize(uint8_t size); - - /** - * Get Static Payload Size - * - * @see setPayloadSize() - * - * @return The number of bytes in the payload - */ - uint8_t getPayloadSize(void); - - /** - * Get Dynamic Payload Size - * - * For dynamic payloads, this pulls the size of the payload off - * the chip - * - * @note Optimization: Corrupt packets are now detected and flushed per the - * manufacturer. - * - * @return Payload length of last-received dynamic payload - */ - uint8_t getDynamicPayloadSize(void); - - /** - * Enable custom payloads on the acknowledge packets - * - * Ack payloads are a handy way to return data back to senders without - * manually changing the radio modes on both units. - * - * @see examples/pingpair_pl/pingpair_pl.pde - */ - void enableAckPayload(void); - - /** - * Enable dynamically-sized payloads - * - * This way you don't always have to send large packets just to send them - * once in a while. This enables dynamic payloads on ALL pipes. - * - * @see examples/pingpair_pl/pingpair_dyn.pde - */ - void enableDynamicPayloads(void); - - /** - * Determine whether the hardware is an nRF24L01+ or not. - * - * @return true if the hardware is nRF24L01+ (or compatible) and false - * if its not. - */ - bool isPVariant(void) ; - - /** - * Enable or disable auto-acknowlede packets - * - * This is enabled by default, so it's only needed if you want to turn - * it off for some reason. - * - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck(bool enable); - - /** - * Enable or disable auto-acknowlede packets on a per pipeline basis. - * - * AA is enabled by default, so it's only needed if you want to turn - * it off/on for some reason on a per pipeline basis. - * - * @param pipe Which pipeline to modify - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck( uint8_t pipe, bool enable ) ; - - /** - * Set Power Amplifier (PA) level to one of four levels: - * RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX - * - * The power levels correspond to the following output levels respectively: - * NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm - * - * SI24R1: -6dBm, 0dBm, 3dBM, and 7dBm. - * - * @param level Desired PA level. - */ - void setPALevel ( uint8_t level ); - - /** - * Fetches the current PA level. - * - * NRF24L01: -18dBm, -12dBm, -6dBm and 0dBm - * SI24R1: -6dBm, 0dBm, 3dBm, 7dBm - * - * @return Returns values 0 to 3 representing the PA Level. - */ - uint8_t getPALevel( void ); - - /** - * Set the transmission data rate - * - * @warning setting RF24_250KBPS will fail for non-plus units - * - * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps - * @return true if the change was successful - */ - bool setDataRate(rf24_datarate_e speed); - - /** - * Fetches the transmission data rate - * - * @return Returns the hardware's currently configured datarate. The value - * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the - * rf24_datarate_e enum. - */ - rf24_datarate_e getDataRate( void ) ; - - /** - * Set the CRC length - * - * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - void setCRCLength(rf24_crclength_e length); - - /** - * Get the CRC length - * - * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - rf24_crclength_e getCRCLength(void); - - /** - * Disable CRC validation - * - */ - void disableCRC( void ) ; - -private: - - /** - * @name Low-level internal interface. - * - * Protected methods that address the chip directly. Regular users cannot - * ever call these. They are documented for completeness and for developers who - * may want to extend this class. - */ - /**@{*/ - - /** - * Set chip select pin - * - * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data - * and best of all, we make use of the radio's FIFO buffers. A lower speed - * means we're less likely to effectively leverage our FIFOs and pay a higher - * AVR runtime cost as toll. - * - * @param mode HIGH to take this unit off the SPI bus, LOW to put it on - */ - void csn(bool mode); - - /** - * Set chip enable - * - * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet - * for a much more detailed description of this pin. - */ - void ce(bool level); - - /** - * Read a chunk of data in from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to put the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); - - /** - * Read single byte from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @return Current value of register @p reg - */ - uint8_t read_register(uint8_t reg); - - /** - * Write a chunk of data to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to get the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); - - /** - * Write a single byte to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param value The new value to write - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, uint8_t value); - - /** - * Write the transmit payload - * - * The size of data written is the fixed payload size, see getPayloadSize() - * - * @param buf Where to get the data - * @param len Number of bytes to be sent - * @return Current value of status register - */ - uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); - - /** - * Read the receive payload - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @param buf Where to put the data - * @param len Maximum number of bytes to read - * @return Current value of status register - */ - uint8_t read_payload(void* buf, uint8_t len); - - /** - * Empty the receive buffer - * - * @return Current value of status register - */ - uint8_t flush_rx(void); - - /** - * Retrieve the current status of the chip - * - * @return Current value of status register - */ - uint8_t get_status(void); - - /** - * Decode and print the given status to stdout - * - * @param status Status value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_status(uint8_t status); - - /** - * Decode and print the given 'observe_tx' value to stdout - * - * @param value The observe_tx value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_observe_tx(uint8_t value); - - /** - * Print the name and value of an 8-bit register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Print the name and value of a 40-bit address register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Turn on or off the special features of the chip - * - * The chip has certain 'features' which are only available when the 'features' - * are enabled. See the datasheet for details. - */ - void toggle_features(void); - - /** - * Built in spi transfer function to simplify repeating code repeating code - */ - - uint8_t spiTrans(uint8_t cmd); - /**@}*/ - -}; - -/** - * @example GettingStarted.ino - * Updated: TMRh20 2014
- * - * This is an example of how to use the RF24 class to communicate on a basic level. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting with the serial monitor and
- * sending a 'T'. The ping node sends the current time to the pong node, which responds by sending the value - * back. The ping node can then see how long the whole cycle took.
- * @note For a more efficient call-response scenario see the GettingStarted_CallResponse.ino example. - * @note When switching between sketches, the radio may need to be powered down to clear settings that are not "un-set" otherwise - */ - -/** - * @example GettingStarted_CallResponse.ino - * New: TMRh20 2014
- * - * This example continues to make use of all the normal functionality of the radios including - * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
- * This allows very fast call-response communication, with the responding radio never having to - * switch out of Primary Receiver mode to send back a payload, but having the option to switch to
- * primary transmitter if wanting to initiate communication instead of respond to a commmunication. - */ - -/** - * @example Transfer.ino - * New: TMRh20
- * This example demonstrates half-rate transfer using the FIFO buffers
- * - * It is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting
- * with the serial monitor and sending a 'T'. The data transfer will begin, - * with the receiver displaying the payload count. (32Byte Payloads)
- */ - -/** - * @example TransferTimeouts.ino - * New: TMRh20
- * This example demonstrates the use of and extended timeout period and - * auto-retries/auto-reUse to increase reliability in noisy or low signal scenarios.
- * - * Write this sketch to two different nodes. Put one of the nodes into 'transmit' - * mode by connecting with the serial monitor and sending a 'T'. The data
- * transfer will begin, with the receiver displaying the payload count and the - * data transfer rate. - */ - -/** - * @example starping.pde - * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the - * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will - * respond back with a copy of the value. Each ping unit can get that response - * back, and determine how long the whole cycle took. - * - * This example requires a bit more complexity to determine which unit is which. - * The pong receiver is identified by having its role_pin tied to ground. - * The ping senders are further differentiated by a byte in eeprom. - */ - -/** - * @example pingpair_ack.ino - * Update: TMRh20
- * This example continues to make use of all the normal functionality of the radios including - * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
- * This allows very fast call-response communication, with the responding radio never having to - * switch out of Primary Receiver mode to send back a payload, but having the option to if wanting
- * to initiate communication instead of respond to a commmunication. - */ - -/** - * @example pingpair_irq.ino - * Update: TMRh20
- * This is an example of how to user interrupts to interact with the radio, and a demonstration - * of how to use them to sleep when receiving, and not miss any payloads.
- * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter. - * Sleep functionality is built directly into my fork of the RF24Network library
- */ - -/** - * @example pingpair_sleepy.ino - * Update: TMRh20
- * This is an example of how to use the RF24 class to create a battery- - * efficient system. It is just like the GettingStarted_CallResponse example, but the
- * ping node powers down the radio and sleeps the MCU after every - * ping/pong cycle, and the receiver sleeps between payloads.
- */ - -/** - * @example pingpair_dyn.ino - * - * This is an example of how to use payloads of a varying (dynamic) size. - */ - -/** - * @example pingpair_maple.pde - * - * This is an example of how to use the RF24 class on the Maple. For a more - * detailed explanation, see my blog post: - * nRF24L01+ Running on Maple - * - * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. - * - * Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -/** - * @example nordic_fob.pde - * - * This is an example of how to use the RF24 class to receive signals from the - * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . - * Thanks to Kirk Mower for providing test hardware. - */ - -/** - * @example led_remote.pde - * - * This is an example of how to use the RF24 class to control a remote - * bank of LED's using buttons on a remote control. - * - * Every time the buttons change on the remote, the entire state of - * buttons is send to the led board, which displays the state. - */ - -/** - * @example scanner.ino - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -/** - * @mainpage Optimized High Speed Driver for nRF24L01(+) 2.4GHz Wireless Transceiver - * - * @section Goals Design Goals - * - * This library fork is designed to be... - * @li More compliant with the manufacturer specified operation of the chip, while allowing advanced users - * to work outside the reccommended operation. - * @li Utilize the capabilities of the radio to their full potential via Arduino - * @li More reliable, responsive and feature rich - * @li Easy for beginners to use, with well documented examples and features - * @li Consumed with a public interface that's similiar to other Arduino standard libraries - * - * @section News News - * - * April 2014: Official Release: Still some work to do, but most benefits have been realized
- * - The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds - * - Changes to read() and available () functionality have increased reliability and response - * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments - * - Delays have been removed where possible to ensure maximum efficiency - * - Full Due support with extended SPI functions - * - ATTiny 24/44/84 25/45/85 now supported. - * - More! See the links below and class documentation for more info. - * - * If issues are discovered with the documentation, please report them here: here - * @section Useful Useful References - * - * Please refer to: - * - * @li Documentation Main Page - * @li RF24 Class Documentation - * @li Source Code - * @li Downloads Page - * @li Chip Datasheet - * @li Original Library - * - * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or - * the SPI hardware will go into 'slave' mode. - * - * @section Board_Support Board Support - * - * Most standard Arduino based boards are supported: - * - ATMega 328 based boards (Uno, Nano, etc) - * - Mega Boards (1280, 2560, etc) - * - ARM based boards (Arduino Due) Note: Do not include printf.h or use printf begin. This functionality is already present. Must use one of the - * hardware SS/CSN pins as extended SPI methods are used. - * Initial Due support taken from https://github.com/mcrosson/RF24/tree/due - * - ATTiny board support added from https://github.com/jscrane/RF24 - * Note: ATTiny support is built into the library. Do not include SPI.h.
- * ATTiny 85: D0(pin 5): MISO, D1(pin6) MOSI, D2(pin7) SCK, D3(pin2):CSN/SS, D4(pin3): CE
- * ATTiny 84: PA6:MISO, PA5:MOSI, PA4:SCK, PA7:CSN/SS, CE as desired
- * - * @section More More Information - * - * @section Info and Projects - * - * @li Project blog: - * @li TMRh20.blogspot.com - * @li RF24 Wireless Audio Library (Coming Soon) - * @li Optimized RF24 Network Layer - * @li ManiacBug on GitHub (Original Library Author) - */ - -#endif // __RF24_H__ - - diff --git a/RF24/RF24_config.h b/RF24/RF24_config.h deleted file mode 100644 index b7154a5..0000000 --- a/RF24/RF24_config.h +++ /dev/null @@ -1,129 +0,0 @@ - -/* - Copyright (C) 2011 J. Coliz - - 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. - - Added Arduino Due support from https://github.com/mcrosson/ - */ - -#ifndef __RF24_CONFIG_H__ -#define __RF24_CONFIG_H__ - - #if ARDUINO < 100 - #include - #else - #include - #endif - - #include - - //TMRh20: - //#define MINIMAL - - // Define _BV for non-Arduino platforms and for Arduino DUE -#if defined (ARDUINO) - #include -#else - #include - #include - #include - extern HardwareSPI SPI; - #define _BV(x) (1<<(x)) - #endif - - - #undef SERIAL_DEBUG - #ifdef SERIAL_DEBUG - #define IF_SERIAL_DEBUG(x) ({x;}) - #else - #define IF_SERIAL_DEBUG(x) - #if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) - #define printf_P(...) - #endif - #endif - -// Avoid spurious warnings -// Arduino DUE is arm and uses traditional PROGMEM constructs -#if 1 -#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) -#undef PROGMEM -#define PROGMEM __attribute__(( section(".progmem.data") )) -#undef PSTR -#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) -#endif -#endif - -// Progmem is Arduino-specific -// Arduino DUE is arm and does not include avr/pgmspace -#if defined(ARDUINO) && ! defined(__arm__) - #include - #define PRIPSTR "%S" -#else -#if ! defined(ARDUINO) // This doesn't work on Arduino DUE - typedef char const char; -#else // Fill in pgm_read_byte that is used, but missing from DUE - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) -#endif - - typedef uint16_t prog_uint16_t; - #define PSTR(x) (x) - #define printf_P printf - #define strlen_P strlen - #define PROGMEM - #define pgm_read_word(p) (*(p)) - #define PRIPSTR "%s" -#endif - - - -// ATTiny support code is from https://github.com/jscrane/RF24 - -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) -#include -#include -#include - -#define SPI_CLOCK_DIV4 0x00 -#define SPI_CLOCK_DIV16 0x01 -#define SPI_CLOCK_DIV64 0x02 -#define SPI_CLOCK_DIV128 0x03 -#define SPI_CLOCK_DIV2 0x04 -#define SPI_CLOCK_DIV8 0x05 -#define SPI_CLOCK_DIV32 0x06 -//#define SPI_CLOCK_DIV64 0x07 - -#define SPI_MODE0 0x00 -#define SPI_MODE1 0x04 -#define SPI_MODE2 0x08 -#define SPI_MODE3 0x0C - -#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR -#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR -#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR - - - -class SPIClass { -public: - static byte transfer(byte _data); - - // SPI Configuration methods - - inline static void attachInterrupt(); - inline static void detachInterrupt(); // Default - - static void begin(); // Default - static void end(); - - static void setBitOrder(uint8_t); - static void setDataMode(uint8_t); - static void setClockDivider(uint8_t); -}; -extern SPIClass SPI; - -#endif //ATTiny -#endif // __RF24_CONFIG_H__ - diff --git a/RF24/RPi/RF24/Makefile b/RF24/RPi/RF24/Makefile deleted file mode 100644 index c2ae37d..0000000 --- a/RF24/RPi/RF24/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -############################################################################# -# -# Makefile for librf24-bcm on Raspberry Pi -# -# License: GPL (General Public License) -# Author: Charles-Henri Hallard -# Date: 2013/03/13 -# -# Description: -# ------------ -# use make all and mak install to install the library -# You can change the install directory by editing the LIBDIR line -# -PREFIX=/usr/local - -# Library parameters -# where to put the lib -LIBDIR=$(PREFIX)/lib -# lib name -LIB=librf24-bcm -# shared library name -LIBNAME=$(LIB).so.1.0 - - -# The recommended compiler flags for the Raspberry Pi -CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s - -# make all -# reinstall the library after each recompilation -all: librf24-bcm install - -# Make the library -librf24-bcm: RF24.o bcm2835.o - g++ -shared -Wl,-soname,$@.so.1 ${CCFLAGS} -o ${LIBNAME} $^ - -# Library parts -RF24.o: RF24.cpp - g++ -Wall -fPIC ${CCFLAGS} -c $^ - -bcm2835.o: bcm2835.c - gcc -Wall -fPIC ${CCFLAGS} -c $^ - -# clear build files -clean: - rm -rf *.o ${LIB}.* - -# Install the library to LIBPATH -install: - @echo "[Install]" - @if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi - #@install -m 0755 ${LIB}.a ${LIBDIR} - @install -m 0755 ${LIBNAME} ${LIBDIR} - @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so.1 - @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so - @ldconfig diff --git a/RF24/RPi/RF24/RF24.cpp b/RF24/RPi/RF24/RF24.cpp deleted file mode 100644 index c45afaf..0000000 --- a/RF24/RPi/RF24/RF24.cpp +++ /dev/null @@ -1,1145 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - - - -03/17/2013 : Charles-Henri Hallard (http://hallard.me) - Modified to use with Arduipi board http://hallard.me/arduipi - Changed to use modified bcm2835 library - -TMRh20 2014: Updated to work with optimized RF24 and RF24 Network Arduino libs. -*/ - -#include "./RF24_config.h" -#include "./RF24.h" -#include "./nRF24L01.h" - - -/****************************************************************************/ -uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) -{ - uint8_t status; - uint8_t * prx = spi_rxbuff; - uint8_t * ptx = spi_txbuff; - uint8_t size = len + 1; // Add register value to transmit buffer - - *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); - while (len--){ - *ptx++ = NOP ; // Dummy operation, just for reading - } - bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); - //bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) buf, size); - - status = *prx++; // status is 1st byte of receive buffer - - // decrement before to skip status byte - while ( --size ){ - *buf++ = *prx++; - } - - return status; -} - -/****************************************************************************/ -uint8_t RF24::read_register(uint8_t reg) -{ - uint8_t result; - uint8_t * prx = spi_rxbuff; - uint8_t * ptx = spi_txbuff; - - *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); - *ptx++ = NOP ; // Dummy operation, just for reading - - bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); - - result = *++prx; // result is 2nd byte of receive buffer - - return result; -} - -/****************************************************************************/ - -uint8_t RF24::write_register(uint8_t reg, uint8_t value) -{ - uint8_t status; - uint8_t * prx = spi_rxbuff; - uint8_t * ptx = spi_txbuff; - - *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); - *ptx = value ; - - bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); - - status = *prx++; // status is 1st byte of receive buffer - - if (debug) - printf("write_register(%02x,%02x)\r\n",reg,value); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) -{ - uint8_t status; - uint8_t * prx = spi_rxbuff; - uint8_t * ptx = spi_txbuff; - uint8_t size = len + 1; // Add register value to transmit buffer - - *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); - while ( len-- ) - *ptx++ = *buf++; - - bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); - - status = *prx; // status is 1st byte of receive buffer - - return status; -} - - -/****************************************************************************/ - -uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeType) -{ - uint8_t status; - uint8_t * prx = spi_rxbuff; - uint8_t * ptx = spi_txbuff; - uint8_t size ; - - const uint8_t* current = reinterpret_cast(buf); - - uint8_t data_len = min(len,payload_size); - uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - - size = data_len + blank_len + 1 ; // Add register value to transmit buffer - - if (debug) - printf("[Writing %u bytes %u blanks]",data_len,blank_len); - - *ptx++ = W_TX_PAYLOAD; - while ( data_len-- ) - *ptx++ = *current++; - while ( blank_len-- ) - *ptx++ = 0; - - bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); - - status = *prx; // status is 1st byte of receive buffer - - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::read_payload(void* buf, uint8_t len) -{ - uint8_t status; - uint8_t * prx = spi_rxbuff; - uint8_t * ptx = spi_txbuff; - uint8_t size ; - - uint8_t* current = reinterpret_cast(buf); - - uint8_t data_len = min(len,payload_size); - uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; - - size = data_len + blank_len + 1; // Add register value to transmit buffer - - if (debug) - printf("[Reading %u bytes %u blanks]",data_len,blank_len); - - *ptx++ = R_RX_PAYLOAD; - while(size--) - *ptx++ = NOP; - - // Size has been lost during while, re affect - size = data_len + blank_len + 1; // Add register value to transmit buffer - - bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); - - // 1st byte is status - status = *prx++; - - // Decrement before to skip 1st status byte - while ( --size ) - *current++ = *prx++; - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::flush_rx(void) -{ - uint8_t status; - - status = bcm2835_spi_transfer( FLUSH_RX ); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::flush_tx(void) -{ - uint8_t status; - - status = bcm2835_spi_transfer( FLUSH_TX ); - - return status; -} - -/****************************************************************************/ - -uint8_t RF24::get_status(void) -{ - return bcm2835_spi_transfer( NOP ); -} - -/****************************************************************************/ - -void RF24::print_status(uint8_t status) -{ - printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n", - status, - (status & _BV(RX_DR))?1:0, - (status & _BV(TX_DS))?1:0, - (status & _BV(MAX_RT))?1:0, - ((status >> RX_P_NO) & 0b111), - (status & _BV(TX_FULL))?1:0 - ); -} - -/****************************************************************************/ - -void RF24::print_observe_tx(uint8_t value) -{ - printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n", - value, - (value >> PLOS_CNT) & 0b1111, - (value >> ARC_CNT) & 0b1111 - ); -} - -/****************************************************************************/ - -void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) -{ - char extra_tab = strlen(name) < 8 ? '\t' : 0; - printf("%s\t%c =", name, extra_tab); - while (qty--) - printf(" 0x%02x",read_register(reg++)); - printf("\n"); -} - -/****************************************************************************/ - -void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) -{ - char extra_tab = strlen(name) < 8 ? '\t' : 0; - printf("%s\t%c =",name,extra_tab); - - while (qty--) - { - uint8_t buffer[5]; - read_register(reg++,buffer,sizeof buffer); - - printf(" 0x"); - uint8_t* bufptr = buffer + sizeof buffer; - while( --bufptr >= buffer ) - printf("%02x",*bufptr); - } - - printf("\r\n"); -} - - -/****************************************************************************/ - -RF24::RF24(uint8_t _cepin, uint8_t _cspin, uint32_t _spi_speed): - ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), wide_band(true), p_variant(false), - payload_size(32), dynamic_payloads_enabled(false), - pipe0_reading_address(0) -{ -} - -/****************************************************************************/ - -void RF24::setChannel(uint8_t channel) -{ - // TODO: This method could take advantage of the 'wide_band' calculation - // done in setChannel() to require certain channel spacing. - - const uint8_t max_channel = 127; - write_register(RF_CH,min(channel,max_channel)); -} - -/****************************************************************************/ - -void RF24::setPayloadSize(uint8_t size) -{ - const uint8_t max_payload_size = 32; - payload_size = min(size,max_payload_size); -} - -/****************************************************************************/ - -uint8_t RF24::getPayloadSize(void) -{ - return payload_size; -} - -/****************************************************************************/ - -static const char rf24_datarate_e_str_0[] = "1MBPS"; -static const char rf24_datarate_e_str_1[] = "2MBPS"; -static const char rf24_datarate_e_str_2[] = "250KBPS"; -static const char * const rf24_datarate_e_str_P[] = { - rf24_datarate_e_str_0, - rf24_datarate_e_str_1, - rf24_datarate_e_str_2, -}; -static const char rf24_model_e_str_0[] = "nRF24L01"; -static const char rf24_model_e_str_1[] = "nRF24L01+"; -static const char * const rf24_model_e_str_P[] = { - rf24_model_e_str_0, - rf24_model_e_str_1, -}; -static const char rf24_crclength_e_str_0[] = "Disabled"; -static const char rf24_crclength_e_str_1[] = "8 bits"; -static const char rf24_crclength_e_str_2[] = "16 bits" ; -static const char * const rf24_crclength_e_str_P[] = { - rf24_crclength_e_str_0, - rf24_crclength_e_str_1, - rf24_crclength_e_str_2, -}; -static const char rf24_pa_dbm_e_str_0[] = "PA_MIN"; -static const char rf24_pa_dbm_e_str_1[] = "PA_LOW"; -static const char rf24_pa_dbm_e_str_2[] = "PA_HIGH"; -static const char rf24_pa_dbm_e_str_3[] = "PA_MAX"; -static const char * const rf24_pa_dbm_e_str_P[] = { - rf24_pa_dbm_e_str_0, - rf24_pa_dbm_e_str_1, - rf24_pa_dbm_e_str_2, - rf24_pa_dbm_e_str_3, -}; - -static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)"; -static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)"; -static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)"; -static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven"; -static const char * const rf24_csn_e_str_P[] = { - rf24_csn_e_str_0, - rf24_csn_e_str_1, - rf24_csn_e_str_2, - rf24_csn_e_str_3, -}; - -// Display NRF24L01 details -void RF24::printDetails(void) -{ - printf("================ SPI Configuration ================\n" ); - - if (csn_pin < BCM2835_SPI_CS_NONE ) - { - printf("CSN Pin \t = %s\n",rf24_csn_e_str_P[csn_pin]); - } - else - { - printf("CSN Pin \t = Custom GPIO%d%s\n", csn_pin, - csn_pin==RPI_V2_GPIO_P1_26 ? " (CE1) Software Driven" : "" ); - } - - printf("CE Pin \t = Custom GPIO%d\n", ce_pin ); - - // SPI Bus Speed - printf("Clock Speed\t = " ); - switch (spi_speed) - { - case BCM2835_SPI_SPEED_64MHZ : printf("64 Mhz"); break ; - case BCM2835_SPI_SPEED_32MHZ : printf("32 Mhz"); break ; - case BCM2835_SPI_SPEED_16MHZ : printf("16 Mhz"); break ; - case BCM2835_SPI_SPEED_8MHZ : printf("8 Mhz"); break ; - case BCM2835_SPI_SPEED_4MHZ : printf("4 Mhz"); break ; - case BCM2835_SPI_SPEED_2MHZ : printf("2 Mhz"); break ; - case BCM2835_SPI_SPEED_1MHZ : printf("1 Mhz"); break ; - case BCM2835_SPI_SPEED_512KHZ: printf("512 KHz"); break ; - case BCM2835_SPI_SPEED_256KHZ: printf("256 KHz"); break ; - case BCM2835_SPI_SPEED_128KHZ: printf("128 KHz"); break ; - case BCM2835_SPI_SPEED_64KHZ : printf("64 KHz"); break ; - case BCM2835_SPI_SPEED_32KHZ : printf("32 KHz"); break ; - case BCM2835_SPI_SPEED_16KHZ : printf("16 KHz"); break ; - case BCM2835_SPI_SPEED_8KHZ : printf("8 KHz"); break ; - default : printf("Probably Bad !!!"); break ; - } - printf("\n"); - - printf("================ NRF Configuration ================\n" ); - - print_status(get_status()); - - print_address_register("RX_ADDR_P0-1",RX_ADDR_P0,2); - print_byte_register("RX_ADDR_P2-5",RX_ADDR_P2,4); - print_address_register("TX_ADDR",TX_ADDR); - - print_byte_register("RX_PW_P0-6",RX_PW_P0,6); - print_byte_register("EN_AA",EN_AA); - print_byte_register("EN_RXADDR",EN_RXADDR); - print_byte_register("RF_CH",RF_CH); - print_byte_register("RF_SETUP",RF_SETUP); - print_byte_register("CONFIG",CONFIG); - print_byte_register("DYNPD/FEATURE",DYNPD,2); - - printf("Data Rate\t = %s\r\n",rf24_datarate_e_str_P[getDataRate()]); - printf("Model\t\t = %s\r\n",rf24_model_e_str_P[isPVariant()]); - printf("CRC Length\t = %s\r\n",rf24_crclength_e_str_P[getCRCLength()]); - printf("PA Power\t = %s\r\n",rf24_pa_dbm_e_str_P[getPALevel()]); -} - -/****************************************************************************/ - -bool RF24::begin(void) -{ - debug = false; - //debug = true; - // This initialize the SPI bus with - // csn pin as chip select (custom or not) - - - // Init BCM2835 chipset for talking with us - if (!bcm2835_init()){ - return false; - } - - switch(csn_pin){ //Ensure valid hardware CS pin - case 0: break; - case 1: break; - case 8: csn_pin = 0; break; - case 7: csn_pin = 1; break; - default: csn_pin = 0; break; - } - - - bcm2835_spi_begin(); - - // used to drive custom I/O to trigger my logic analyser - // bcm2835_gpio_fsel(GPIO_CTRL_PIN , BCM2835_GPIO_FSEL_OUTP); - - // start the SPI library: - // Note the NRF24 wants mode 0, MSB first and default to 1 Mbps - bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); - bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); - - // Set SPI bus Speed - bcm2835_spi_setClockDivider(spi_speed); - - // Choose hardware CSN pin - bcm2835_spi_chipSelect(csn_pin); - - - // Initialise the CE pin of NRF24 (chip enable) after the CSN pin, so that - // The input mode is not changed if using one of the hardware CE pins - bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP); - bcm2835_gpio_write(ce_pin, LOW); - - // wait 100ms - delay(100); - - // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier - // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet - // sizes must never be used. See documentation for a more complete explanation. - //printf("write_register(%02X, %02X)\n", SETUP_RETR, (0b0100 << ARD) | (0b1111 << ARC)); - setRetries(5,15); - - // Determine if this is a p or non-p RF24 module and then - // reset our data rate back to default value. This works - // because a non-P variant won't allow the data rate to - // be set to 250Kbps. - if( setDataRate( RF24_250KBPS ) ) - { - p_variant = true ; - } - - // Then set the data rate to the slowest (and most reliable) speed supported by all - // hardware. - setDataRate( RF24_1MBPS ) ; - - // Initialize CRC and request 2-byte (16bit) CRC - setCRCLength( RF24_CRC_16 ) ; - - // Reset current status - // Notice reset and flush is the last thing we do - write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Set up default configuration. Callers can always change it later. - // This channel should be universally safe and not bleed over into adjacent - // spectrum. - setChannel(76); - - // Flush buffers - flush_rx(); - flush_tx(); - - powerUp(); - - // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) - // PTX should use only 22uA of power - write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); - - return true; -} - -/****************************************************************************/ - -void RF24::startListening(void) -{ - powerUp(); - write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); - write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Restore the pipe0 adddress, if exists - if (pipe0_reading_address) - write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); - - // Flush buffers - flush_rx(); - flush_tx(); - - // Go! - bcm2835_gpio_write(ce_pin, HIGH); - - // wait for the radio to come up (130us actually only needed) - //delayMicroseconds(130); -} - -/****************************************************************************/ - -void RF24::stopListening(void) -{ - bcm2835_gpio_write(ce_pin, LOW); - flush_tx(); - flush_rx(); - write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); - -} - -/****************************************************************************/ - -void RF24::powerDown(void) -{ - bcm2835_gpio_write(ce_pin, LOW); - write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); -} - -/****************************************************************************/ - -void RF24::powerUp(void) -{ - bool up = read_register(CONFIG) & _BV(PWR_UP); - - if(! up ){ - write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) )); - delay(5); - } - - -} - -/******************************************************************/ - -bool RF24::write( const void* buf, uint8_t len, const bool multicast ){ - - // Begin the write - startFastWrite(buf,len, multicast); - - - //Wait until complete or failed - // If this hangs, it ain't coming back, no sense in timing out - while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } - - - uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - bcm2835_gpio_write(ce_pin, LOW); - - //Max retries exceeded - if( status & _BV(MAX_RT)){ - flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush - return 0; - } - //TX OK 1 or 0 - return 1; -} - -bool RF24::write( const void* buf, uint8_t len ){ - return write(buf,len,0); -} - -/****************************************************************************/ - -//For general use, the interrupt flags are not important to clear -bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) -{ - //Block until the FIFO is NOT full. - //Keep track of the MAX retries and set auto-retry if seeing failures - //This way the FIFO will fill up and allow blocking until packets go through - //The radio will auto-clear everything in the FIFO as long as CE remains high - - uint32_t timer = millis(); //Get the time that the payload transmission started - - while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout - - if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached - reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag - if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 - } - - } - - //Start Writing - startFastWrite(buf,len,0); //Write the payload if a buffer is clear - - return 1; //Return 1 to indicate successful transmission -} - -/****************************************************************************/ - -void RF24::reUseTX(){ - write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag - //spiTrans( REUSE_TX_PL ); - bcm2835_spi_transfer( REUSE_TX_PL); - bcm2835_gpio_write(ce_pin, LOW); //Re-Transfer packet - bcm2835_gpio_write(ce_pin, HIGH); -} - -/****************************************************************************/ - -bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) -{ - //Block until the FIFO is NOT full. - //Keep track of the MAX retries and set auto-retry if seeing failures - //Return 0 so the user can control the retrys and set a timer or failure counter if required - //The radio will auto-clear everything in the FIFO as long as CE remains high - - while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail - - if( get_status() & _BV(MAX_RT)){ - //reUseTX(); //Set re-transmit - write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag - return 0; //Return 0. The previous payload has been retransmitted - //From the user perspective, if you get a 0, just keep trying to send the same payload - } - - } - //Start Writing - startFastWrite(buf,len,multicast); - - return 1; -} - -bool RF24::writeFast( const void* buf, uint8_t len ){ - return writeFast(buf,len,0); -} - -/****************************************************************************/ - -//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high -//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) -//Otherwise we enter Standby-II mode, which is still faster than standby mode -//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data - -void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 - - //write_payload( buf,len); - write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; - bcm2835_gpio_write(ce_pin, HIGH); - -} - -/****************************************************************************/ - -void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ) -{ - - // Send the payload - - write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; - bcm2835_gpio_write(ce_pin, HIGH); - delayMicroseconds(10); - bcm2835_gpio_write(ce_pin, LOW); -} - -/****************************************************************************/ - -bool RF24::txStandBy(){ - - while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ - if( get_status() & _BV(MAX_RT)){ - write_register(STATUS,_BV(MAX_RT) ); - bcm2835_gpio_write(ce_pin, LOW); - flush_tx(); //Non blocking, flush the data - return 0; - } - } - - bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode - return 1; -} - -/****************************************************************************/ - -bool RF24::txStandBy(uint32_t timeout){ - - uint32_t start = millis(); - - while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ - if( get_status() & _BV(MAX_RT)){ - write_register(STATUS,_BV(MAX_RT) ); - bcm2835_gpio_write(ce_pin, LOW); //Set re-transmit - bcm2835_gpio_write(ce_pin, HIGH); - if(millis() - start >= timeout){ - bcm2835_gpio_write(ce_pin, LOW); - flush_tx(); - return 0; - } - } - } - bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode - return 1; - -} -/****************************************************************************/ - -void RF24::maskIRQ(bool tx, bool fail, bool rx){ - - write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); -} - -/****************************************************************************/ - -uint8_t RF24::getDynamicPayloadSize(void) -{ - spi_txbuff[0] = R_RX_PL_WID; - spi_rxbuff[1] = 0xff; - - bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); - - if(spi_rxbuff[1] > 32) { flush_rx(); return 0; } - - return spi_rxbuff[1]; -} - -/****************************************************************************/ - -bool RF24::available(void) -{ - return available(NULL); -} - -/****************************************************************************/ - -bool RF24::available(uint8_t* pipe_num) -{ - //Check the FIFO buffer to see if data is waitng to be read - - if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ - - - // If the caller wants the pipe number, include that - if ( pipe_num ){ - uint8_t status = get_status(); - *pipe_num = ( status >> RX_P_NO ) & 0b111; - } - return 1; - } - - return 0; -} - -/****************************************************************************/ - -void RF24::read( void* buf, uint8_t len ) -{ - // Fetch the payload - read_payload( buf, len ); - - //Clear the two possible interrupt flags with one command - write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); -} - -/****************************************************************************/ - -void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) -{ - // Read the status & reset the status in one easy call - // Or is that such a good idea? - uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - - // Report to the user what happened - tx_ok = status & _BV(TX_DS); - tx_fail = status & _BV(MAX_RT); - rx_ready = status & _BV(RX_DR); -} - -/****************************************************************************/ - -void RF24::openWritingPipe(uint64_t value) -{ - // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) - // expects it LSB first too, so we're good. - - write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); - write_register(TX_ADDR, reinterpret_cast(&value), 5); - - - write_register(RX_PW_P0,payload_size); -} - -/****************************************************************************/ - -static const uint8_t child_pipe[] = -{ - RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 -}; -static const uint8_t child_payload_size[] = -{ - RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 -}; -static const uint8_t child_pipe_enable[] = -{ - ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 -}; - -void RF24::openReadingPipe(uint8_t child, uint64_t address) -{ - // If this is pipe 0, cache the address. This is needed because - // openWritingPipe() will overwrite the pipe 0 address, so - // startListening() will have to restore it. - if (child == 0) - pipe0_reading_address = address; - - if (child <= 6) - { - // For pipes 2-5, only write the LSB - if ( child < 2 ) - write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); - else - write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); - - write_register(pgm_read_byte(&child_payload_size[child]),payload_size); - - // Note it would be more efficient to set all of the bits for all open - // pipes at once. However, I thought it would make the calling code - // more simple to do it this way. - write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); - } -} - -/****************************************************************************/ - -void RF24::toggle_features(void) -{ - bcm2835_spi_transfer( ACTIVATE ); - bcm2835_spi_transfer( 0x73 ); -} - -/****************************************************************************/ - -void RF24::enableDynamicPayloads(void) -{ - - // So enable them and try again - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); - - if (debug) - printf("FEATURE=%i\r\n",read_register(FEATURE)); - - // Enable dynamic payload on all pipes - // - // Not sure the use case of only having dynamic payload on certain - // pipes, so the library does not support it. - write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); - - dynamic_payloads_enabled = true; -} - -/****************************************************************************/ - -void RF24::enableAckPayload(void) -{ - // - // enable ack payload and dynamic payload features - // - - - // So enable them and try again - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); - - if (debug) - printf("FEATURE=%i\r\n",read_register(FEATURE)); - - // - // Enable dynamic payload on pipes 0 & 1 - // - - write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); -} - -/****************************************************************************/ - -void RF24::enableDynamicAck(void){ - // - // enable dynamic ack features - // - toggle_features(); - write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); - - if(debug){printf("FEATURE=%i\r\n",read_register(FEATURE));} - - -} - -/****************************************************************************/ - -void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) -{ - - uint8_t * ptx = spi_txbuff; - uint8_t size ; - - const uint8_t* current = reinterpret_cast(buf); - - uint8_t data_len = min(len,payload_size); - - size = data_len + 1 ; // Add register value to transmit buffer - - if (debug){ - printf("[Writing %u bytes]",data_len); - } - *ptx++ = W_ACK_PAYLOAD | ( pipe & 0b111 ); - while ( data_len-- ){ - *ptx++ = *current++; - } - bcm2835_spi_transfern( (char *) spi_txbuff, size); - -} - - -/****************************************************************************/ - -bool RF24::isAckPayloadAvailable(void) -{ - return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); -} - -/****************************************************************************/ - -bool RF24::isPVariant(void) -{ - return p_variant ; -} - -/****************************************************************************/ - -void RF24::setAutoAck(bool enable) -{ - if ( enable ) - write_register(EN_AA, 0b111111); - else - write_register(EN_AA, 0); -} - -/****************************************************************************/ - -void RF24::setAutoAck( uint8_t pipe, bool enable ) -{ - if ( pipe <= 6 ) - { - uint8_t en_aa = read_register( EN_AA ) ; - if( enable ) - { - en_aa |= _BV(pipe) ; - } - else - { - en_aa &= ~_BV(pipe) ; - } - write_register( EN_AA, en_aa ) ; - } -} - -/****************************************************************************/ - -bool RF24::testCarrier(void) -{ - return ( read_register(CD) & 1 ); -} - -/****************************************************************************/ - -bool RF24::testRPD(void) -{ - return ( read_register(RPD) & 1 ) ; -} - -/****************************************************************************/ - -void RF24::setPALevel(uint8_t level) -{ - uint8_t setup = read_register(RF_SETUP) & 0b11111000; - - if(level > 3){ // If invalid level, go to max PA - level = (RF24_PA_MAX << 1) + 1; // +1 to support the SI24R1 chip extra bit - }else{ - level = (level << 1) + 1; // Else set level as requested - } - - - write_register( RF_SETUP, setup |= level ) ; // Write it to the chip -} - -/****************************************************************************/ - -uint8_t RF24::getPALevel(void) -{ - return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; -} - -/****************************************************************************/ - -bool RF24::setDataRate(rf24_datarate_e speed) -{ - bool result = false; - uint8_t setup = read_register(RF_SETUP) ; - - // HIGH and LOW '00' is 1Mbs - our default - wide_band = false ; - setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; - if( speed == RF24_250KBPS ) - { - // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 - // Making it '10'. - wide_band = false ; - setup |= _BV( RF_DR_LOW ) ; - } - else - { - // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 - // Making it '01' - if ( speed == RF24_2MBPS ) - { - wide_band = true ; - setup |= _BV(RF_DR_HIGH); - } - else - { - // 1Mbs - wide_band = false ; - } - } - write_register(RF_SETUP,setup); - - // Verify our result - if ( read_register(RF_SETUP) == setup ) - { - result = true; - } - else - { - wide_band = false; - } - - return result; -} - -/****************************************************************************/ - -rf24_datarate_e RF24::getDataRate( void ) -{ - rf24_datarate_e result ; - uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); - - // switch uses RAM (evil!) - // Order matters in our case below - if ( dr == _BV(RF_DR_LOW) ) - { - // '10' = 250KBPS - result = RF24_250KBPS ; - } - else if ( dr == _BV(RF_DR_HIGH) ) - { - // '01' = 2MBPS - result = RF24_2MBPS ; - } - else - { - // '00' = 1MBPS - result = RF24_1MBPS ; - } - return result ; -} - -/****************************************************************************/ - -void RF24::setCRCLength(rf24_crclength_e length) -{ - uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; - - // switch uses RAM (evil!) - if ( length == RF24_CRC_DISABLED ) - { - // Do nothing, we turned it off above. - } - else if ( length == RF24_CRC_8 ) - { - config |= _BV(EN_CRC); - } - else - { - config |= _BV(EN_CRC); - config |= _BV( CRCO ); - } - write_register( CONFIG, config ) ; -} - -/****************************************************************************/ - -rf24_crclength_e RF24::getCRCLength(void) -{ - rf24_crclength_e result = RF24_CRC_DISABLED; - uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; - - if ( config & _BV(EN_CRC ) ) - { - if ( config & _BV(CRCO) ) - result = RF24_CRC_16; - else - result = RF24_CRC_8; - } - - return result; -} - -/****************************************************************************/ - -void RF24::disableCRC( void ) -{ - uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; - write_register( CONFIG, disable ) ; -} - -/****************************************************************************/ -void RF24::setRetries(uint8_t delay, uint8_t count) -{ - write_register(SETUP_RETR,(delay&0xf)< - - 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. - - 03/17/2013 : Charles-Henri Hallard (http://hallard.me) - Modified to use with Arduipi board http://hallard.me/arduipi - Modified to use the great bcm2835 library for I/O and SPI - - */ - -/** - * @file RF24.h - * - * Class declaration for RF24 and helper enums - */ - -#ifndef __RF24_H__ -#define __RF24_H__ - -#include "RF24_config.h" -#include "./bcm2835.h" - - -/** - * Power Amplifier level. - * - * For use with setPALevel() - */ -typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; - -/** - * Data rate. How fast data moves through the air. - * - * For use with setDataRate() - */ -typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; - -/** - * CRC Length. How big (if any) of a CRC is included. - * - * For use with setCRCLength() - */ -typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; - -/** - * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver - */ - -class RF24 -{ -private: - uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ - uint8_t csn_pin; /**< SPI Chip select */ - uint16_t spi_speed; /**< SPI Bus Speed */ - bool wide_band; /* 2Mbs data rate in use? */ - bool p_variant; /* False for RF24L01 and true for RF24L01P */ - uint8_t payload_size; /**< Fixed size of payloads */ - //bool ack_payload_available; /**< Whether there is an ack payload waiting */ - bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ - //uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ - uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ - //uint32_t spispeed; - uint8_t debug ; /* Debug flag */ - uint8_t spi_rxbuff[32+1] ; //SPI receive buffer (payload max 32 bytes) - uint8_t spi_txbuff[32+1] ; //SPI transmit buffer (payload max 32 bytes + 1 byte for the command) - -protected: - /** - * @name Low-level internal interface. - * - * Protected methods that address the chip directly. Regular users cannot - * ever call these. They are documented for completeness and for developers who - * may want to extend this class. - */ - /**@{*/ - - - /** - * Read a chunk of data in from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to put the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); - - /** - * Read single byte from a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @return Current value of register @p reg - */ - uint8_t read_register(uint8_t reg); - - /** - * Write a chunk of data to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param buf Where to get the data - * @param len How many bytes of data to transfer - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); - - /** - * Write a single byte to a register - * - * @param reg Which register. Use constants from nRF24L01.h - * @param value The new value to write - * @return Current value of status register - */ - uint8_t write_register(uint8_t reg, uint8_t value); - - /** - * Write the transmit payload - * - * The size of data written is the fixed payload size, see getPayloadSize() - * - * @param buf Where to get the data - * @param len Number of bytes to be sent - * @return Current value of status register - */ - uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); - - /** - * Read the receive payload - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @param buf Where to put the data - * @param len Maximum number of bytes to read - * @return Current value of status register - */ - uint8_t read_payload(void* buf, uint8_t len); - - /** - * Empty the receive buffer - * - * @return Current value of status register - */ - uint8_t flush_rx(void); - - /** - * Empty the transmit buffer - * - * @return Current value of status register - */ - uint8_t flush_tx(void); - - /** - * Retrieve the current status of the chip - * - * @return Current value of status register - */ - uint8_t get_status(void); - - /** - * Decode and print the given status to stdout - * - * @param status Status value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_status(uint8_t status); - - /** - * Decode and print the given 'observe_tx' value to stdout - * - * @param value The observe_tx value to print - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void print_observe_tx(uint8_t value); - - /** - * Print the name and value of an 8-bit register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Print the name and value of a 40-bit address register to stdout - * - * Optionally it can print some quantity of successive - * registers on the same line. This is useful for printing a group - * of related registers on one line. - * - * @param name Name of the register - * @param reg Which register. Use constants from nRF24L01.h - * @param qty How many successive registers to print - */ - void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); - - /** - * Turn on or off the special features of the chip - * - * The chip has certain 'features' which are only available when the 'features' - * are enabled. See the datasheet for details. - */ - void toggle_features(void); - /**@}*/ - -public: - /** - * @name Primary public interface - * - * These are the main methods you need to operate the chip - */ - /**@{*/ - - /** - * Constructor - * - * Creates a new instance of this driver. Before using, you create an instance - * and send in the unique pins that this chip is connected to. - * - * @param _cepin The pin attached to Chip Enable on the RF module - * @param _cspin The pin attached to Chip Select - */ - RF24(uint8_t _cepin, uint8_t _cspin); - RF24(uint8_t _cepin, uint8_t _cspin, uint32_t spispeed ); - - /** - * Begin operation of the chip - * - * Call this in setup(), before calling any other methods. - */ - bool begin(void); - - /** - * Start listening on the pipes opened for reading. - * - * Be sure to call openReadingPipe() first. Do not call write() while - * in this mode, without first calling stopListening(). Call - * isAvailable() to check for incoming traffic, and read() to get it. - */ - void startListening(void); - - /** - * Stop listening for incoming messages - * - * Do this before calling write(). - */ - void stopListening(void); - - /** - * Write to the open writing pipe - * - * Be sure to call openWritingPipe() first to set the destination - * of where to write to. - * - * This blocks until the message is successfully acknowledged by - * the receiver or the timeout/retransmit maxima are reached. In - * the current configuration, the max delay here is 60ms. - * - * The maximum size of data written is the fixed payload size, see - * getPayloadSize(). However, you can write less, and the remainder - * will just be filled with zeroes. - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - bool write( const void* buf, uint8_t len ); - - /** - * Test whether there are bytes available to be read - * - * @return True if there is a payload available, false if none is - */ - bool available(void); - - /** - * Read the payload - * - * Return the last payload received - * - * The size of data read is the fixed payload size, see getPayloadSize() - * - * @note I specifically chose 'void*' as a data type to make it easier - * for beginners to use. No casting needed. - * - * @param buf Pointer to a buffer where the data should be written - * @param len Maximum number of bytes to read into the buffer - * @return True if the payload was delivered successfully false if not - */ - void read( void* buf, uint8_t len ); - - /** - * Open a pipe for writing - * - * Only one pipe can be open at once, but you can change the pipe - * you'll listen to. Do not call this while actively listening. - * Remember to stopListening() first. - * - * Addresses are 40-bit hex values, e.g.: - * - * @code - * openWritingPipe(0xF0F0F0F0F0); - * @endcode - * - * @param address The 40-bit address of the pipe to open. This can be - * any value whatsoever, as long as you are the only one writing to it - * and only one other radio is listening to it. Coordinate these pipe - * addresses amongst nodes on the network. - */ - void openWritingPipe(uint64_t address); - - /** - * Open a pipe for reading - * - * Up to 6 pipes can be open for reading at once. Open all the - * reading pipes, and then call startListening(). - * - * @see openWritingPipe - * - * @warning Pipes 1-5 should share the first 32 bits. - * Only the least significant byte should be unique, e.g. - * @code - * openReadingPipe(1,0xF0F0F0F0AA); - * openReadingPipe(2,0xF0F0F0F066); - * @endcode - * - * @warning Pipe 0 is also used by the writing pipe. So if you open - * pipe 0 for reading, and then startListening(), it will overwrite the - * writing pipe. Ergo, do an openWritingPipe() again before write(). - * - * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits - * - * @param number Which pipe# to open, 0-5. - * @param address The 40-bit address of the pipe to open. - */ - void openReadingPipe(uint8_t number, uint64_t address); - - /**@}*/ - /** - * @name Optional Configurators - * - * Methods you can use to get or set the configuration of the chip. - * None are required. Calling begin() sets up a reasonable set of - * defaults. - */ - /**@{*/ - /** - * Set the number and delay of retries upon failed submit - * - * @param delay How long to wait between each retry, in multiples of 250us, - * max is 15. 0 means 250us, 15 means 4000us. - * @param count How many retries before giving up, max 15 - */ - void setRetries(uint8_t delay, uint8_t count); - - /** - * Set RF communication channel - * - * @param channel Which RF channel to communicate on, 0-127 - */ - void setChannel(uint8_t channel); - - /** - * Set Static Payload Size - * - * This implementation uses a pre-stablished fixed payload size for all - * transmissions. If this method is never called, the driver will always - * transmit the maximum payload size (32 bytes), no matter how much - * was sent to write(). - * - * @todo Implement variable-sized payloads feature - * - * @param size The number of bytes in the payload - */ - void setPayloadSize(uint8_t size); - - /** - * Get Static Payload Size - * - * @see setPayloadSize() - * - * @return The number of bytes in the payload - */ - uint8_t getPayloadSize(void); - - /** - * Get Dynamic Payload Size - * - * For dynamic payloads, this pulls the size of the payload off - * the chip - * - * @return Payload length of last-received dynamic payload - */ - uint8_t getDynamicPayloadSize(void); - - /** - * Enable custom payloads on the acknowledge packets - * - * Ack payloads are a handy way to return data back to senders without - * manually changing the radio modes on both units. - * - * @see examples/pingpair_pl/pingpair_pl.pde - */ - void enableAckPayload(void); - - /** - * Enable dynamically-sized payloads - * - * This way you don't always have to send large packets just to send them - * once in a while. This enables dynamic payloads on ALL pipes. - * - * @see examples/pingpair_pl/pingpair_dyn.pde - */ - void enableDynamicPayloads(void); - - /** - * Determine whether the hardware is an nRF24L01+ or not. - * - * @return true if the hardware is nRF24L01+ (or compatible) and false - * if its not. - */ - bool isPVariant(void) ; - - /** - * Enable or disable auto-acknowlede packets - * - * This is enabled by default, so it's only needed if you want to turn - * it off for some reason. - * - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck(bool enable); - - /** - * Enable or disable auto-acknowlede packets on a per pipeline basis. - * - * AA is enabled by default, so it's only needed if you want to turn - * it off/on for some reason on a per pipeline basis. - * - * @param pipe Which pipeline to modify - * @param enable Whether to enable (true) or disable (false) auto-acks - */ - void setAutoAck( uint8_t pipe, bool enable ) ; - - /** - * Set Power Amplifier (PA) level to one of four levels. - * Relative mnemonics have been used to allow for future PA level - * changes. According to 6.5 of the nRF24L01+ specification sheet, - * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, - * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. - * - * @param level Desired PA level. - */ - void setPALevel( uint8_t level ) ; - - /** - * Fetches the current PA level. - * - * @return Returns a value from the rf24_pa_dbm_e enum describing - * the current PA setting. Please remember, all values represented - * by the enum mnemonics are negative dBm. See setPALevel for - * return value descriptions. - */ - uint8_t getPALevel( void ) ; - - /** - * Set the transmission data rate - * - * @warning setting RF24_250KBPS will fail for non-plus units - * - * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps - * @return true if the change was successful - */ - bool setDataRate(rf24_datarate_e speed); - - /** - * Fetches the transmission data rate - * - * @return Returns the hardware's currently configured datarate. The value - * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the - * rf24_datarate_e enum. - */ - rf24_datarate_e getDataRate( void ) ; - - /** - * Set the CRC length - * - * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - void setCRCLength(rf24_crclength_e length); - - /** - * Get the CRC length - * - * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit - */ - rf24_crclength_e getCRCLength(void); - - /** - * Disable CRC validation - * - */ - void disableCRC( void ) ; - - /**@}*/ - /** - * @name Advanced Operation - * - * Methods you can use to drive the chip in more advanced ways - */ - /**@{*/ - - /** - * Print a giant block of debugging information to stdout - * - * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h - */ - void printDetails(void); - - /** - * Enter low-power mode - * - * To return to normal power mode, either write() some data or - * startListening, or powerUp(). - */ - void powerDown(void); - - /** - * Leave low-power mode - making radio more responsive - * - * To return to low power mode, call powerDown(). - */ - void powerUp(void) ; - -/** - * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. - * - * @note enableDynamicAck() must be called to enable this feature - * - * Can be used with enableAckPayload() to request a response - * @see enableDynamicAck() - * @see setAutoAck() - * @see write() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0), NOACK (1) - */ - bool write( const void* buf, uint8_t len, const bool multicast ); - - /** - * @note Optimization: New Command * - * This will not block until the 3 FIFO buffers are filled with data. - * Once the FIFOs are full, writeFast will simply wait for success or - * timeout, and return 1 or 0 respectively. From a user perspective, just - * keep trying to send the same data. The library will keep auto retrying - * the current payload using the built in functionality. - * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto - * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO - * to clear by issuing txStandBy() or ensure appropriate time between transmissions. - * - * ONLY max retry interrupt flags will be cleared when writeFast is called - * - * @code - * Example (Partial blocking): - * - * radio.writeFast(&buf,32); // Writes 1 payload to the buffers - * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. - * - * radio.writeFast(&buf,32); // Writes 1 payload to the buffers - * txStandBy(1000); // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0. - * @endcode - * - * @see txStandBy() - * @see write() - * @see writeBlocking() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @return True if the payload was delivered successfully false if not - */ - bool writeFast( const void* buf, uint8_t len ); - - /** - * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. - * - * @note enableDynamicAck() must be called to enable this feature - * @see enableDynamicAck() - * @see setAutoAck() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0) or NOACK (1) - */ - bool writeFast( const void* buf, uint8_t len, const bool multicast ); - - /** - * @note Optimization: New Command - * This function extends the auto-retry mechanism to any specified duration. - * It will not block until the 3 FIFO buffers are filled with data. - * If so the library will auto retry until a new payload is written - * or the user specified timeout period is reached. - * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto - * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO - * to clear by issuing txStandBy() or ensure appropriate time between transmissions. - * - * ONLY max retry interrupt flags will be cleared when writeBlocking is called - * @code - * Example (Full blocking): - * - * radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers - * txStandBy(1000); //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed. - * //Blocks only until user timeout or success. Data flushed on fail. - * @endcode - * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). - * @see txStandBy() - * @see write() - * @see writeFast() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param timeout User defined timeout in milliseconds. - * @return True if the payload was loaded into the buffer successfully false if not - */ - bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); - - /** - * @note Optimization: New Command - * This function should be called as soon as transmission is finished to - * drop the radio back to STANDBY-I mode. If not issued, the radio will - * remain in STANDBY-II mode which, per the data sheet, is not a recommended - * operating mode. - * - * @note When transmitting data in rapid succession, it is still recommended by - * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is - * time enough between sends for the FIFOs to empty. - * - * Relies on built-in auto retry functionality. - * - * @code - * Example (Partial blocking): - * - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); //Fills the FIFO buffers up - * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. - * //Blocks only until MAX_RT timeout or success. Data flushed on fail. - * @endcode - * @see txStandBy(unsigned long timeout) - * @return True if transmission is successful - * - */ - bool txStandBy(); - - /** - * @note Optimization: New Command - * - * This function allows extended blocking and auto-retries per a user defined timeout - * @code - * Fully Blocking Example: - * - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); - * radio.writeFast(&buf,32); //Fills the FIFO buffers up - * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. - * //Blocks only until user defined timeout or success. Data flushed on fail. - * @endcode - * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). - * @param timeout Number of milliseconds to retry failed payloads - * @return True if transmission is successful - * - */ - bool txStandBy(uint32_t timeout); - - /** - * Test whether there are bytes available to be read - * - * Use this version to discover on which pipe the message - * arrived. - * - * @param[out] pipe_num Which pipe has the payload available - * @return True if there is a payload available, false if none is - */ - bool available(uint8_t* pipe_num); - -/** - * Non-blocking write to the open writing pipe used for buffered writes - * - * @note Optimization: This function now leaves the CE pin high, so the radio - * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. - * This allows the chip to be used to its full potential in TX mode. - * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto - * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO - * to clear by issuing txStandBy() or ensure appropriate time between transmissions. - * - * @see write() - * @see writeFast() - * @see startWrite() - * @see writeBlocking() - * - * For single noAck writes see: - * @see enableDynamicAck() - * @see setAutoAck() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0) or NOACK (1) - * @return True if the payload was delivered successfully false if not - */ - void startFastWrite( const void* buf, uint8_t len, const bool multicast ); - - /** - * Non-blocking write to the open writing pipe - * - * Just like write(), but it returns immediately. To find out what happened - * to the send, catch the IRQ and then call whatHappened(). - * - * @note Optimization: This function again behaves as it did previously for backwards-compatibility. - * with user code. The library uses startFastWrite() internally. - * This is mainly used for single-payload transactions. - * - * @see write() - * @see writeFast() - * @see startFastWrite() - * @see whatHappened() - * - * For single noAck writes see: - * @see enableDynamicAck() - * @see setAutoAck() - * - * @param buf Pointer to the data to be sent - * @param len Number of bytes to be sent - * @param multicast Request ACK (0) or NOACK (1) - * - */ - void startWrite( const void* buf, uint8_t len, const bool multicast ); - - /** - * Optimization: New Command - * - * This function is mainly used internally to take advantage of the auto payload - * re-use functionality of the chip, but can be beneficial to users as well. - * - * The function will instruct the radio to re-use the data in the FIFO buffers, - * and instructs the radio to re-send once the timeout limit has been reached. - * Used by writeFast and writeBlocking to initiate retries when a TX failure - * occurs. Retries are automatically initiated except with the standard write(). - * This way, data is not flushed from the buffer until switching between modes. - * - * @note This is to be used AFTER auto-retry fails if wanting to resend - * using the built-in payload reuse features. - * After issuing reUseTX(), it will keep reending the same payload forever or until - * a payload is written to the FIFO, or a flush_tx command is given. - */ - void reUseTX(); - - /** - * Write an ack payload for the specified pipe - * - * The next time a message is received on @p pipe, the data in @p buf will - * be sent back in the acknowledgement. - * - * @warning According to the data sheet, only three of these can be pending - * at any time. I have not tested this. - * - * @param pipe Which pipe# (typically 1-5) will get this response. - * @param buf Pointer to data that is sent - * @param len Length of the data to send, up to 32 bytes max. Not affected - * by the static payload set by setPayloadSize(). - */ - void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); - - /** - * Enable dynamic ACKs (single write multicasting) for chosen messages - * - * @note To enable full multicasting or per-pipe multicast, use setAutoAck() - * - * @warning This MUST be called prior to attempting single write NOACK calls - * @code - * radio.enableDynamicAck(); - * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested - * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK - * @endcode - */ - void enableDynamicAck(); - - /** - * Determine if an ack payload was received in the most recent call to - * write(). - * - * Call read() to retrieve the ack payload. - * - * @warning Calling this function clears the internal flag which indicates - * a payload is available. If it returns true, you must read the packet - * out as the very next interaction with the radio, or the results are - * undefined. - * - * @return True if an ack payload is available. - */ - bool isAckPayloadAvailable(void); - - /** - * Call this when you get an interrupt to find out why - * - * Tells you what caused the interrupt, and clears the state of - * interrupts. - * - * @param[out] tx_ok The send was successful (TX_DS) - * @param[out] tx_fail The send failed, too many retries (MAX_RT) - * @param[out] rx_ready There is a message waiting to be read (RX_DS) - */ - void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); - - /** - * Test whether there was a carrier on the line for the - * previous listening period. - * - * Useful to check for interference on the current channel. - * - * @return true if was carrier, false if not - */ - bool testCarrier(void); - - /** - * Test whether a signal (carrier or otherwise) greater than - * or equal to -64dBm is present on the channel. Valid only - * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). - * - * Useful to check for interference on the current channel and - * channel hopping strategies. - * - * @return true if signal => -64dBm, false if not - */ - bool testRPD(void) ; - - /** - * Test whether this is a real radio, or a mock shim for - * debugging. Setting either pin to 0xff is the way to - * indicate that this is not a real radio. - * - * @return true if this is a legitimate radio - */ - bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } - - /** - * The radio will generate interrupt signals when a transmission is complete, - * a transmission fails, or a payload is received. This allows users to mask - * those interrupts to prevent them from generating a signal on the interrupt - * pin. - * - * @code - * Mask all interrupts except the receive interrupt: - * - * radio.maskIRQ(1,1,0); - * @endcode - * - * @param tx_ok Mask transmission complete interrupts - * @param tx_fail Mask transmit failure interrupts - * @param rx_ready Mask payload received interrupts - */ - void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); - - /**@}*/ -}; - -/** - * @example GettingStarted.pde - * - * This is an example which corresponds to my "Getting Started" blog post: - * Getting Started with nRF24L01+ on Arduino. - * - * It is an example of how to use the RF24 class. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting - * with the serial monitor and sending a 'T'. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. - */ - -/** - * @example nordic_fob.pde - * - * This is an example of how to use the RF24 class to receive signals from the - * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . - * Thanks to Kirk Mower for providing test hardware. - */ - -/** - * @example led_remote.pde - * - * This is an example of how to use the RF24 class to control a remote - * bank of LED's using buttons on a remote control. - * - * Every time the buttons change on the remote, the entire state of - * buttons is send to the led board, which displays the state. - */ - -/** - * @example pingpair.pde - * - * This is an example of how to use the RF24 class. Write this sketch to two - * different nodes, connect the role_pin to ground on one. The ping node sends - * the current time to the pong node, which responds by sending the value back. - * The ping node can then see how long the whole cycle took. - */ - -/** - * @example pingpair_maple.pde - * - * This is an example of how to use the RF24 class on the Maple. For a more - * detailed explanation, see my blog post: - * nRF24L01+ Running on Maple - * - * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. - * - * Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -/** - * @example starping.pde - * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the - * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will - * respond back with a copy of the value. Each ping unit can get that response - * back, and determine how long the whole cycle took. - * - * This example requires a bit more complexity to determine which unit is which. - * The pong receiver is identified by having its role_pin tied to ground. - * The ping senders are further differentiated by a byte in eeprom. - */ - -/** - * @example pingpair_pl.pde - * - * This is an example of how to do two-way communication without changing - * transmit/receive modes. Here, a payload is set to the transmitter within - * the Ack packet of each transmission. Note that the payload is set BEFORE - * the sender's message arrives. - */ - -/** - * @example pingpair_irq.pde - * - * This is an example of how to user interrupts to interact with the radio. - * It builds on the pingpair_pl example, and uses ack payloads. - */ - -/** - * @example pingpair_sleepy.pde - * - * This is an example of how to use the RF24 class to create a battery- - * efficient system. It is just like the pingpair.pde example, but the - * ping node powers down the radio and sleeps the MCU after every - * ping/pong cycle. - */ - -/** - * @example scanner.pde - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -/** - * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver - * - * @section Goals Design Goals - * - * This library is designed to be... - * @li Maximally compliant with the intended operation of the chip - * @li Easy for beginners to use - * @li Consumed with a public interface that's similiar to other Arduino standard libraries - * - * @section News News - * - * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. - * Please open an issue if you find any problems using it with any version of Arduino. - * - * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the - * Maple Native, - * and should work with any Maple board. See the pingpair_maple example. - * Note that only the pingpair_maple example has been tested on Maple, although - * the others can certainly be adapted. - * - * @section Useful Useful References - * - * Please refer to: - * - * @li Documentation Main Page - * @li RF24 Class Documentation - * @li Source Code - * @li Downloads Page - * @li Chip Datasheet - * - * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or - * the SPI hardware will go into 'slave' mode. - * - * @section More More Information - * - * @subpage FAQ - * - * @section Projects Projects - * - * Stuff I have built with RF24 - * - * RF24 Getting Started - Finished Product - * - * Getting Started with nRF24L01+ on Arduino - * - * Nordic FOB and nRF24L01+ - * - * Using the Sparkfun Nordic FOB - * - * RF Duinode V3 (2V4) - * - * Low-Power Wireless Sensor Node - * - * nRF24L01+ connected to Leaf Labs Maple Native - * - * nRF24L01+ Running on Maple - */ - -#endif // __RF24_H__ -// vim:ai:cin:sts=2 sw=2 ft=cpp - diff --git a/RF24/RPi/RF24/RF24_config.h b/RF24/RPi/RF24/RF24_config.h deleted file mode 100644 index eef0704..0000000 --- a/RF24/RPi/RF24/RF24_config.h +++ /dev/null @@ -1,34 +0,0 @@ - -/* - Copyright (C) 2011 J. Coliz - - 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. - - 03/17/2013 : Charles-Henri Hallard (http://hallard.me) - Modified to use with Arduipi board http://hallard.me/arduipi - Modified to use the great bcm2835 library for I/O and SPI - - */ - -#ifndef __RF24_CONFIG_H__ -#define __RF24_CONFIG_H__ - -#include -#include -#include -#include -#include -#include -#include "bcm2835.h" - -// GCC a Arduino Missing -#define max(a,b) (a>b?a:b) -#define min(a,b) (a -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "bcm2835.h" - -// This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11) -// You can do some safe, non-destructive testing on any platform with: -// gcc bcm2835.c -D BCM2835_TEST -// ./a.out -//#define BCM2835_TEST - -// Uncommenting this define compiles alternative I2C code for the version 1 RPi -// The P1 header I2C pins are connected to SDA0 and SCL0 on V1. -// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. -// #define I2C_V1 - -// Pointers to the hardware register bases -volatile uint32_t *bcm2835_gpio = (volatile uint32_t *)MAP_FAILED; -volatile uint32_t *bcm2835_pwm = (volatile uint32_t *)MAP_FAILED; -volatile uint32_t *bcm2835_clk = (volatile uint32_t *)MAP_FAILED; -volatile uint32_t *bcm2835_pads = (volatile uint32_t *)MAP_FAILED; -volatile uint32_t *bcm2835_spi0 = (volatile uint32_t *)MAP_FAILED; -volatile uint32_t *bcm2835_bsc0 = (volatile uint32_t *)MAP_FAILED; -volatile uint32_t *bcm2835_bsc1 = (volatile uint32_t *)MAP_FAILED; -volatile uint32_t *bcm2835_st = (volatile 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; - -// I2C The time needed to transmit one byte. In microseconds. -static int i2c_byte_wait_us = 0; - -// SPI Custom Chip Select Pin -//static int spi_custom_cs = 0; - -// Time for millis() -static unsigned long long epoch ; - -// -// Low level register access functions -// - -void bcm2835_set_debug(uint8_t d) -{ - debug = d; -} - -// safe read from peripheral -uint32_t bcm2835_peri_read(volatile uint32_t* paddr) -{ - if (debug) - { - printf("bcm2835_peri_read paddr %08X\n", (unsigned) paddr); - return 0; - } - else - { - // Make sure we dont return the _last_ read which might get lost - // if subsequent code changes to a different peripheral - uint32_t ret = *paddr; - *paddr; // Read without assigneing to an unused variable - return ret; - } -} - -// read from peripheral without the read barrier -uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) -{ - if (debug) - { - printf("bcm2835_peri_read_nb paddr %08X\n", (unsigned) paddr); - return 0; - } - else - { - return *paddr; - } -} - -// safe write to peripheral -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); - } - else - { - // Make sure we don't rely on the first write, which may get - // lost if the previous access was to a different peripheral. - *paddr = value; - *paddr = value; - } -} - -// write to peripheral without the write barrier -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); - } - else - { - *paddr = value; - } -} - -// Set/clear only the bits in value covered by the mask -void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask) -{ - uint32_t v = bcm2835_peri_read(paddr); - v = (v & ~mask) | (value & mask); - bcm2835_peri_write(paddr, v); -} - -// -// Low level convenience functions -// - -// Function select -// pin is a BCM2835 GPIO pin number NOT RPi pin number -// There are 6 control registers, each control the functions of a block -// of 10 pins. -// Each control register has 10 sets of 3 bits per GPIO pin: -// -// 000 = GPIO Pin X is an input -// 001 = GPIO Pin X is an output -// 100 = GPIO Pin X takes alternate function 0 -// 101 = GPIO Pin X takes alternate function 1 -// 110 = GPIO Pin X takes alternate function 2 -// 111 = GPIO Pin X takes alternate function 3 -// 011 = GPIO Pin X takes alternate function 4 -// 010 = GPIO Pin X takes alternate function 5 -// -// So the 3 bits for port X are: -// X / 10 + ((X % 10) * 3) -void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode) -{ - // Function selects are 10 pins per 32 bit word, 3 bits per pin - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10); - uint8_t shift = (pin % 10) * 3; - uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift; - uint32_t value = mode << shift; - bcm2835_peri_set_bits(paddr, value, mask); -} - -// Set output pin -void bcm2835_gpio_set(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32; - uint8_t shift = pin % 32; - bcm2835_peri_write(paddr, 1 << shift); -} - -// Clear output pin -void bcm2835_gpio_clr(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32; - uint8_t shift = pin % 32; - bcm2835_peri_write(paddr, 1 << shift); -} - -// Set all output pins in the mask -void bcm2835_gpio_set_multi(uint32_t mask) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4; - bcm2835_peri_write(paddr, mask); -} - -// Clear all output pins in the mask -void bcm2835_gpio_clr_multi(uint32_t mask) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4; - bcm2835_peri_write(paddr, mask); -} - -// Read input pin -uint8_t bcm2835_gpio_lev(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEV0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = bcm2835_peri_read(paddr); - return (value & (1 << shift)) ? HIGH : LOW; -} - -// See if an event detection bit is set -// Sigh cant support interrupts yet -uint8_t bcm2835_gpio_eds(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = bcm2835_peri_read(paddr); - return (value & (1 << shift)) ? HIGH : LOW; -} - -// Write a 1 to clear the bit in EDS -void bcm2835_gpio_set_eds(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_write(paddr, value); -} - -// Rising edge detect enable -void bcm2835_gpio_ren(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, value, value); -} -void bcm2835_gpio_clr_ren(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, 0, value); -} - -// Falling edge detect enable -void bcm2835_gpio_fen(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, value, value); -} -void bcm2835_gpio_clr_fen(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, 0, value); -} - -// High detect enable -void bcm2835_gpio_hen(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, value, value); -} -void bcm2835_gpio_clr_hen(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, 0, value); -} - -// Low detect enable -void bcm2835_gpio_len(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, value, value); -} -void bcm2835_gpio_clr_len(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, 0, value); -} - -// Async rising edge detect enable -void bcm2835_gpio_aren(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, value, value); -} -void bcm2835_gpio_clr_aren(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, 0, value); -} - -// Async falling edge detect enable -void bcm2835_gpio_afen(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, value, value); -} -void bcm2835_gpio_clr_afen(uint8_t pin) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; - uint8_t shift = pin % 32; - uint32_t value = 1 << shift; - bcm2835_peri_set_bits(paddr, 0, value); -} - -// Set pullup/down -void bcm2835_gpio_pud(uint8_t pud) -{ - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; - bcm2835_peri_write(paddr, pud); -} - -// Pullup/down clock -// Clocks the value of pud into the GPIO pin -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); -} - -// Read GPIO pad behaviour for groups of GPIOs -uint32_t bcm2835_gpio_pad(uint8_t group) -{ - volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; - return bcm2835_peri_read(paddr); -} - -// Set GPIO pad behaviour for groups of GPIOs -// powerup value for al pads is -// BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA -void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) -{ - volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; - bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD); -} - -// Some convenient arduino-like functions -// milliseconds -void bcm2835_delay(unsigned int millis) -{ - struct timespec sleeper; - - sleeper.tv_sec = (time_t)(millis / 1000); - sleeper.tv_nsec = (long)(millis % 1000) * 1000000; - nanosleep(&sleeper, NULL); -} - -// microseconds -void bcm2835_delayMicroseconds(uint64_t micros) -{ - struct timespec t1; - uint64_t start; - - // Calling nanosleep() takes at least 100-200 us, so use it for - // long waits and use a busy wait on the System Timer for the rest. - start = bcm2835_st_read(); - - if (micros > 450) - { - t1.tv_sec = 0; - t1.tv_nsec = 1000 * (long)(micros - 200); - nanosleep(&t1, NULL); - } - - bcm2835_st_delay(start, micros); -} - -// This function is added in order to simulate arduino millis() function -unsigned int bcm2835_millis(void) -{ -struct timeval now; -unsigned long long ms; - -gettimeofday(&now, NULL); - -ms = (now.tv_sec * 1000000 + now.tv_usec) / 1000 ; - -return ((uint32_t) (ms - epoch )); -} - -// -// Higher level convenience functions -// - -// Set the state of an output -void bcm2835_gpio_write(uint8_t pin, uint8_t on) -{ - if (on) - bcm2835_gpio_set(pin); - else - bcm2835_gpio_clr(pin); -} - -// Set the state of a all 32 outputs in the mask to on or off -void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on) -{ - if (on) - bcm2835_gpio_set_multi(mask); - else - bcm2835_gpio_clr_multi(mask); -} - -// Set the state of a all 32 outputs in the mask to the values in value -void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) -{ - bcm2835_gpio_set_multi(value & mask); - bcm2835_gpio_clr_multi((~value) & mask); -} - -// Set the pullup/down resistor for a pin -// -// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on -// the respective GPIO pins. These registers must be used in conjunction with the GPPUD -// register to effect GPIO Pull-up/down changes. The following sequence of events is -// required: -// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither -// to remove the current Pull-up/down) -// 2. Wait 150 cycles ? this provides the required set-up time for the control signal -// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to -// modify ? NOTE only the pads which receive a clock will be modified, all others will -// retain their previous state. -// 4. Wait 150 cycles ? this provides the required hold time for the control signal -// 5. Write to GPPUD to remove the control signal -// 6. Write to GPPUDCLK0/1 to remove the clock -// -// RPi has P1-03 and P1-05 with 1k8 pullup resistor -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); -} - -void bcm2835_spi_begin() -{ - - // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them - //bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 - - // Enable CE0 by default, will be disabled if bcm2835_chipSelect() is called using another pin - bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 - - - bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO - bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI - bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK - - // Set the SPI CS register to the some sensible defaults - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; - bcm2835_peri_write(paddr, 0); // All 0s - - // Clear TX and RX fifos - bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); -} - -void bcm2835_spi_end(void) -{ - /* // Set all the SPI0 pins back to input - if (spi_custom_cs == 0) - { - bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 - bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 - } - else - { - bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_INPT); // Custom GPIO - } - bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO - bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI - bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK - */ - - // Set all the SPI0 pins back to input - bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 - bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 - bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO - bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI - bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK - -} - -/* -// Drive Custom chip select pin -void bcm2835_spi_setChipSelect(uint8_t level) -{ - // Do this only if we are using custom ChipSelect I/O - if ( spi_custom_cs > BCM2835_SPI_CS_NONE ) - bcm2835_gpio_write(spi_custom_cs, level); -}*/ - - - void bcm2835_spi_setBitOrder(uint8_t order) -{ - // BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one suported by SPI0 -} - -// defaults to 0, which means a divider of 65536. -// The divisor must be a power of 2. Odd numbers -// rounded down. The maximum SPI clock rate is -// of the APB clock -void bcm2835_spi_setClockDivider(uint16_t divider) -{ - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CLK/4; - bcm2835_peri_write(paddr, divider); -} - -void bcm2835_spi_setDataMode(uint8_t mode) -{ - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; - // Mask in the CPO and CPHA bits of CS - bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA); -} - -// Writes (and reads) a single byte to SPI -uint8_t bcm2835_spi_transfer(uint8_t value) -{ - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; - volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; - - // Custom chip select LOW - /*if(spi_custom_cs > BCM2835_SPI_CS_NONE){ - bcm2835_spi_setChipSelect(LOW); - }*/ - - // This is Polled transfer as per section 10.6.1 - // BUG ALERT: what happens if we get interupted in this section, and someone else - // accesses a different peripheral? - // 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)){ - delayMicroseconds(10); - } - - // Write to FIFO, no barrier - bcm2835_peri_write_nb(fifo, value); - - // Wait for DONE to be set - while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ - delayMicroseconds(10); - } - - // Read any byte that was sent back by the slave while we sere sending to it - uint32_t ret = bcm2835_peri_read_nb(fifo); - - // Set TA = 0, and also set the barrier - bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); - - // Custom chip select HIGH - //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ - // bcm2835_spi_setChipSelect(HIGH); - //} - return ret; -} - -// Writes (and reads) an number of bytes to SPI -void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) -{ - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; - volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; - uint32_t TXCnt=0; - uint32_t RXCnt=0; - - // This is Polled transfer as per section 10.6.1 - // BUG ALERT: what happens if we get interupted in this section, and someone else - // accesses a different peripheral? - - // 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); - - // Use the FIFO's to reduce the interbyte times - 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 )) - { - delayMicroseconds(10); - bcm2835_peri_write_nb(fifo, tbuf[TXCnt]); - TXCnt++; - } - //Rx fifo not empty, so get the next received bytes - while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) - { - delayMicroseconds(10); - rbuf[RXCnt] = bcm2835_peri_read_nb(fifo); - RXCnt++; - } - } - // Wait for DONE to be set - while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ - delayMicroseconds(10); - } - // Set TA = 0, and also set the barrier - bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); -} - -// Writes an number of bytes to SPI -void bcm2835_spi_writenb(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; - - //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ - // bcm2835_spi_setChipSelect(LOW); - //} - // This is Polled transfer as per section 10.6.1 - // BUG ALERT: what happens if we get interupted in this section, and someone else - // accesses a different peripheral? - - // 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); - - uint32_t i; - for (i = 0; i < len; i++) - { - // Maybe wait for TXD - while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) - ; - - // Write to FIFO, no barrier - bcm2835_peri_write_nb(fifo, tbuf[i]); - - // Read from FIFO to prevent stalling - while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) - (void) bcm2835_peri_read_nb(fifo); - } - - // Wait for DONE to be set - while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) { - while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) - (void) bcm2835_peri_read_nb(fifo); - }; - - // Set TA = 0, and also set the barrier - bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); - - //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ - // bcm2835_spi_setChipSelect(HIGH); - //} -} - -// Writes (and reads) an number of bytes to SPI -// Read bytes are copied over onto the transmit buffer -void bcm2835_spi_transfern(char* buf, uint32_t len) -{ - bcm2835_spi_transfernb(buf, buf, len); -} - -void bcm2835_spi_chipSelect(uint8_t cs) -{ - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; - - if(cs){ - bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 - bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // Revert default of CE0 back to input - }else{ - bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 - } - - // Mask in the CS bits of CS - bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); -} - -void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active) -{ - volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; - uint8_t shift = 21 + cs; - // Mask in the appropriate CSPOLn bit - bcm2835_peri_set_bits(paddr, active << shift, 1 << shift); -} - -void bcm2835_i2c_begin(void) -{ -#ifdef I2C_V1 - volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; - // Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them - bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA - bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL -#else - volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; - // Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them - bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA - bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL -#endif - - // Read the clock divider register - uint16_t cdiv = bcm2835_peri_read(paddr); - // Calculate time for transmitting one byte - // 1000000 = micros seconds in a second - // 9 = Clocks per byte : 8 bits + ACK - i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9; -} - -void bcm2835_i2c_end(void) -{ -#ifdef I2C_V1 - // Set all the I2C/BSC0 pins back to input - bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA - bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL -#else - // Set all the I2C/BSC1 pins back to input - bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA - bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL -#endif -} - -void bcm2835_i2c_setSlaveAddress(uint8_t addr) -{ - // Set I2C Device Address -#ifdef I2C_V1 - volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4; -#else - volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4; -#endif - bcm2835_peri_write(paddr, addr); -} - -// defaults to 0x5dc, should result in a 166.666 kHz I2C clock frequency. -// The divisor must be a power of 2. Odd numbers -// rounded down. -void bcm2835_i2c_setClockDivider(uint16_t divider) -{ -#ifdef I2C_V1 - volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; -#else - volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; -#endif - bcm2835_peri_write(paddr, divider); - // Calculate time for transmitting one byte - // 1000000 = micros seconds in a second - // 9 = Clocks per byte : 8 bits + ACK - i2c_byte_wait_us = ((float)divider / BCM2835_CORE_CLK_HZ) * 1000000 * 9; -} - -// set I2C clock divider by means of a baudrate number -void bcm2835_i2c_set_baudrate(uint32_t baudrate) -{ - uint32_t divider; - // use 0xFFFE mask to limit a max value and round down any odd number - divider = (BCM2835_CORE_CLK_HZ / baudrate) & 0xFFFE; - bcm2835_i2c_setClockDivider( (uint16_t)divider ); -} - -// Writes an number of bytes to I2C -uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) -{ -#ifdef I2C_V1 - volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; -#else - volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif - - uint32_t remaining = len; - uint32_t i = 0; - uint8_t reason = BCM2835_I2C_REASON_OK; - - // Clear FIFO - 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 - bcm2835_peri_write_nb(dlen, len); - // pre populate FIFO with max buffer - while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) - { - bcm2835_peri_write_nb(fifo, buf[i]); - i++; - remaining--; - } - - // Enable device and start transfer - bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); - - // Transfer is over when BCM2835_BSC_S_DONE - while(!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE )) - { - while ( remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TXD )) - { - // Write to FIFO, no barrier - bcm2835_peri_write_nb(fifo, buf[i]); - i++; - remaining--; - } - } - - // Received a NACK - if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) - { - reason = BCM2835_I2C_REASON_ERROR_NACK; - } - - // Received Clock Stretch Timeout - else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) - { - reason = BCM2835_I2C_REASON_ERROR_CLKT; - } - - // Not all data is sent - else if (remaining) - { - reason = BCM2835_I2C_REASON_ERROR_DATA; - } - - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); - - return reason; -} - -// Read an number of bytes from I2C -uint8_t bcm2835_i2c_read(char* buf, uint32_t len) -{ -#ifdef I2C_V1 - volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; -#else - volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif - - uint32_t remaining = len; - uint32_t i = 0; - uint8_t reason = BCM2835_I2C_REASON_OK; - - // Clear FIFO - 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 - bcm2835_peri_write_nb(dlen, len); - // Start read - bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ); - - // 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) - { - // Read from FIFO, no barrier - buf[i] = bcm2835_peri_read_nb(fifo); - i++; - remaining--; - } - } - - // transfer has finished - grab any remaining stuff in FIFO - while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) - { - // Read from FIFO, no barrier - buf[i] = bcm2835_peri_read_nb(fifo); - i++; - remaining--; - } - - // Received a NACK - if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) - { - reason = BCM2835_I2C_REASON_ERROR_NACK; - } - - // Received Clock Stretch Timeout - else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) - { - reason = BCM2835_I2C_REASON_ERROR_CLKT; - } - - // Not all data is received - else if (remaining) - { - reason = BCM2835_I2C_REASON_ERROR_DATA; - } - - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); - - return reason; -} - -// Read an number of bytes from I2C sending a repeated start after writing -// the required register. Only works if your device supports this mode -uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) -{ -#ifdef I2C_V1 - volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; -#else - volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif - uint32_t remaining = len; - uint32_t i = 0; - uint8_t reason = BCM2835_I2C_REASON_OK; - - // Clear FIFO - 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 - bcm2835_peri_write_nb(dlen, 1); - // Enable device and start transfer - bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN); - bcm2835_peri_write_nb(fifo, regaddr[0]); - bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); - - // poll for transfer has started - while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) - { - // Linux may cause us to miss entire transfer stage - if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) - break; - } - - // Send a repeated start with read bit set in address - bcm2835_peri_write_nb(dlen, len); - bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); - - // Wait for write to complete and first byte back. - bcm2835_delayMicroseconds(i2c_byte_wait_us * 3); - - // 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 (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) - { - // Read from FIFO, no barrier - buf[i] = bcm2835_peri_read_nb(fifo); - i++; - remaining--; - } - } - - // transfer has finished - grab any remaining stuff in FIFO - while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) - { - // Read from FIFO, no barrier - buf[i] = bcm2835_peri_read_nb(fifo); - i++; - remaining--; - } - - // Received a NACK - if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) - { - reason = BCM2835_I2C_REASON_ERROR_NACK; - } - - // Received Clock Stretch Timeout - else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) - { - reason = BCM2835_I2C_REASON_ERROR_CLKT; - } - - // Not all data is sent - else if (remaining) - { - reason = BCM2835_I2C_REASON_ERROR_DATA; - } - - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); - - return reason; -} - -// Sending an arbitrary number of bytes before issuing a repeated start -// (with no prior stop) and reading a response. Some devices require this behavior. -uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len) -{ -#ifdef I2C_V1 - volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; -#else - volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; - volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; - volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; - volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; -#endif - - uint32_t remaining = cmds_len; - uint32_t i = 0; - uint8_t reason = BCM2835_I2C_REASON_OK; - - // Clear FIFO - 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 - bcm2835_peri_write_nb(dlen, cmds_len); - - // pre populate FIFO with max buffer - while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) - { - bcm2835_peri_write_nb(fifo, cmds[i]); - i++; - remaining--; - } - - // Enable device and start transfer - bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); - - // poll for transfer has started (way to do repeated start, from BCM2835 datasheet) - while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) - { - // Linux may cause us to miss entire transfer stage - if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) - break; - } - - remaining = buf_len; - i = 0; - - // Send a repeated start with read bit set in address - bcm2835_peri_write_nb(dlen, buf_len); - bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); - - // Wait for write to complete and first byte back. - bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1)); - - // 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 (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) - { - // Read from FIFO, no barrier - buf[i] = bcm2835_peri_read_nb(fifo); - i++; - remaining--; - } - } - - // transfer has finished - grab any remaining stuff in FIFO - while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) - { - // Read from FIFO, no barrier - buf[i] = bcm2835_peri_read_nb(fifo); - i++; - remaining--; - } - - // Received a NACK - if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) - { - reason = BCM2835_I2C_REASON_ERROR_NACK; - } - - // Received Clock Stretch Timeout - else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) - { - reason = BCM2835_I2C_REASON_ERROR_CLKT; - } - - // Not all data is sent - else if (remaining) - { - reason = BCM2835_I2C_REASON_ERROR_DATA; - } - - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); - - return reason; -} - -// Read the System Timer Counter (64-bits) -uint64_t bcm2835_st_read(void) -{ - volatile uint32_t* paddr; - uint64_t st; - paddr = bcm2835_st + BCM2835_ST_CHI/4; - st = bcm2835_peri_read(paddr); - st <<= 32; - paddr = bcm2835_st + BCM2835_ST_CLO/4; - st += bcm2835_peri_read(paddr); - return st; -} - -// Delays for the specified number of microseconds with offset -void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros) -{ - uint64_t compare = offset_micros + micros; - - while(bcm2835_st_read() < compare) - ; -} - -// PWM - -void bcm2835_pwm_set_clock(uint32_t divisor) -{ - // From Gerts code - divisor &= 0xfff; - // Stop PWM clock - bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01); - bcm2835_delay(110); // Prevents clock going slow - // Wait for the clock to be not busy - while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) - bcm2835_delay(1); - // set the clock divider and enable PWM clock - bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); - bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); // Source=osc and enable -} - -void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled) -{ - uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL); - - if (channel == 0) - { - if (markspace) - control |= BCM2835_PWM0_MS_MODE; - else - control &= ~BCM2835_PWM0_MS_MODE; - if (enabled) - control |= BCM2835_PWM0_ENABLE; - else - control &= ~BCM2835_PWM0_ENABLE; - } - else if (channel == 1) - { - if (markspace) - control |= BCM2835_PWM1_MS_MODE; - else - control &= ~BCM2835_PWM1_MS_MODE; - if (enabled) - control |= BCM2835_PWM1_ENABLE; - else - control &= ~BCM2835_PWM1_ENABLE; - } - - // If you use the barrier here, wierd things happen, and the commands dont work - bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control); - // bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); - -} - -void bcm2835_pwm_set_range(uint8_t channel, uint32_t range) -{ - if (channel == 0) - bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range); - else if (channel == 1) - bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range); -} - -void bcm2835_pwm_set_data(uint8_t channel, uint32_t data) -{ - if (channel == 0) - bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data); - else if (channel == 1) - bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data); -} - -// Allocate page-aligned memory. -void *malloc_aligned(size_t size) -{ - void *mem; - errno = posix_memalign(&mem, BCM2835_PAGE_SIZE, size); - return (errno ? NULL : mem); -} - -// Map 'size' bytes starting at 'off' in file 'fd' to memory. -// Return mapped address on success, MAP_FAILED otherwise. -// On error print message. -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_FAILED == map) - fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); - return map; -} - -static void unmapmem(void **pmem, size_t size) -{ - if (*pmem == MAP_FAILED) return; - munmap(*pmem, size); - *pmem = MAP_FAILED; -} - -// Initialise this library. -int bcm2835_init(void) -{ - struct timeval tv ; - - if (debug) - { - bcm2835_pads = (uint32_t*)BCM2835_GPIO_PADS; - bcm2835_clk = (uint32_t*)BCM2835_CLOCK_BASE; - bcm2835_gpio = (uint32_t*)BCM2835_GPIO_BASE; - bcm2835_pwm = (uint32_t*)BCM2835_GPIO_PWM; - bcm2835_spi0 = (uint32_t*)BCM2835_SPI0_BASE; - bcm2835_bsc0 = (uint32_t*)BCM2835_BSC0_BASE; - bcm2835_bsc1 = (uint32_t*)BCM2835_BSC1_BASE; - bcm2835_st = (uint32_t*)BCM2835_ST_BASE; - return 1; // Success - } - int memfd = -1; - int ok = 0; - // Open the master /dev/memory device - if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) - { - fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", - strerror(errno)) ; - goto exit; - } - - // GPIO: - bcm2835_gpio = (volatile uint32_t *)mapmem("gpio", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_BASE); - if (bcm2835_gpio == MAP_FAILED) goto exit; - - // PWM - bcm2835_pwm = (volatile uint32_t *)mapmem("pwm", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PWM); - if (bcm2835_pwm == MAP_FAILED) goto exit; - - // Clock control (needed for PWM) - bcm2835_clk = (volatile uint32_t *)mapmem("clk", BCM2835_BLOCK_SIZE, memfd, BCM2835_CLOCK_BASE); - if (bcm2835_clk == MAP_FAILED) goto exit; - - bcm2835_pads = (volatile uint32_t *)mapmem("pads", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PADS); - if (bcm2835_pads == MAP_FAILED) goto exit; - - bcm2835_spi0 = (volatile uint32_t *)mapmem("spi0", BCM2835_BLOCK_SIZE, memfd, BCM2835_SPI0_BASE); - if (bcm2835_spi0 == MAP_FAILED) goto exit; - - // I2C - bcm2835_bsc0 = (volatile uint32_t *)mapmem("bsc0", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC0_BASE); - if (bcm2835_bsc0 == MAP_FAILED) goto exit; - - bcm2835_bsc1 = (volatile uint32_t *)mapmem("bsc1", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC1_BASE); - if (bcm2835_bsc1 == MAP_FAILED) goto exit; - - // ST - bcm2835_st = (volatile uint32_t *)mapmem("st", BCM2835_BLOCK_SIZE, memfd, BCM2835_ST_BASE); - if (bcm2835_st == MAP_FAILED) goto exit; - - ok = 1; - -exit: - if (memfd >= 0) - close(memfd); - - if (!ok) - bcm2835_close(); - - gettimeofday (&tv, NULL) ; - epoch = (tv.tv_sec * 1000000 + tv.tv_usec) / 1000 ; - - return ok; -} - -// Close this library and deallocate everything -int bcm2835_close(void) -{ - if (debug) return 1; // Success - unmapmem((void**) &bcm2835_gpio, BCM2835_BLOCK_SIZE); - unmapmem((void**) &bcm2835_pwm, BCM2835_BLOCK_SIZE); - unmapmem((void**) &bcm2835_clk, BCM2835_BLOCK_SIZE); - unmapmem((void**) &bcm2835_spi0, BCM2835_BLOCK_SIZE); - unmapmem((void**) &bcm2835_bsc0, BCM2835_BLOCK_SIZE); - unmapmem((void**) &bcm2835_bsc1, BCM2835_BLOCK_SIZE); - unmapmem((void**) &bcm2835_st, BCM2835_BLOCK_SIZE); - unmapmem((void**) &bcm2835_pads, BCM2835_BLOCK_SIZE); - return 1; // Success -} - -#ifdef BCM2835_TEST -// this is a simple test program that prints out what it will do rather than -// actually doing it -int main(int argc, char **argv) -{ - // Be non-destructive - bcm2835_set_debug(1); - - if (!bcm2835_init()) - return 1; - - // Configure some GPIO pins fo some testing - // Set RPI pin P1-11 to be an output - bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP); - // Set RPI pin P1-15 to be an input - bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT); - // with a pullup - bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP); - // And a low detect enable - bcm2835_gpio_len(RPI_GPIO_P1_15); - // and input hysteresis disabled on GPIOs 0 to 27 - bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA); - -#if 1 - // Blink - while (1) - { - // Turn it on - bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH); - - // wait a bit - bcm2835_delay(500); - - // turn it off - bcm2835_gpio_write(RPI_GPIO_P1_11, LOW); - - // wait a bit - bcm2835_delay(500); - } -#endif - -#if 0 - // Read input - while (1) - { - // Read some data - uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15); - printf("read from pin 15: %d\n", value); - - // wait a bit - bcm2835_delay(500); - } -#endif - -#if 0 - // Look for a low event detection - // eds will be set whenever pin 15 goes low - while (1) - { - if (bcm2835_gpio_eds(RPI_GPIO_P1_15)) - { - // Now clear the eds flag by setting it to 1 - bcm2835_gpio_set_eds(RPI_GPIO_P1_15); - printf("low event detect for pin 15\n"); - } - - // wait a bit - bcm2835_delay(500); - } -#endif - - if (!bcm2835_close()) - return 1; - - return 0; -} -#endif - - - diff --git a/RF24/RPi/RF24/bcm2835.h b/RF24/RPi/RF24/bcm2835.h deleted file mode 100644 index f550fcb..0000000 --- a/RF24/RPi/RF24/bcm2835.h +++ /dev/null @@ -1,1353 +0,0 @@ -// bcm2835.h -// -// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi -// -// Author: Mike McCauley -// Copyright (C) 2011-2013 Mike McCauley -// $Id: bcm2835.h,v 1.13 2013/12/06 22:24:52 mikem Exp mikem $ -// -//****************************************/ -// TMRh20 2014 - Merge updated lib from here: http://www.airspayce.com/mikem/bcm2835/group__spi.html#ga2fa186568605c21e9166a19b1d82ea95 -// with lib from here: https://github.com/farconada/RF24Network/blob/master/bcm2835.c -// Uses ONLY hardware CS pins -// Any CE pin can be used -// For use with optimized RPi RF24 and RF24 Network libs - see github.com/TMRh20 -//****************************************/ -// -/// \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi -/// -/// This is a C library for Raspberry Pi (RPi). It provides access to -/// GPIO and other IO functions on the Broadcom BCM 2835 chip, -/// 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. -/// -/// It provides functions for reading digital inputs and setting digital outputs, using SPI and I2C, -/// and for accessing the system timers. -/// Pin event detection is supported by polling (interrupts are not supported). -/// -/// 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.36.tar.gz -/// You can find the latest version at http://www.airspayce.com/mikem/bcm2835 -/// -/// Several example programs are provided. -/// -/// Based on data in http://elinux.org/RPi_Low-level_peripherals and -/// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf -/// and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 -/// -/// 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. -/// -/// Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian -/// and Occidentalisv01 -/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() -/// are used and the pin is pulled LOW -/// it can cause temporary hangs on 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian -/// and Occidentalisv01. -/// Reason for this is not yet determined, but we suspect that an interrupt handler is -/// hitting a hard loop on those OSs. -/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with -/// bcm2835_gpio_clr_len() and friends after use. -/// -/// \par Installation -/// -/// This library consists of a single non-shared library and header file, which will be -/// installed in the usual places by make install -/// -/// \code -/// # download the latest version of the library, say bcm2835-1.xx.tar.gz, then: -/// tar zxvf bcm2835-1.xx.tar.gz -/// cd bcm2835-1.xx -/// ./configure -/// make -/// sudo make check -/// sudo make install -/// \endcode -/// -/// \par Physical Addresses -/// -/// The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() -/// are low level peripheral register access functions. They are designed to use -/// physical addresses as described in section 1.2.3 ARM physical addresses -/// of the BCM2835 ARM Peripherals manual. -/// Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus -/// addresses for peripherals are set up to map onto the peripheral bus address range starting at -/// 0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at -/// physical address 0x20nnnnnn. -/// -/// The base address of the various peripheral registers are available with the following -/// externals: -/// bcm2835_gpio -/// bcm2835_pwm -/// bcm2835_clk -/// bcm2835_pads -/// bcm2835_spio0 -/// bcm2835_st -/// bcm2835_bsc0 -/// bcm2835_bsc1 -/// -/// \par Pin Numbering -/// -/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying -/// BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs -/// -/// RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, -/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the -/// IDE header. -/// -/// RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd. -/// -/// The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ -/// the RPi pin number. There are symbolic definitions for each of the available pins -/// that you should use for convenience. See \ref RPiGPIOPin. -/// -/// \par SPI Pins -/// -/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, -/// allowing you to send and received data by SPI (Serial Peripheral Interface). -/// For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus -/// -/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their -/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able -/// to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). -/// When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be -/// configured and controled with the usual bcm2835_gpio_* calls. -/// -/// The Raspberry Pi GPIO pins used for SPI are: -/// -/// - P1-19 (MOSI) -/// - P1-21 (MISO) -/// - P1-23 (CLK) -/// - P1-24 (CE0) -/// - P1-26 (CE1) -/// -/// \par I2C Pins -/// -/// The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface, -/// allowing you to send and received data by I2C ("eye-squared cee"; generically referred to as "two-wire interface") . -/// For more information about I?C, see http://en.wikipedia.org/wiki/I%C2%B2C -/// -/// The Raspberry Pi V2 GPIO pins used for I2C are: -/// -/// - P1-03 (SDA) -/// - P1-05 (SLC) -/// -/// \par PWM -/// -/// The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides -/// functions for configuring and controlling PWM output on these pins. -/// -/// The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset of -/// GPIO pins. The following GPIO pins may be connected to the following PWM channels (from section 9.5): -/// \code -/// GPIO PIN RPi pin PWM Channel ALT FUN -/// 12 0 0 -/// 13 1 0 -/// 18 1-12 0 5 -/// 19 1 5 -/// 40 0 0 -/// 41 1 0 -/// 45 1 0 -/// 52 0 1 -/// 53 1 1 -/// \endcode -/// In order for a GPIO pin to emit output from its PWM channel, it must be set to the Alt Function given above. -/// Note carefully that current versions of the Raspberry Pi only expose one of these pins (GPIO 18 = RPi Pin 1-12) -/// on the IO headers, and therefore this is the only IO pin on the RPi that can be used for PWM. -/// Further it must be set to ALT FUN 5 to get PWM output. -/// -/// Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using -/// bcm2835_pwm_set_clock(). Each channel can be separately enabled with bcm2835_pwm_set_mode(). -/// The average output of the PWM channel is determined by the ratio of DATA/RANGE for that channel. -/// Use bcm2835_pwm_set_range() to set the range and bcm2835_pwm_set_data() to set the data in that ratio -/// -/// Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware -/// sends a combination of clock pulses that results in an overall DATA pulses per RANGE pulses. -/// In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by -/// LOW for RANGE-DATA clock pulses. -/// -/// The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from -/// a 19.2MHz clock. You can set any divider, but some common ones are provided by the BCM2835_PWM_CLOCK_DIVIDER_* -/// values of \ref bcm2835PWMClockDivider. -/// -/// For example, say you wanted to drive a DC motor with PWM at about 1kHz, -/// and control the speed in 1/1024 increments from -/// 0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the -/// clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be -/// 1.2MHz/1024 = 1171.875Hz. -/// -/// \par Real Time performance constraints -/// -/// The bcm2835 is a library for user programs (i.e. they run in 'userland'). -/// Such programs are not part of the kernel and are usually -/// subject to paging and swapping by the kernel while it does other things besides running your program. -/// This means that you should not expect to get real-time performance or -/// real-time timing constraints from such programs. In particular, there is no guarantee that the -/// bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. -/// In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times -/// than the one you asked for. So please dont expect to get exactly the time delay you request. -/// -/// Arjan reports that you can prevent swapping on Linux with the following code fragment: -/// -/// \code -/// struct sched_param sp; -/// memset(&sp, 0, sizeof(sp)); -/// sp.sched_priority = sched_get_priority_max(SCHED_FIFO); -/// sched_setscheduler(0, SCHED_FIFO, &sp); -/// mlockall(MCL_CURRENT | MCL_FUTURE); -/// \endcode -/// -/// \par Bindings to other languages -/// -/// mikem has made Perl bindings available at CPAN: -/// http://search.cpan.org/~mikem/Device-BCM2835-1.9/lib/Device/BCM2835.pm -/// Matthew Baker has kindly made Python bindings available at: -/// https://github.com/mubeta06/py-libbcm2835 -/// Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility -/// for Raspberry Pi, based on the bcm2835 library. The -/// utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as a -/// free download with source included: http://ipsolutionscorp.com/raspberry-pi-spi-utility/ -/// -/// \par Open Source Licensing GPL V2 -/// -/// This is the appropriate option if you want to share the source code of your -/// application with everyone you distribute it to, and you also want to give them -/// 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 -/// -/// \par Acknowledgements -/// -/// Some of this code has been inspired by Dom and Gert. -/// The I2C code has been inspired by Alan Barr. -/// -/// \par Revision History -/// -/// \version 1.0 Initial release -/// \version 1.1 Minor bug fixes -/// \version 1.2 Added support for SPI -/// \version 1.3 Added bcm2835_spi_transfern() -/// \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson. -/// \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz -/// \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 -/// Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() -/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now -/// changes only the pin specified. Other pins that were already previously -/// enabled stay enabled. -/// Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() -/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() -/// to clear the enable for individual pins, suggested by Andreas Sundstrom. -/// \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write. -/// \version 1.8 Improvements to read barrier, as suggested by maddin. -/// \version 1.9 Improvements contributed by mikew: -/// I noticed that it was mallocing memory for the mmaps on /dev/mem. -/// It's not necessary to do that, you can just mmap the file directly, -/// so I've removed the mallocs (and frees). -/// I've also modified delayMicroseconds() to use nanosleep() for long waits, -/// and a busy wait on a high resolution timer for the rest. This is because -/// I've found that calling nanosleep() takes at least 100-200 us. -/// You need to link using '-lrt' using this version. -/// I've added some unsigned casts to the debug prints to silence compiler -/// warnings I was getting, fixed some typos, and changed the value of -/// BCM2835_PAD_HYSTERESIS_ENABLED to 0x08 as per Gert van Loo's doc at -/// http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 -/// Also added a define for the passwrd value that Gert says is needed to -/// change pad control settings. -/// \version 1.10 Changed the names of the delay functions to bcm2835_delay() -/// and bcm2835_delayMicroseconds() to prevent collisions with wiringPi. -/// Macros to map delay()-> bcm2835_delay() and -/// Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which -/// can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY -/// \version 1.11 Fixed incorrect link to download file -/// \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) -/// \version 1.13 New GPIO pin definitions for RPi version 2 plug P5 -/// Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio -/// bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0. -/// \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead. -/// Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. -/// Reported by Ben Simpson. -/// \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe. -/// \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi -/// to allow a mask of pins to be set all at once. Requested by Sebastian Loncar. -/// \version 1.17 Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar. -/// \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: -/// now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. -/// No need to link with -lrt now. Contributed by Arjan van Vught. -/// \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. -/// Reported by olly. -/// \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted. -/// \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. -/// Patch courtesy Jeremy Mortis. -/// \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set -/// falling edge events. Reported by Mark Dootson. -/// \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. -/// Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions -/// to fix ocasional reads not completing. Patched by Mark Dootson. -/// \version 1.24 Mark Dootson p[atched a problem with his previously submitted code -/// under high load from other processes. -/// \version 1.25 Updated author and distribution location details to airspayce.com -/// \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. -/// Reported by Hartmut Henkel. -/// \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. -/// \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. -/// \version 1.29 Further fix to SPI from Peter Würtz. -/// \version 1.30 10 microsecond delays from bcm2835_spi_transfer and bcm2835_spi_transfern for -/// significant performance improvements, Patch by Alan Watson. -/// \version 1.31 Fix a GCC warning about dummy variable, patched by Alan Watson. Thanks. -/// \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. -/// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. -/// Contributed by Malcolm Wiles based on work by Arvi Govindaraj. -/// \version 1.33 Added command line utilities i2c and gpio to examples. Contributed by Shahrooz Shahparnia. -/// \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, -/// sends a repeat start, and reads from the device. Contributed by Eduardo Steinhorst. -/// \version 1.35 Fix build errors when compiled under Qt. Also performance improvements with SPI transfers. Contributed by Udo Klaas. -/// \version 1.36 Make automake's test runner detect that we're skipping tests when not root, the second -/// one makes us skip the test when using fakeroot (as used when building -/// Debian packages). Contributed by Guido Günther. -/// -/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS - - - -// Defines for BCM2835 -#ifndef BCM2835_H -#define BCM2835_H - -#include - -/// \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. -/// @{ - - -/// This means pin HIGH, true, 3.3volts on a pin. -#define HIGH 0x1 -/// This means pin LOW, false, 0volts on a pin. -#define LOW 0x0 - -/// Speed of the core clock core_clk -#define BCM2835_CORE_CLK_HZ 250000000 ///< 250 MHz - -// Physical addresses for various peripheral register sets -/// Base Physical Address of the BCM 2835 peripheral registers -#define BCM2835_PERI_BASE 0x20000000 -/// Base Physical Address of the System Timer registers -#define BCM2835_ST_BASE (BCM2835_PERI_BASE + 0x3000) -/// Base Physical Address of the Pads registers -#define BCM2835_GPIO_PADS (BCM2835_PERI_BASE + 0x100000) -/// Base Physical Address of the Clock/timer registers -#define BCM2835_CLOCK_BASE (BCM2835_PERI_BASE + 0x101000) -/// Base Physical Address of the GPIO registers -#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) -/// Base Physical Address of the SPI0 registers -#define BCM2835_SPI0_BASE (BCM2835_PERI_BASE + 0x204000) -/// Base Physical Address of the BSC0 registers -#define BCM2835_BSC0_BASE (BCM2835_PERI_BASE + 0x205000) -/// Base Physical Address of the PWM registers -#define BCM2835_GPIO_PWM (BCM2835_PERI_BASE + 0x20C000) - /// Base Physical Address of the BSC1 registers -#define BCM2835_BSC1_BASE (BCM2835_PERI_BASE + 0x804000) - - -/// Base of the ST (System Timer) registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_st; - -/// Base of the GPIO registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_gpio; - -/// Base of the PWM registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_pwm; - -/// Base of the CLK registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_clk; - -/// Base of the PADS registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_pads; - -/// Base of the SPI0 registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_spi0; - -/// Base of the BSC0 registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_bsc0; - -/// Base of the BSC1 registers. -/// Available after bcm2835_init has been called -extern volatile uint32_t *bcm2835_bsc1; - -/// Size of memory page on RPi -#define BCM2835_PAGE_SIZE (4*1024) -/// Size of memory block on RPi -#define BCM2835_BLOCK_SIZE (4*1024) - - -// Defines for GPIO -// The BCM2835 has 54 GPIO pins. -// BCM2835 data sheet, Page 90 onwards. -/// GPIO register offsets from BCM2835_GPIO_BASE. Offsets into the GPIO Peripheral block in bytes per 6.1 Register View -#define BCM2835_GPFSEL0 0x0000 ///< GPIO Function Select 0 -#define BCM2835_GPFSEL1 0x0004 ///< GPIO Function Select 1 -#define BCM2835_GPFSEL2 0x0008 ///< GPIO Function Select 2 -#define BCM2835_GPFSEL3 0x000c ///< GPIO Function Select 3 -#define BCM2835_GPFSEL4 0x0010 ///< GPIO Function Select 4 -#define BCM2835_GPFSEL5 0x0014 ///< GPIO Function Select 5 -#define BCM2835_GPSET0 0x001c ///< GPIO Pin Output Set 0 -#define BCM2835_GPSET1 0x0020 ///< GPIO Pin Output Set 1 -#define BCM2835_GPCLR0 0x0028 ///< GPIO Pin Output Clear 0 -#define BCM2835_GPCLR1 0x002c ///< GPIO Pin Output Clear 1 -#define BCM2835_GPLEV0 0x0034 ///< GPIO Pin Level 0 -#define BCM2835_GPLEV1 0x0038 ///< GPIO Pin Level 1 -#define BCM2835_GPEDS0 0x0040 ///< GPIO Pin Event Detect Status 0 -#define BCM2835_GPEDS1 0x0044 ///< GPIO Pin Event Detect Status 1 -#define BCM2835_GPREN0 0x004c ///< GPIO Pin Rising Edge Detect Enable 0 -#define BCM2835_GPREN1 0x0050 ///< GPIO Pin Rising Edge Detect Enable 1 -#define BCM2835_GPFEN0 0x0058 ///< GPIO Pin Falling Edge Detect Enable 0 -#define BCM2835_GPFEN1 0x005c ///< GPIO Pin Falling Edge Detect Enable 1 -#define BCM2835_GPHEN0 0x0064 ///< GPIO Pin High Detect Enable 0 -#define BCM2835_GPHEN1 0x0068 ///< GPIO Pin High Detect Enable 1 -#define BCM2835_GPLEN0 0x0070 ///< GPIO Pin Low Detect Enable 0 -#define BCM2835_GPLEN1 0x0074 ///< GPIO Pin Low Detect Enable 1 -#define BCM2835_GPAREN0 0x007c ///< GPIO Pin Async. Rising Edge Detect 0 -#define BCM2835_GPAREN1 0x0080 ///< GPIO Pin Async. Rising Edge Detect 1 -#define BCM2835_GPAFEN0 0x0088 ///< GPIO Pin Async. Falling Edge Detect 0 -#define BCM2835_GPAFEN1 0x008c ///< GPIO Pin Async. Falling Edge Detect 1 -#define BCM2835_GPPUD 0x0094 ///< GPIO Pin Pull-up/down Enable -#define BCM2835_GPPUDCLK0 0x0098 ///< GPIO Pin Pull-up/down Enable Clock 0 -#define BCM2835_GPPUDCLK1 0x009c ///< GPIO Pin Pull-up/down Enable Clock 1 - -/// \brief bcm2835PortFunction -/// Port function select modes for bcm2835_gpio_fsel() -typedef enum -{ - BCM2835_GPIO_FSEL_INPT = 0b000, ///< Input - BCM2835_GPIO_FSEL_OUTP = 0b001, ///< Output - BCM2835_GPIO_FSEL_ALT0 = 0b100, ///< Alternate function 0 - BCM2835_GPIO_FSEL_ALT1 = 0b101, ///< Alternate function 1 - BCM2835_GPIO_FSEL_ALT2 = 0b110, ///< Alternate function 2 - BCM2835_GPIO_FSEL_ALT3 = 0b111, ///< Alternate function 3 - BCM2835_GPIO_FSEL_ALT4 = 0b011, ///< Alternate function 4 - BCM2835_GPIO_FSEL_ALT5 = 0b010, ///< Alternate function 5 - BCM2835_GPIO_FSEL_MASK = 0b111 ///< Function select bits mask -} bcm2835FunctionSelect; - -/// \brief bcm2835PUDControl -/// Pullup/Pulldown defines for bcm2835_gpio_pud() -typedef enum -{ - BCM2835_GPIO_PUD_OFF = 0b00, ///< Off ? disable pull-up/down - BCM2835_GPIO_PUD_DOWN = 0b01, ///< Enable Pull Down control - BCM2835_GPIO_PUD_UP = 0b10 ///< Enable Pull Up control -} bcm2835PUDControl; - -/// 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 -#define BCM2835_PADS_GPIO_46_53 0x0034 ///< Pad control register for pads 46 to 53 - -/// Pad Control masks -#define BCM2835_PAD_PASSWRD (0x5A << 24) ///< Password to enable setting pad mask -#define BCM2835_PAD_SLEW_RATE_UNLIMITED 0x10 ///< Slew rate unlimited -#define BCM2835_PAD_HYSTERESIS_ENABLED 0x08 ///< Hysteresis enabled -#define BCM2835_PAD_DRIVE_2mA 0x00 ///< 2mA drive current -#define BCM2835_PAD_DRIVE_4mA 0x01 ///< 4mA drive current -#define BCM2835_PAD_DRIVE_6mA 0x02 ///< 6mA drive current -#define BCM2835_PAD_DRIVE_8mA 0x03 ///< 8mA drive current -#define BCM2835_PAD_DRIVE_10mA 0x04 ///< 10mA drive current -#define BCM2835_PAD_DRIVE_12mA 0x05 ///< 12mA drive current -#define BCM2835_PAD_DRIVE_14mA 0x06 ///< 14mA drive current -#define BCM2835_PAD_DRIVE_16mA 0x07 ///< 16mA drive current - -/// \brief bcm2835PadGroup -/// Pad group specification for bcm2835_gpio_pad() -typedef enum -{ - BCM2835_PAD_GROUP_GPIO_0_27 = 0, ///< Pad group for GPIO pads 0 to 27 - BCM2835_PAD_GROUP_GPIO_28_45 = 1, ///< Pad group for GPIO pads 28 to 45 - BCM2835_PAD_GROUP_GPIO_46_53 = 2 ///< Pad group for GPIO pads 46 to 53 -} bcm2835PadGroup; - -/// \brief GPIO Pin Numbers -/// -/// Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers. -/// These can be passed as a pin number to any function requiring a pin. -/// Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins -/// and some can adopt an alternate function. -/// RPi version 2 has some slightly different pinouts, and these are values RPI_V2_*. -/// At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively -/// When SPI0 is in use (ie after bcm2835_spi_begin()), pins 19, 21, 23, 24, 26 are dedicated to SPI -/// and cant be controlled independently -typedef enum -{ - RPI_GPIO_P1_03 = 0, ///< Version 1, Pin P1-03 - RPI_GPIO_P1_05 = 1, ///< Version 1, Pin P1-05 - RPI_GPIO_P1_07 = 4, ///< Version 1, Pin P1-07 - RPI_GPIO_P1_08 = 14, ///< Version 1, Pin P1-08, defaults to alt function 0 UART0_TXD - RPI_GPIO_P1_10 = 15, ///< Version 1, Pin P1-10, defaults to alt function 0 UART0_RXD - RPI_GPIO_P1_11 = 17, ///< Version 1, Pin P1-11 - RPI_GPIO_P1_12 = 18, ///< Version 1, Pin P1-12, can be PWM channel 0 in ALT FUN 5 - RPI_GPIO_P1_13 = 21, ///< Version 1, Pin P1-13 - RPI_GPIO_P1_15 = 22, ///< Version 1, Pin P1-15 - RPI_GPIO_P1_16 = 23, ///< Version 1, Pin P1-16 - RPI_GPIO_P1_18 = 24, ///< Version 1, Pin P1-18 - RPI_GPIO_P1_19 = 10, ///< Version 1, Pin P1-19, MOSI when SPI0 in use - RPI_GPIO_P1_21 = 9, ///< Version 1, Pin P1-21, MISO when SPI0 in use - RPI_GPIO_P1_22 = 25, ///< Version 1, Pin P1-22 - RPI_GPIO_P1_23 = 11, ///< Version 1, Pin P1-23, CLK when SPI0 in use - RPI_GPIO_P1_24 = 8, ///< Version 1, Pin P1-24, CE0 when SPI0 in use - RPI_GPIO_P1_26 = 7, ///< Version 1, Pin P1-26, CE1 when SPI0 in use - - // RPi Version 2 - RPI_V2_GPIO_P1_03 = 2, ///< Version 2, Pin P1-03 - RPI_V2_GPIO_P1_05 = 3, ///< Version 2, Pin P1-05 - RPI_V2_GPIO_P1_07 = 4, ///< Version 2, Pin P1-07 - RPI_V2_GPIO_P1_08 = 14, ///< Version 2, Pin P1-08, defaults to alt function 0 UART0_TXD - RPI_V2_GPIO_P1_10 = 15, ///< Version 2, Pin P1-10, defaults to alt function 0 UART0_RXD - RPI_V2_GPIO_P1_11 = 17, ///< Version 2, Pin P1-11 - RPI_V2_GPIO_P1_12 = 18, ///< Version 2, Pin P1-12, can be PWM channel 0 in ALT FUN 5 - RPI_V2_GPIO_P1_13 = 27, ///< Version 2, Pin P1-13 - RPI_V2_GPIO_P1_15 = 22, ///< Version 2, Pin P1-15 - RPI_V2_GPIO_P1_16 = 23, ///< Version 2, Pin P1-16 - RPI_V2_GPIO_P1_18 = 24, ///< Version 2, Pin P1-18 - RPI_V2_GPIO_P1_19 = 10, ///< Version 2, Pin P1-19, MOSI when SPI0 in use - RPI_V2_GPIO_P1_21 = 9, ///< Version 2, Pin P1-21, MISO when SPI0 in use - RPI_V2_GPIO_P1_22 = 25, ///< Version 2, Pin P1-22 - RPI_V2_GPIO_P1_23 = 11, ///< Version 2, Pin P1-23, CLK when SPI0 in use - RPI_V2_GPIO_P1_24 = 8, ///< Version 2, Pin P1-24, CE0 when SPI0 in use - RPI_V2_GPIO_P1_26 = 7, ///< Version 2, Pin P1-26, CE1 when SPI0 in use - - // RPi Version 2, new plug P5 - RPI_V2_GPIO_P5_03 = 28, ///< Version 2, Pin P5-03 - RPI_V2_GPIO_P5_04 = 29, ///< Version 2, Pin P5-04 - RPI_V2_GPIO_P5_05 = 30, ///< Version 2, Pin P5-05 - RPI_V2_GPIO_P5_06 = 31, ///< Version 2, Pin P5-06 - -} RPiGPIOPin; - -// Defines for SPI -// GPIO register offsets from BCM2835_SPI0_BASE. -// Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map -#define BCM2835_SPI0_CS 0x0000 ///< SPI Master Control and Status -#define BCM2835_SPI0_FIFO 0x0004 ///< SPI Master TX and RX FIFOs -#define BCM2835_SPI0_CLK 0x0008 ///< SPI Master Clock Divider -#define BCM2835_SPI0_DLEN 0x000c ///< SPI Master Data Length -#define BCM2835_SPI0_LTOH 0x0010 ///< SPI LOSSI mode TOH -#define BCM2835_SPI0_DC 0x0014 ///< SPI DMA DREQ Controls - -// Register masks for SPI0_CS -#define BCM2835_SPI0_CS_LEN_LONG 0x02000000 ///< Enable Long data word in Lossi mode if DMA_LEN is set -#define BCM2835_SPI0_CS_DMA_LEN 0x01000000 ///< Enable DMA mode in Lossi mode -#define BCM2835_SPI0_CS_CSPOL2 0x00800000 ///< Chip Select 2 Polarity -#define BCM2835_SPI0_CS_CSPOL1 0x00400000 ///< Chip Select 1 Polarity -#define BCM2835_SPI0_CS_CSPOL0 0x00200000 ///< Chip Select 0 Polarity -#define BCM2835_SPI0_CS_RXF 0x00100000 ///< RXF - RX FIFO Full -#define BCM2835_SPI0_CS_RXR 0x00080000 ///< RXR RX FIFO needs Reading ( full) -#define BCM2835_SPI0_CS_TXD 0x00040000 ///< TXD TX FIFO can accept Data -#define BCM2835_SPI0_CS_RXD 0x00020000 ///< RXD RX FIFO contains Data -#define BCM2835_SPI0_CS_DONE 0x00010000 ///< Done transfer Done -#define BCM2835_SPI0_CS_TE_EN 0x00008000 ///< Unused -#define BCM2835_SPI0_CS_LMONO 0x00004000 ///< Unused -#define BCM2835_SPI0_CS_LEN 0x00002000 ///< LEN LoSSI enable -#define BCM2835_SPI0_CS_REN 0x00001000 ///< REN Read Enable -#define BCM2835_SPI0_CS_ADCS 0x00000800 ///< ADCS Automatically Deassert Chip Select -#define BCM2835_SPI0_CS_INTR 0x00000400 ///< INTR Interrupt on RXR -#define BCM2835_SPI0_CS_INTD 0x00000200 ///< INTD Interrupt on Done -#define BCM2835_SPI0_CS_DMAEN 0x00000100 ///< DMAEN DMA Enable -#define BCM2835_SPI0_CS_TA 0x00000080 ///< Transfer Active -#define BCM2835_SPI0_CS_CSPOL 0x00000040 ///< Chip Select Polarity -#define BCM2835_SPI0_CS_CLEAR 0x00000030 ///< Clear FIFO Clear RX and TX -#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX -#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX -#define BCM2835_SPI0_CS_CPOL 0x00000008 ///< Clock Polarity -#define BCM2835_SPI0_CS_CPHA 0x00000004 ///< Clock Phase -#define BCM2835_SPI0_CS_CS 0x00000003 ///< Chip Select - -/// \brief bcm2835SPIBitOrder SPI Bit order -/// Specifies the SPI data bit ordering for bcm2835_spi_setBitOrder() -typedef enum -{ - BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, ///< LSB First - BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 ///< MSB First -}bcm2835SPIBitOrder; - -/// \brief SPI Data mode -/// Specify the SPI data mode to be passed to bcm2835_spi_setDataMode() -typedef enum -{ - BCM2835_SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0 - BCM2835_SPI_MODE1 = 1, ///< CPOL = 0, CPHA = 1 - BCM2835_SPI_MODE2 = 2, ///< CPOL = 1, CPHA = 0 - BCM2835_SPI_MODE3 = 3, ///< CPOL = 1, CPHA = 1 -}bcm2835SPIMode; - -/// \brief bcm2835SPIChipSelect -/// Specify the SPI chip select pin(s) -typedef enum -{ - // Can also use non-standard pins with this lib - BCM2835_SPI_CS0 = 0, ///< Chip Select 0 - BCM2835_SPI_CS1 = 1, ///< Chip Select 1 - BCM2835_SPI_CS2 = 2, ///< Chip Select 2 (ie pins CS1 and CS2 are asserted) - BCM2835_SPI_CS_NONE = 3, ///< No CS, control it yourself - - // Only GPIO > 3 can be used (to not interfere with the previous value just above ) - // Lucky we have plenty of theese pins - BCM2835_SPI_CS_GPIO4 = RPI_V2_GPIO_P1_07, /// BCM GPIO 4 - BCM2835_SPI_CS_GPIO17 = RPI_V2_GPIO_P1_11, /// BCM GPIO 17 - BCM2835_SPI_CS_GPIO18 = RPI_V2_GPIO_P1_12, /// BCM GPIO 18 - BCM2835_SPI_CS_GPIO22 = RPI_V2_GPIO_P1_15, /// BCM GPIO 22 - BCM2835_SPI_CS_GPIO23 = RPI_V2_GPIO_P1_16, /// BCM GPIO 23 - BCM2835_SPI_CS_GPIO24 = RPI_V2_GPIO_P1_18, /// BCM GPIO 24 - BCM2835_SPI_CS_GPIO25 = RPI_V2_GPIO_P1_22, /// BCM GPIO 25 - BCM2835_SPI_CS_GPIO28 = RPI_V2_GPIO_P5_03, /// BCM GPIO 28 - BCM2835_SPI_CS_GPIO29 = RPI_V2_GPIO_P5_04, /// BCM GPIO 29 - BCM2835_SPI_CS_GPIO30 = RPI_V2_GPIO_P5_05, /// BCM GPIO 30 - BCM2835_SPI_CS_GPIO31 = RPI_V2_GPIO_P5_06, /// BCM GPIO 31 - -} bcm2835SPIChipSelect; - -/// \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 -/// It is reported that (contrary to the documentation) any even divider may used. -/// The frequencies shown for each divider have been confirmed by measurement -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.5625MHz - 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 -} bcm2835SPIClockDivider; - -/// \brief bcm2835SPISpeed -/// Specifies the divider used to generate the SPI clock from the system clock. -/// Figures below give the clock speed instead of clock divider. -#define BCM2835_SPI_SPEED_64MHZ BCM2835_SPI_CLOCK_DIVIDER_4 -#define BCM2835_SPI_SPEED_32MHZ BCM2835_SPI_CLOCK_DIVIDER_8 -#define BCM2835_SPI_SPEED_16MHZ BCM2835_SPI_CLOCK_DIVIDER_16 -#define BCM2835_SPI_SPEED_8MHZ BCM2835_SPI_CLOCK_DIVIDER_32 -#define BCM2835_SPI_SPEED_4MHZ BCM2835_SPI_CLOCK_DIVIDER_64 -#define BCM2835_SPI_SPEED_2MHZ BCM2835_SPI_CLOCK_DIVIDER_128 -#define BCM2835_SPI_SPEED_1MHZ BCM2835_SPI_CLOCK_DIVIDER_256 -#define BCM2835_SPI_SPEED_512KHZ BCM2835_SPI_CLOCK_DIVIDER_512 -#define BCM2835_SPI_SPEED_256KHZ BCM2835_SPI_CLOCK_DIVIDER_1024 -#define BCM2835_SPI_SPEED_128KHZ BCM2835_SPI_CLOCK_DIVIDER_2048 -#define BCM2835_SPI_SPEED_64KHZ BCM2835_SPI_CLOCK_DIVIDER_4096 -#define BCM2835_SPI_SPEED_32KHZ BCM2835_SPI_CLOCK_DIVIDER_8192 -#define BCM2835_SPI_SPEED_16KHZ BCM2835_SPI_CLOCK_DIVIDER_16384 -#define BCM2835_SPI_SPEED_8KHZ BCM2835_SPI_CLOCK_DIVIDER_32768 - -// Defines for I2C -// GPIO register offsets from BCM2835_BSC*_BASE. -// Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map -#define BCM2835_BSC_C 0x0000 ///< BSC Master Control -#define BCM2835_BSC_S 0x0004 ///< BSC Master Status -#define BCM2835_BSC_DLEN 0x0008 ///< BSC Master Data Length -#define BCM2835_BSC_A 0x000c ///< BSC Master Slave Address -#define BCM2835_BSC_FIFO 0x0010 ///< BSC Master Data FIFO -#define BCM2835_BSC_DIV 0x0014 ///< BSC Master Clock Divider -#define BCM2835_BSC_DEL 0x0018 ///< BSC Master Data Delay -#define BCM2835_BSC_CLKT 0x001c ///< BSC Master Clock Stretch Timeout - -// Register masks for BSC_C -#define BCM2835_BSC_C_I2CEN 0x00008000 ///< I2C Enable, 0 = disabled, 1 = enabled -#define BCM2835_BSC_C_INTR 0x00000400 ///< Interrupt on RX -#define BCM2835_BSC_C_INTT 0x00000200 ///< Interrupt on TX -#define BCM2835_BSC_C_INTD 0x00000100 ///< Interrupt on DONE -#define BCM2835_BSC_C_ST 0x00000080 ///< Start transfer, 1 = Start a new transfer -#define BCM2835_BSC_C_CLEAR_1 0x00000020 ///< Clear FIFO Clear -#define BCM2835_BSC_C_CLEAR_2 0x00000010 ///< Clear FIFO Clear -#define BCM2835_BSC_C_READ 0x00000001 ///< Read transfer - -// Register masks for BSC_S -#define BCM2835_BSC_S_CLKT 0x00000200 ///< Clock stretch timeout -#define BCM2835_BSC_S_ERR 0x00000100 ///< ACK error -#define BCM2835_BSC_S_RXF 0x00000080 ///< RXF FIFO full, 0 = FIFO is not full, 1 = FIFO is full -#define BCM2835_BSC_S_TXE 0x00000040 ///< TXE FIFO full, 0 = FIFO is not full, 1 = FIFO is full -#define BCM2835_BSC_S_RXD 0x00000020 ///< RXD FIFO contains data -#define BCM2835_BSC_S_TXD 0x00000010 ///< TXD FIFO can accept data -#define BCM2835_BSC_S_RXR 0x00000008 ///< RXR FIFO needs reading (full) -#define BCM2835_BSC_S_TXW 0x00000004 ///< TXW FIFO needs writing (full) -#define BCM2835_BSC_S_DONE 0x00000002 ///< Transfer DONE -#define BCM2835_BSC_S_TA 0x00000001 ///< Transfer Active - -#define BCM2835_BSC_FIFO_SIZE 16 ///< BSC FIFO size - -/// \brief bcm2835I2CClockDivider -/// Specifies the divider used to generate the I2C clock from the system clock. -/// Clock divided is based on nominal base clock rate of 250MHz -typedef enum -{ - BCM2835_I2C_CLOCK_DIVIDER_2500 = 2500, ///< 2500 = 10us = 100 kHz - BCM2835_I2C_CLOCK_DIVIDER_626 = 626, ///< 622 = 2.504us = 399.3610 kHz - BCM2835_I2C_CLOCK_DIVIDER_150 = 150, ///< 150 = 60ns = 1.666 MHz (default at reset) - BCM2835_I2C_CLOCK_DIVIDER_148 = 148, ///< 148 = 59ns = 1.689 MHz -} bcm2835I2CClockDivider; - -/// \brief bcm2835I2CReasonCodes -/// Specifies the reason codes for the bcm2835_i2c_write and bcm2835_i2c_read functions. -typedef enum -{ - BCM2835_I2C_REASON_OK = 0x00, ///< Success - BCM2835_I2C_REASON_ERROR_NACK = 0x01, ///< Received a NACK - BCM2835_I2C_REASON_ERROR_CLKT = 0x02, ///< Received Clock Stretch Timeout - BCM2835_I2C_REASON_ERROR_DATA = 0x04, ///< Not all data is sent / received -} bcm2835I2CReasonCodes; - -// Defines for ST -// GPIO register offsets from BCM2835_ST_BASE. -// Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers -// The System Timer peripheral provides four 32-bit timer channels and a single 64-bit free running counter. -// BCM2835_ST_CLO is the System Timer Counter Lower bits register. -// The system timer free-running counter lower register is a read-only register that returns the current value -// of the lower 32-bits of the free running counter. -// BCM2835_ST_CHI is the System Timer Counter Upper bits register. -// The system timer free-running counter upper register is a read-only register that returns the current value -// of the upper 32-bits of the free running counter. -#define BCM2835_ST_CS 0x0000 ///< System Timer Control/Status -#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 -#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 -#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 -#define BCM2835_PWM1_USEFIFO 0x2000 ///< Data from FIFO -#define BCM2835_PWM1_REVPOLAR 0x1000 ///< Reverse polarity -#define BCM2835_PWM1_OFFSTATE 0x0800 ///< Ouput Off state -#define BCM2835_PWM1_REPEATFF 0x0400 ///< Repeat last value if FIFO empty -#define BCM2835_PWM1_SERIAL 0x0200 ///< Run in serial mode -#define BCM2835_PWM1_ENABLE 0x0100 ///< Channel Enable - -#define BCM2835_PWM0_MS_MODE 0x0080 ///< Run in Mark/Space mode -#define BCM2835_PWM_CLEAR_FIFO 0x0040 ///< Clear FIFO -#define BCM2835_PWM0_USEFIFO 0x0020 ///< Data from FIFO -#define BCM2835_PWM0_REVPOLAR 0x0010 ///< Reverse polarity -#define BCM2835_PWM0_OFFSTATE 0x0008 ///< Ouput Off state -#define BCM2835_PWM0_REPEATFF 0x0004 ///< Repeat last value if FIFO empty -#define BCM2835_PWM0_SERIAL 0x0002 ///< Run in serial mode -#define BCM2835_PWM0_ENABLE 0x0001 ///< Channel Enable - - -/// \brief bcm2835PWMClockDivider -/// Specifies the divider used to generate the PWM clock from the system clock. -/// Figures below give the divider, clock period and clock frequency. -/// Clock divided is based on nominal PWM base clock rate of 19.2MHz -/// The frequencies shown for each divider have been confirmed by measurement -typedef enum -{ - BCM2835_PWM_CLOCK_DIVIDER_32768 = 32768, ///< 32768 = 585Hz - BCM2835_PWM_CLOCK_DIVIDER_16384 = 16384, ///< 16384 = 1171.8Hz - BCM2835_PWM_CLOCK_DIVIDER_8192 = 8192, ///< 8192 = 2.34375kHz - BCM2835_PWM_CLOCK_DIVIDER_4096 = 4096, ///< 4096 = 4.6875kHz - BCM2835_PWM_CLOCK_DIVIDER_2048 = 2048, ///< 2048 = 9.375kHz - BCM2835_PWM_CLOCK_DIVIDER_1024 = 1024, ///< 1024 = 18.75kHz - BCM2835_PWM_CLOCK_DIVIDER_512 = 512, ///< 512 = 37.5kHz - BCM2835_PWM_CLOCK_DIVIDER_256 = 256, ///< 256 = 75kHz - BCM2835_PWM_CLOCK_DIVIDER_128 = 128, ///< 128 = 150kHz - BCM2835_PWM_CLOCK_DIVIDER_64 = 64, ///< 64 = 300kHz - BCM2835_PWM_CLOCK_DIVIDER_32 = 32, ///< 32 = 600.0kHz - BCM2835_PWM_CLOCK_DIVIDER_16 = 16, ///< 16 = 1.2MHz - BCM2835_PWM_CLOCK_DIVIDER_8 = 8, ///< 8 = 2.4MHz - BCM2835_PWM_CLOCK_DIVIDER_4 = 4, ///< 4 = 4.8MHz - BCM2835_PWM_CLOCK_DIVIDER_2 = 2, ///< 2 = 9.6MHz, fastest you can get - 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) -#define delayMicroseconds(x) bcm2835_delayMicroseconds(x) -#define millis() bcm2835_millis() -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - /// \defgroup init Library initialisation and management - /// These functions allow you to intialise and control the bcm2835 library - /// @{ - - /// Initialise the library by opening /dev/mem and getting pointers to the - /// internal memory for BCM 2835 device registers. You must call this (successfully) - /// before calling any other - /// functions in this library (except bcm2835_set_debug). - /// If bcm2835_init() fails by returning 0, - /// calling any other function may result in crashes or other failures. - /// Prints messages to stderr in case of errors. - /// \return 1 if successful else 0 - extern int bcm2835_init(void); - - /// Close the library, deallocating any allocated memory and closing /dev/mem - /// \return 1 if successful else 0 - extern int bcm2835_close(void); - - /// Sets the debug level of the library. - /// A value of 1 prevents mapping to /dev/mem, and makes the library print out - /// what it would do, rather than accessing the GPIO registers. - /// A value of 0, the default, causes normal operation. - /// Call this before calling bcm2835_init(); - /// \param[in] debug The new debug level. 1 means debug - extern void bcm2835_set_debug(uint8_t debug); - - /// @} // end of init - - /// \defgroup lowlevel Low level register access - /// These functions provide low level register access, and should not generally - /// need to be used - /// - /// @{ - - /// Reads 32 bit value from a peripheral address - /// The read is done twice, and is therefore always safe in terms of - /// manual section 1.3 Peripheral access precautions for correct memory ordering - /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. - /// \return the value read from the 32 bit register - /// \sa Physical Addresses - extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr); - - - /// Reads 32 bit value from a peripheral address without the read barrier - /// You should only use this when your code has previously called bcm2835_peri_read() - /// within the same peripheral, and no other peripheral access has occurred since. - /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. - /// \return the value read from the 32 bit register - /// \sa Physical Addresses - extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr); - - - /// Writes 32 bit value from a peripheral address - /// The write is done twice, and is therefore always safe in terms of - /// manual section 1.3 Peripheral access precautions for correct memory ordering - /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. - /// \param[in] value The 32 bit value to write - /// \sa Physical Addresses - extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value); - - /// Writes 32 bit value from a peripheral address without the write barrier - /// You should only use this when your code has previously called bcm2835_peri_write() - /// within the same peripheral, and no other peripheral access has occurred since. - /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. - /// \param[in] value The 32 bit value to write - /// \sa Physical Addresses - extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); - - /// Alters a number of bits in a 32 peripheral regsiter. - /// It reads the current valu and then alters the bits deines as 1 in mask, - /// according to the bit value in value. - /// All other bits that are 0 in the mask are unaffected. - /// Use this to alter a subset of the bits in a register. - /// The write is done twice, and is therefore always safe in terms of - /// manual section 1.3 Peripheral access precautions for correct memory ordering - /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. - /// \param[in] value The 32 bit value to write, masked in by mask. - /// \param[in] mask Bitmask that defines the bits that will be altered in the register. - /// \sa Physical Addresses - extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); - /// @} // end of lowlevel - - /// \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. - /// @{ - - /// Sets the Function Select register for the given pin, which configures - /// the pin as Input, Output or one of the 6 alternate functions. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - /// \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect - extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); - - /// Sets the specified pin output to - /// HIGH. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - /// \sa bcm2835_gpio_write() - extern void bcm2835_gpio_set(uint8_t pin); - - /// Sets the specified pin output to - /// LOW. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - /// \sa bcm2835_gpio_write() - extern void bcm2835_gpio_clr(uint8_t pin); - - /// Sets any of the first 32 GPIO output pins specified in the mask to - /// HIGH. - /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) - /// \sa bcm2835_gpio_write_multi() - extern void bcm2835_gpio_set_multi(uint32_t mask); - - /// Sets any of the first 32 GPIO output pins specified in the mask to - /// LOW. - /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) - /// \sa bcm2835_gpio_write_multi() - extern void bcm2835_gpio_clr_multi(uint32_t mask); - - /// Reads the current level on the specified - /// pin and returns either HIGH or LOW. Works whether or not the pin - /// is an input or an output. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - /// \return the current level either HIGH or LOW - extern uint8_t bcm2835_gpio_lev(uint8_t pin); - - /// Event Detect Status. - /// Tests whether the specified pin has detected a level or edge - /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), - /// bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). - /// Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - /// \return HIGH if the event detect status for the given pin is true. - extern uint8_t bcm2835_gpio_eds(uint8_t pin); - - /// Sets the Event Detect Status register for a given pin to 1, - /// which has the effect of clearing the flag. Use this afer seeing - /// an Event Detect Status on the pin. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_set_eds(uint8_t pin); - - /// Enable Rising Edge Detect Enable for the specified pin. - /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. - /// The GPRENn registers use - /// synchronous edge detection. This means the input signal is sampled using the - /// system clock and then it is looking for a ?011? pattern on the sampled signal. This - /// has the effect of suppressing glitches. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_ren(uint8_t pin); - - /// Disable Rising Edge Detect Enable for the specified pin. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_clr_ren(uint8_t pin); - - /// Enable Falling Edge Detect Enable for the specified pin. - /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. - /// The GPRENn registers use - /// synchronous edge detection. This means the input signal is sampled using the - /// system clock and then it is looking for a ?100? pattern on the sampled signal. This - /// has the effect of suppressing glitches. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_fen(uint8_t pin); - - /// Disable Falling Edge Detect Enable for the specified pin. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_clr_fen(uint8_t pin); - - /// Enable High Detect Enable for the specified pin. - /// When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_hen(uint8_t pin); - - /// Disable High Detect Enable for the specified pin. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_clr_hen(uint8_t pin); - - /// Enable Low Detect Enable for the specified pin. - /// When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_len(uint8_t pin); - - /// Disable Low Detect Enable for the specified pin. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_clr_len(uint8_t pin); - - /// Enable Asynchronous Rising Edge Detect Enable for the specified pin. - /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. - /// Asynchronous means the incoming signal is not sampled by the system clock. As such - /// rising edges of very short duration can be detected. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_aren(uint8_t pin); - - /// Disable Asynchronous Rising Edge Detect Enable for the specified pin. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_clr_aren(uint8_t pin); - - /// Enable Asynchronous Falling Edge Detect Enable for the specified pin. - /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. - /// Asynchronous means the incoming signal is not sampled by the system clock. As such - /// falling edges of very short duration can be detected. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_afen(uint8_t pin); - - /// Disable Asynchronous Falling Edge Detect Enable for the specified pin. - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - extern void bcm2835_gpio_clr_afen(uint8_t pin); - - /// Sets the Pull-up/down register for the given pin. This is - /// 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 - /// \sa bcm2835_gpio_set_pud() - extern void bcm2835_gpio_pud(uint8_t pud); - - /// Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. - /// \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. - /// \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. - /// \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. - /// \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 - /// is automatically included. - extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); - - /// Delays for the specified number of milliseconds. - /// Uses nanosleep(), and therefore does not use CPU until the time is up. - /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): - /// If the interval specified in req is not an exact multiple of the granularity - /// underlying clock (see time(7)), then the interval will be - /// rounded up to the next multiple. Furthermore, after the sleep completes, - /// there may still be a delay before the CPU becomes free to once - /// again execute the calling thread. - /// \param[in] millis Delay in milliseconds - extern void bcm2835_delay (unsigned int millis); - - /// Delays for the specified number of microseconds. - /// Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers, - /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): - /// If the interval specified in req is not an exact multiple of the granularity - /// underlying clock (see time(7)), then the interval will be - /// rounded up to the next multiple. Furthermore, after the sleep completes, - /// there may still be a delay before the CPU becomes free to once - /// again execute the calling thread. - /// For times less than about 450 microseconds, uses a busy wait on the System Timer. - /// It is reported that a delay of 0 microseconds on RaspberryPi will in fact - /// result in a delay of about 80 microseconds. Your mileage may vary. - /// \param[in] micros Delay in microseconds - extern void bcm2835_delayMicroseconds (uint64_t micros); - - /// Indicate the number of milliseconds since startup of PI - /// This function is like the Arduino millis function - /// \return Number of milliseconds - extern unsigned int bcm2835_millis(void); - - /// Sets the output state of the specified pin - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. - extern void bcm2835_gpio_write(uint8_t pin, uint8_t on); - - /// Sets any of the first 32 GPIO output pins specified in the mask to the state given by on - /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) - /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. - extern void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on); - - /// Sets the first 32 GPIO output pins specified in the mask to the value given by value - /// \param[in] value values required for each bit masked in by mask, eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) - /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) - extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask); - - /// Sets the Pull-up/down mode for the specified pin. This is more convenient than - /// clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk(). - /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl - extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); - - /// @} - - /// \defgroup spi SPI access - /// These functions let you use SPI0 (Serial Peripheral Interface) to - /// interface with an external SPI device. - /// @{ - - /// Start SPI operations. - /// Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) - /// to alternate function ALT0, which enables those pins for SPI interface. - /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. - /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect - /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect - /// by default PI hardware driven using CE0 - /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to - /// their default functions - /// \sa bcm2835_spi_end() - extern void bcm2835_spi_begin(); - - /// End SPI operations. - /// SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) - /// are returned to their default INPUT behaviour. - extern void bcm2835_spi_end(void); - - /// Sets the SPI custom Chip Select pin to correct level - /// Defaults to - /// \param[in] level The desired level, LOW or HIGH, - extern void bcm2835_spi_setChipSelect(uint8_t level); - - /// Sets the SPI bit order - /// NOTE: has no effect. Not supported by SPI0. - /// Defaults to - /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, - /// see \ref bcm2835SPIBitOrder - extern void bcm2835_spi_setBitOrder(uint8_t order); - - /// Sets the SPI clock divider and therefore the - /// SPI clock speed. - /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, - /// see \ref bcm2835SPIClockDivider - extern void bcm2835_spi_setClockDivider(uint16_t divider); - - /// Sets the SPI data mode - /// Sets the clock polariy and phase - /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, - /// see \ref bcm2835SPIMode - extern void bcm2835_spi_setDataMode(uint8_t mode); - - /// Sets the chip select pin(s) - /// When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the - /// transfer. - /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. - /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect - /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect - extern void bcm2835_spi_chipSelect(uint8_t cs); - - /// Sets the chip select pin polarity for a given pin - /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) - /// will be asserted to the - /// value given by active. When transfers are not happening, the chip select pin(s) - /// return to the complement (inactive) value. - /// \param[in] cs The chip select pin to affect - /// \param[in] active Whether the chip select pin is to be active HIGH - extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); - - /// Transfers one byte 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] value The 8 bit data byte to write to MOSI - /// \return The 8 bit byte simultaneously read from MISO - /// \sa bcm2835_spi_transfern() - extern uint8_t bcm2835_spi_transfer(uint8_t value); - - /// Transfers any number of bytes 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 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 - /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual - /// \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 - /// \sa bcm2835_spi_transfer() - extern void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len); - - /// Transfers any number of bytes to and from the currently selected SPI slave - /// using bcm2835_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_spi_transfer() - extern void bcm2835_spi_transfern(char* buf, uint32_t len); - - /// Transfers any number of bytes to the currently selected SPI slave. - /// 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 tbuf buffer, and the number of bytes to send - extern void bcm2835_spi_writenb(char* buf, uint32_t len); - - /// @} - - /// \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. - /// @{ - - /// Start I2C operations. - /// Forces RPi I2C pins P1-03 (SDA) and P1-05 (SCL) - /// to alternate function ALT0, which enables those pins for I2C interface. - /// You should call bcm2835_i2c_end() when all I2C functions are complete to return the pins to - /// their default functions - /// \sa bcm2835_i2c_end() - extern void bcm2835_i2c_begin(void); - - /// End I2C operations. - /// I2C pins P1-03 (SDA) and P1-05 (SCL) - /// are returned to their default INPUT behaviour. - extern void bcm2835_i2c_end(void); - - /// Sets the I2C slave address. - /// \param[in] addr The I2C slave address. - extern void bcm2835_i2c_setSlaveAddress(uint8_t addr); - - /// Sets the I2C clock divider and therefore the I2C clock speed. - /// \param[in] divider The desired I2C clock divider, one of BCM2835_I2C_CLOCK_DIVIDER_*, - /// see \ref bcm2835I2CClockDivider - extern void bcm2835_i2c_setClockDivider(uint16_t divider); - - /// Sets the I2C clock divider by converting the baudrate parameter to - /// the equivalent I2C clock divider. ( see \sa bcm2835_i2c_setClockDivider) - /// For the I2C standard 100khz you would set baudrate to 100000 - /// The use of baudrate corresponds to its use in the I2C kernel device - /// driver. (Of course, bcm2835 has nothing to do with the kernel driver) - extern void bcm2835_i2c_set_baudrate(uint32_t baudrate); - - /// Transfers any number of bytes to the currently selected I2C slave. - /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) - /// \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. - /// \return reason see \ref bcm2835I2CReasonCodes - extern uint8_t bcm2835_i2c_write(const char * buf, uint32_t len); - - /// Transfers any number of bytes from the currently selected I2C slave. - /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) - /// \param[in] buf Buffer of bytes to receive. - /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. - /// \return reason see \ref bcm2835I2CReasonCodes - extern uint8_t bcm2835_i2c_read(char* buf, uint32_t len); - - /// Allows reading from I2C slaves that require a repeated start (without any prior stop) - /// to read after the required slave register has been set. For example, the popular - /// MPL3115A2 pressure and temperature sensor. Note that your device must support or - /// require this mode. If your device does not require this mode then the standard - /// combined: - /// \sa bcm2835_i2c_write - /// \sa bcm2835_i2c_read - /// are a better choice. - /// Will read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress - /// \param[in] regaddr Buffer containing the slave register you wish to read from. - /// \param[in] buf Buffer of bytes to receive. - /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. - /// \return reason see \ref bcm2835I2CReasonCodes - extern uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len); - - /// Allows sending an arbitrary number of bytes to I2C slaves before issuing a repeated - /// start (with no prior stop) and reading a response. - /// Necessary for devices that require such behavior, such as the MLX90620. - /// Will write to and read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress - /// \param[in] cmds Buffer containing the bytes to send before the repeated start condition. - /// \param[in] cmds_len Number of bytes to send from cmds buffer - /// \param[in] buf Buffer of bytes to receive. - /// \param[in] buf_len Number of bytes to receive in the buf buffer. - /// \return reason see \ref bcm2835I2CReasonCodes - extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len); - - /// @} - - /// \defgroup st System Timer access - /// Allows access to and delays using the System Timer Counter. - /// @{ - - /// Read the System Timer Counter register. - /// \return the value read from the System Timer Counter Lower 32 bits register - extern uint64_t bcm2835_st_read(void); - - /// Delays for the specified number of microseconds with offset. - /// \param[in] offset_micros Offset in microseconds - /// \param[in] micros Delay in microseconds - extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); - - /// @} - - /// \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 - /// documentation on the Main Page. - /// @{ - - /// Sets the PWM clock divisor, - /// to control the basic PWM pulse widths. - /// \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common - /// values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider - extern void bcm2835_pwm_set_clock(uint32_t divisor); - - /// Sets the mode of the given PWM channel, - /// allowing you to control the PWM mode and enable/disable that channel - /// \param[in] channel The PWM channel. 0 or 1. - /// \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode. - /// \param[in] enabled Set true to enable this channel and produce PWM pulses. - extern void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled); - - /// Sets the maximum range of the PWM output. - /// The data value can vary between 0 and this range to control PWM output - /// \param[in] channel The PWM channel. 0 or 1. - /// \param[in] range The maximum value permitted for DATA. - extern void bcm2835_pwm_set_range(uint8_t channel, uint32_t range); - - /// Sets the PWM pulse ratio to emit to DATA/RANGE, - /// where RANGE is set by bcm2835_pwm_set_range(). - /// \param[in] channel The PWM channel. 0 or 1. - /// \param[in] data Controls the PWM output ratio as a fraction of the range. - /// Can vary from 0 to RANGE. - extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data); - - /// @} -#ifdef __cplusplus -} -#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. diff --git a/RF24/RPi/RF24/examples/Makefile b/RF24/RPi/RF24/examples/Makefile deleted file mode 100644 index 73da217..0000000 --- a/RF24/RPi/RF24/examples/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -############################################################################# -# -# Makefile for librf24 examples on Raspberry Pi -# -# License: GPL (General Public License) -# Author: gnulnulf -# Date: 2013/02/07 (version 1.0) -# -# Description: -# ------------ -# use make all and make install to install the examples -# You can change the install directory by editing the prefix line -# -prefix := /usr/local - -# The recommended compiler flags for the Raspberry Pi -CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -#CCFLAGS= - -# define all programs -#PROGRAMS = scanner pingtest gettingstarted -PROGRAMS = gettingstarted gettingstarted_call_response transfer pingpair_dyn -SOURCES = ${PROGRAMS:=.cpp} - -all: ${PROGRAMS} - -${PROGRAMS}: ${SOURCES} - g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ - -clean: - rm -rf $(PROGRAMS) - -install: all - test -d $(prefix) || mkdir $(prefix) - test -d $(prefix)/bin || mkdir $(prefix)/bin - for prog in $(PROGRAMS); do \ - install -m 0755 $$prog $(prefix)/bin; \ - done - -.PHONY: install diff --git a/RF24/RPi/RF24/examples/extra/Makefile b/RF24/RPi/RF24/examples/extra/Makefile deleted file mode 100644 index c313b5b..0000000 --- a/RF24/RPi/RF24/examples/extra/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -############################################################################# -# -# Makefile for librf24 examples on Raspberry Pi -# -# License: GPL (General Public License) -# Author: gnulnulf -# Date: 2013/02/07 (version 1.0) -# -# Description: -# ------------ -# use make all and make install to install the examples -# You can change the install directory by editing the prefix line -# -prefix := /usr/local - -# The recommended compiler flags for the Raspberry Pi -CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -#CCFLAGS= - -# define all programs -PROGRAMS = rpi-hub scanner - -SOURCES = ${PROGRAMS:=.cpp} - -all: ${PROGRAMS} - -${PROGRAMS}: ${SOURCES} - g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ - -clean: - rm -rf $(PROGRAMS) - -install: all - test -d $(prefix) || mkdir $(prefix) - test -d $(prefix)/bin || mkdir $(prefix)/bin - for prog in $(PROGRAMS); do \ - install -m 0755 $$prog $(prefix)/bin; \ - done - -.PHONY: install diff --git a/RF24/RPi/RF24/examples/extra/rpi-hub.cpp b/RF24/RPi/RF24/examples/extra/rpi-hub.cpp deleted file mode 100644 index 4785148..0000000 --- a/RF24/RPi/RF24/examples/extra/rpi-hub.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * - * Filename : rpi-hub.cpp - * - * This program makes the RPi as a hub listening to all six pipes from the remote sensor nodes ( usually Arduino ) - * and will return the packet back to the sensor on pipe0 so that the sender can calculate the round trip delays - * when the payload matches. - * - * I encounter that at times, it also receive from pipe7 ( or pipe0 ) with content of FFFFFFFFF that I will not sent - * back to the sender - * - * Refer to RF24/examples/rpi_hub_arduino/ for the corresponding Arduino sketches to work with this code. - * - * - * CE is not used and CSN is GPIO25 (not pinout) - * - * Refer to RPi docs for GPIO numbers - * - * Author : Stanley Seow - * e-mail : stanleyseow@gmail.com - * date : 6th Mar 2013 - * - * 03/17/2013 : Charles-Henri Hallard (http://hallard.me) - * Modified to use with Arduipi board http://hallard.me/arduipi - * Changed to use modified bcm2835 and RF24 library - * - * - */ - -#include -#include -#include "../RF24.h" - -using namespace std; - -// Radio pipe addresses for the 2 nodes to communicate. -// First pipe is for writing, 2nd, 3rd, 4th, 5th & 6th is for reading... -const uint64_t pipes[6] = - { 0xF0F0F0F0D2LL, 0xF0F0F0F0E1LL, - 0xF0F0F0F0E2LL, 0xF0F0F0F0E3LL, - 0xF0F0F0F0F1, 0xF0F0F0F0F2 - }; - -// CE Pin, CSN Pin, SPI Speed - -// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); - -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); - - -int main(int argc, char** argv) -{ - uint8_t len; - - // Refer to RF24.h or nRF24L01 DS for settings - radio.begin(); - radio.enableDynamicPayloads(); - radio.setAutoAck(1); - radio.setRetries(15,15); - radio.setDataRate(RF24_1MBPS); - radio.setPALevel(RF24_PA_MAX); - radio.setChannel(76); - radio.setCRCLength(RF24_CRC_16); - - // Open 6 pipes for readings ( 5 plus pipe0, also can be used for reading ) - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - radio.openReadingPipe(2,pipes[2]); - radio.openReadingPipe(3,pipes[3]); - radio.openReadingPipe(4,pipes[4]); - radio.openReadingPipe(5,pipes[5]); - - // - // Start listening - // - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - radio.printDetails(); - - printf("Output below : \n"); - delay(1); - - while(1) - { - char receivePayload[32]; - uint8_t pipe = 1; - - // Start listening - radio.startListening(); - - while ( radio.available(&pipe) ) - { - len = radio.getDynamicPayloadSize(); - radio.read( receivePayload, len ); - - // Display it on screen - printf("Recv: size=%i payload=%s pipe=%i",len,receivePayload,pipe); - - // Send back payload to sender - radio.stopListening(); - - // if pipe is 7, do not send it back - if ( pipe != 7 ) - { - radio.write(receivePayload,len); - receivePayload[len]=0; - printf("\t Send: size=%i payload=%s pipe:%i\n",len,receivePayload,pipe); - } - else - { - printf("\n"); - } - - pipe++; - - // reset pipe to 0 - if ( pipe > 6 ) - pipe = 0; - } - - delayMicroseconds(20); - } - - return 0; -} - diff --git a/RF24/RPi/RF24/examples/extra/scanner.cpp b/RF24/RPi/RF24/examples/extra/scanner.cpp deleted file mode 100644 index 1390495..0000000 --- a/RF24/RPi/RF24/examples/extra/scanner.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - - - 03/17/2013 : Charles-Henri Hallard (http://hallard.me) - Modified to use with Arduipi board http://hallard.me/arduipi - Changed to use modified bcm2835 and RF24 library - - */ - -/** - * Channel scanner - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -#include -#include -#include "../RF24.h" - -using namespace std; - -// -// Hardware configuration -// - -// CE Pin, CSN Pin, SPI Speed - -// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); - -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); - - -// -// Channel info -// -//const uint8_t num_channels = 128; -const uint8_t num_channels = 120; -uint8_t values[num_channels]; - - -const int num_reps = 100; -int reset_array=0; - - -int main(int argc, char** argv) -{ - // - // Print preamble - // - - //Serial.begin(57600); - //printf_begin(); - printf("RF24/examples/scanner/\n"); - - // - // Setup and configure rf radio - // - radio.begin(); - - radio.setAutoAck(false); - - // Get into standby mode - radio.startListening(); - radio.stopListening(); - - radio.printDetails(); - - // Print out header, high then low digit - int i = 0; - - while ( i < num_channels ) - { - printf("%x",i>>4); - ++i; - } - printf("\n"); - - i = 0; - while ( i < num_channels ) - { - printf("%x",i&0xf); - ++i; - } - printf("\n"); - - // forever loop - while(1) - { - if ( reset_array == 1 ) - { - // Clear measurement values - memset(values,0,sizeof(values)); - printf("\n"); - } - - // Scan all channels num_reps times - int i = num_channels; - while (i--) - { - // Select this channel - radio.setChannel(i); - - // Listen for a little - radio.startListening(); - delayMicroseconds(128); - //radio.stopListening(); - - // Did we get a carrier? - if ( radio.testCarrier() ) - ++values[i]; - if ( values[i] == 0xf ) - { - reset_array = 2; - } - radio.stopListening(); - } - - // Print out channel measurements, clamped to a single hex digit - i = 0; - while ( i < num_channels ) - { - printf("%x",min(0xf,(values[i]&0xf))); - ++i; - } - - printf("\n"); - } - - return 0; -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24/RPi/RF24/examples/gettingstarted.cpp b/RF24/RPi/RF24/examples/gettingstarted.cpp deleted file mode 100644 index 5539c4d..0000000 --- a/RF24/RPi/RF24/examples/gettingstarted.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - - 03/17/2013 : Charles-Henri Hallard (http://hallard.me) - Modified to use with Arduipi board http://hallard.me/arduipi - Changed to use modified bcm2835 and RF24 library -TMRh20 2014 - Updated to work with optimized RF24 Arduino library - - */ - -/** - * Example RF Radio Ping Pair - * - * This is an example of how to use the RF24 class on RPi, communicating to an Arduino running - * the GettingStarted sketch. - */ - -#include -#include -#include -#include -#include "./RF24.h" - -using namespace std; -// -// Hardware configuration -// - -// CE Pin, CSN Pin, SPI Speed - -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); - - - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; - -bool role_ping_out = 1, role_pong_back = 0; -bool role = 0; - -int main(int argc, char** argv){ - - - // Print preamble: - printf("RF24/examples/pingtest/\n"); - - // Setup and configure rf radio - radio.begin(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); - - // Dump the configuration of the rf unit for debugging - radio.printDetails(); - - -/********* Role chooser ***********/ - - printf("\n ************ Role Setup ***********\n"); - string input = ""; - char myChar = {0}; - cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit) \n>"; - getline(cin,input); - - if(input.length() == 1) { - myChar = input[0]; - if(myChar == '0'){ - cout << "Role: Pong Back, awaiting transmission " << endl << endl; - }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; - role = role_ping_out; - } - } -/***********************************/ - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - - if ( role == role_ping_out ) { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } else { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - radio.startListening(); - - } - - // forever loop - while (1) - { - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - - printf("Now sending...\n"); - unsigned long time = millis(); - - bool ok = radio.write( &time, sizeof(unsigned long) ); - - if (!ok){ - printf("failed.\n"); - } - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) { - // by bcatalin » Thu Feb 14, 2013 11:26 am - //delay(5); //add a small delay to let radio.available to check payload - if (millis() - started_waiting_at > 200 ) - timeout = true; - } - - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n",got_time,millis()-got_time); - } - - // Try again 1s later - // delay(1000); - sleep(1); - - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - //printf("Check available...\n"); - //delay(3); - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - - - // Fetch the payload, and see if this was the last one. - radio.read( &got_time, sizeof(unsigned long) ); - - radio.stopListening(); - //delay(1); - // Seem to need a delay, or the RPi is too quick - radio.write( &got_time, sizeof(unsigned long) ); - - //delay(1); - // Now, resume listening so we catch the next packets. - radio.startListening(); - //delay(1); - // Spew it - printf("Got payload(%d) %lu...\n",sizeof(unsigned long), got_time); - } - //delay(5); - } - } // forever loop - - return 0; -} - diff --git a/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp b/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp deleted file mode 100644 index 19a16cc..0000000 --- a/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* -TMRh20 2014 - Updated to work with optimized RF24 Arduino library -*/ - - -/** - * Example for efficient call-response using ack-payloads - * - * This example continues to make use of all the normal functionality of the radios including - * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. - * This allows very fast call-response communication, with the responding radio never having to - * switch out of Primary Receiver mode to send back a payload, but having the option to switch to - * primary transmitter if wanting to initiate communication instead of respond to a commmunication. - */ - -#include -#include -#include -#include -#include "./RF24.h" - -using namespace std; - -// -// Hardware configuration -// - -// CE Pin, CSN Pin, SPI Speed - -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); - - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; - -bool role_ping_out = 1, role_pong_back = 0, role = 0; - -uint8_t counter = 1; // A single byte to keep track of the data being sent back and forth - - -int main(int argc, char** argv){ - - - printf("RF24/examples/gettingstarted_call_response\n"); - - radio.begin(); - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.enableAckPayload(); // Allow optional ack payloads - radio.setRetries(1,15); // Smallest time between retries, max no. of retries - radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed - //radio.powerUp(); - radio.printDetails(); // Dump the configuration of the rf unit for debugging - - -/********* Role chooser ***********/ - - printf("\n ************ Role Setup ***********\n"); - string input = ""; - char myChar = {0}; - cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit)\n>"; - getline(cin,input); - - if(input.length() == 1) { - myChar = input[0]; - if(myChar == '0'){ - cout << "Role: Pong Back, awaiting transmission " << endl << endl; - }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; - role = role_ping_out; - } - } -/***********************************/ - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - - if ( role == role_ping_out ) { - radio.openWritingPipe(addresses[0]); - radio.openReadingPipe(1,addresses[1]); - }else{ - radio.openWritingPipe(addresses[1]); - radio.openReadingPipe(1,addresses[0]); - radio.startListening(); - } - -// forever loop -while (1){ - - -/****************** Ping Out Role ***************************/ - - if (role == role_ping_out){ // Radio is in ping mode - - uint8_t gotByte; // Initialize a variable for the incoming response - - radio.stopListening(); // First, stop listening so we can talk. - printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload - unsigned long time = millis(); // Record the current microsecond count - - if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio - if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank - printf("Got blank response. round-trip delay: %lu ms\n\r",millis()-time); - }else{ - while(radio.available() ){ // If an ack with payload was received - radio.read( &gotByte, 1 ); // Read it, and display the response time - printf("Got response %d, round-trip delay: %lu ms\n\r",gotByte,millis()-time); - counter++; // Increment the counter variable - } - } - - }else{ printf("Sending failed.\n\r"); } // If no ack response, sending failed - - sleep(1); // Try again later - } - -/****************** Pong Back Role ***************************/ - - if ( role == role_pong_back ) { - uint8_t pipeNo, gotByte; // Declare variables for the pipe and the byte received - while( radio.available(&pipeNo)){ // Read all available payloads - radio.read( &gotByte, 1 ); - // Since this is a call-response. Respond directly with an ack payload. - // Ack payloads are much more efficient than switching to transmit mode to respond to a call - radio.writeAckPayload(pipeNo,&gotByte, 1 ); // This can be commented out to send empty payloads. - printf("Sent response %d \n\r", gotByte); - } - } - -} //while 1 -} //main - - diff --git a/RF24/RPi/RF24/examples/pingpair_dyn.cpp b/RF24/RPi/RF24/examples/pingpair_dyn.cpp deleted file mode 100644 index 09f94e6..0000000 --- a/RF24/RPi/RF24/examples/pingpair_dyn.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - TMRh20 2014 - Optimized RF24 Library Fork -*/ - -/** - * Example using Dynamic Payloads - * - * This is an example of how to use payloads of a varying (dynamic) size. - */ - -#include -#include -#include -#include -#include "./RF24.h" - - -using namespace std; -// -// Hardware configuration -// - -// CE Pin, CSN Pin, SPI Speed - -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); - - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -bool role_ping_out = 1, role_pong_back = 0; -bool role = 0; - -const int min_payload_size = 4; -const int max_payload_size = 32; -const int payload_size_increments_by = 1; -int next_payload_size = min_payload_size; - -char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char - -int main(int argc, char** argv){ - - - // Print preamble: - printf("RF24/examples/pingpair_dyn/\n"); - - // Setup and configure rf radio - radio.begin(); - radio.enableDynamicPayloads(); - radio.setRetries(5,15); - radio.printDetails(); - - -/********* Role chooser ***********/ - - printf("\n ************ Role Setup ***********\n"); - string input = ""; - char myChar = {0}; - cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit) \n>"; - getline(cin,input); - - if(input.length() == 1) { - myChar = input[0]; - if(myChar == '0'){ - cout << "Role: Pong Back, awaiting transmission " << endl << endl; - }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; - role = role_ping_out; - } - } -/***********************************/ - - if ( role == role_ping_out ) { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } else { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - radio.startListening(); - } - - -// forever loop - while (1) - { - -if (role == role_ping_out) - { - // The payload will always be the same, what will change is how much of it we send. - static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - printf("Now sending length %i...",next_payload_size); - radio.write( send_payload, next_payload_size ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 500 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - uint8_t len = radio.getDynamicPayloadSize(); - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got response size=%i value=%s\n\r",len,receive_payload); - } - - // Update size for next time. - next_payload_size += payload_size_increments_by; - if ( next_payload_size > max_payload_size ) - next_payload_size = min_payload_size; - - // Try again 1s later - delay(100); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - uint8_t len; - - while (radio.available()) - { - // Fetch the payload, and see if this was the last one. - len = radio.getDynamicPayloadSize(); - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got payload size=%i value=%s\n\r",len,receive_payload); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( receive_payload, len ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -} - - diff --git a/RF24/RPi/RF24/examples/transfer.cpp b/RF24/RPi/RF24/examples/transfer.cpp deleted file mode 100644 index 9d55470..0000000 --- a/RF24/RPi/RF24/examples/transfer.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* -TMRh20 2014 - - 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. - */ - -/** General Data Transfer Rate Test - * This example demonstrates basic data transfer functionality with the - updated library. This example will display the transfer rates acheived using - the slower form of high-speed transfer using blocking-writes. - */ - -#include -#include -#include -#include -#include "./RF24.h" - - -using namespace std; -// -// Hardware configuration -// - -// CE Pin, CSN Pin, SPI Speed - -// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz -//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz -//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); - -// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz -RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); - - - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; - -bool role_ping_out = 1, role_pong_back = 0; -bool role = 0; -uint8_t data[32]; -unsigned long startTime, stopTime, counter, rxTimer=0; - -int main(int argc, char** argv){ - - - // Print preamble: - - printf("RF24/examples/Transfer/\n"); - - radio.begin(); // Setup and configure rf radio - radio.setChannel(1); - radio.setPALevel(RF24_PA_MAX); - radio.setDataRate(RF24_2MBPS); - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries - radio.setCRCLength(RF24_CRC_8); - radio.printDetails(); -/********* Role chooser ***********/ - - printf("\n ************ Role Setup ***********\n"); - string input = ""; - char myChar = {0}; - cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit)\n>"; - getline(cin,input); - - if(input.length() == 1) { - myChar = input[0]; - if(myChar == '0'){ - cout << "Role: Pong Back, awaiting transmission " << endl << endl; - }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; - role = role_ping_out; - } - } -/***********************************/ - - if ( role == role_ping_out ) { - radio.openWritingPipe(addresses[0]); - radio.openReadingPipe(1,addresses[1]); - } else { - radio.openWritingPipe(addresses[1]); - radio.openReadingPipe(1,addresses[0]); - radio.startListening(); - } - - - for(int i=0; i<32; i++){ - data[i] = rand() % 255; //Load the buffer with random data - } - - // forever loop - while (1){ - - if (role == role_ping_out){ - sleep(2); - printf("Initiating Basic Data Transfer\n\r"); - - long int cycles = 10000; //Change this to a higher or lower number. - - startTime = millis(); - - for(int i=0; i 1000){ - rxTimer = millis(); - printf("Rate: "); - float numBytes = counter*32; - printf("%.2f KB/s \n\r",numBytes/1000); - printf("Payload Count: %lu \n\r", counter); - counter = 0; - } - } - -} // loop -} // main - - - - - - diff --git a/RF24/RPi/RF24/nRF24L01.h b/RF24/RPi/RF24/nRF24L01.h deleted file mode 100644 index ba83534..0000000 --- a/RF24/RPi/RF24/nRF24L01.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright (c) 2007 Stefan Engelke - Portions Copyright (C) 2011 Greg Copeland - - 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. -*/ - -/* Memory Map */ -#define CONFIG 0x00 -#define EN_AA 0x01 -#define EN_RXADDR 0x02 -#define SETUP_AW 0x03 -#define SETUP_RETR 0x04 -#define RF_CH 0x05 -#define RF_SETUP 0x06 -#define STATUS 0x07 -#define OBSERVE_TX 0x08 -#define CD 0x09 -#define RX_ADDR_P0 0x0A -#define RX_ADDR_P1 0x0B -#define RX_ADDR_P2 0x0C -#define RX_ADDR_P3 0x0D -#define RX_ADDR_P4 0x0E -#define RX_ADDR_P5 0x0F -#define TX_ADDR 0x10 -#define RX_PW_P0 0x11 -#define RX_PW_P1 0x12 -#define RX_PW_P2 0x13 -#define RX_PW_P3 0x14 -#define RX_PW_P4 0x15 -#define RX_PW_P5 0x16 -#define FIFO_STATUS 0x17 -#define DYNPD 0x1C -#define FEATURE 0x1D - -/* Bit Mnemonics */ -#define MASK_RX_DR 6 -#define MASK_TX_DS 5 -#define MASK_MAX_RT 4 -#define EN_CRC 3 -#define CRCO 2 -#define PWR_UP 1 -#define PRIM_RX 0 -#define ENAA_P5 5 -#define ENAA_P4 4 -#define ENAA_P3 3 -#define ENAA_P2 2 -#define ENAA_P1 1 -#define ENAA_P0 0 -#define ERX_P5 5 -#define ERX_P4 4 -#define ERX_P3 3 -#define ERX_P2 2 -#define ERX_P1 1 -#define ERX_P0 0 -#define AW 0 -#define ARD 4 -#define ARC 0 -#define PLL_LOCK 4 -#define RF_DR 3 -#define RF_PWR 6 -#define RX_DR 6 -#define TX_DS 5 -#define MAX_RT 4 -#define RX_P_NO 1 -#define TX_FULL 0 -#define PLOS_CNT 4 -#define ARC_CNT 0 -#define TX_REUSE 6 -#define FIFO_FULL 5 -#define TX_EMPTY 4 -#define RX_FULL 1 -#define RX_EMPTY 0 -#define DPL_P5 5 -#define DPL_P4 4 -#define DPL_P3 3 -#define DPL_P2 2 -#define DPL_P1 1 -#define DPL_P0 0 -#define EN_DPL 2 -#define EN_ACK_PAY 1 -#define EN_DYN_ACK 0 - -/* Instruction Mnemonics */ -#define R_REGISTER 0x00 -#define W_REGISTER 0x20 -#define REGISTER_MASK 0x1F -#define ACTIVATE 0x50 -#define R_RX_PL_WID 0x60 -#define R_RX_PAYLOAD 0x61 -#define W_TX_PAYLOAD 0xA0 -#define W_ACK_PAYLOAD 0xA8 -#define FLUSH_TX 0xE1 -#define FLUSH_RX 0xE2 -#define REUSE_TX_PL 0xE3 -#define NOP 0xFF - -/* Non-P omissions */ -#define LNA_HCURR 0 - -/* P model memory Map */ -#define RPD 0x09 -#define W_TX_PAYLOAD_NO_ACK 0xB0 - -/* P model bit Mnemonics */ -#define RF_DR_LOW 5 -#define RF_DR_HIGH 3 -#define RF_PWR_LOW 1 -#define RF_PWR_HIGH 2 diff --git a/RF24/RPi/RF24/readme.md b/RF24/RPi/RF24/readme.md deleted file mode 100644 index a7880d6..0000000 --- a/RF24/RPi/RF24/readme.md +++ /dev/null @@ -1,117 +0,0 @@ - -Optimized Raspberry Pi RF24 and RF24 Network Libraries -============================================= - General Documentation: http://tmrh20.github.io - - Library functions are mostly the same. - See the included examples for RPi specific usage - -##Raspberry Pi - PreConfig - - -####Possible pre-configuration: - If SPI is not already enabled, load it on boot: -``` - sudo raspi-config -``` -A. Update the tool via the menu as required -B. Select Advanced and enable the SPI kernel module -C. Update other software and libraries: -``` - sudo apt-get update - sudo apt-get upgrade -``` -RPi - RF24 Quick-Start -====================== -A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: -``` - mkdir ~/rf24libs - cd ~/rf24libs -``` -B. Clone the RF24 Repo -``` - git clone https://github.com/tmrh20/RF24.git rtemp -``` -C. Copy the RPi library folder to the current directory, and delete the rest -``` - mv rtemp/RPi/RF24 ./ - rm -r rtemp - cd RF24 -``` -D. Build the library, and run an example file: -``` - sudo make - cd examples - sudo make - sudo ./gettingstarted -``` - -RPi - RF24Network Quick-Start -============================= -A. Enter the same directory that contains the RF24 library folder -``` - cd ~/rf24libs -``` -B. Clone the RF24Network Repo -``` - git clone https://github.com/tmrh20/RF24Network.git ntemp -``` -C. Copy the RF24Network folder to the current directory, and delete the rest -``` - mv ntemp/RPi/RF24Network ./ - rm -r ntemp - cd RF24Network -``` -D. Build the library -``` - sudo make - cd examples - sudo make - sudo ./helloworld_rx OR sudo ./helloworld_tx -``` - -Connection Info -=============== - -Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN - -Can use either RPi CE0 or CE1 pins for radio CSN. -Choose any RPi output pin for radio CE pin. - -**Constructor:** -``` -RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); -or -RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS1, BCM2835_SPI_SPEED_8MHZ); -``` - -**Pins:** - - |NRF24L01 | RPI | P1 Connector | - |-----|-----------|-------------| - | VCC | rpi-3v3 | (17) | - | GND | rpi-gnd | (25) | - | CE | rpi-gpio22 | (15) | - | CSN | rpi-gpio8 | (24) | - | SCK | rpi-sckl | (23) | - | MOSI | rpi-mosi | (19) | - | MISO | rpi-miso | (21) | - - -See http://www.airspayce.com/mikem/bcm2835/index.html for BCM2835 class documentation. -Note: The BCM library has been customized slightly to allow use of hardware CE pins not -in use for SPI, and to include a millis() function. - -**************** - - -Based on the arduino lib from J. Coliz . -the library was berryfied by Purinda Gunasekara . -then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi -Network lib also based on https://github.com/farconada/RF24Network - -Currently optimized and aligned with Arduino fork of libraries by TMRh20: -https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi -Documentation: http://tmrh20.github.io - - diff --git a/RF24/RPi/readme.md b/RF24/RPi/readme.md deleted file mode 100644 index a7880d6..0000000 --- a/RF24/RPi/readme.md +++ /dev/null @@ -1,117 +0,0 @@ - -Optimized Raspberry Pi RF24 and RF24 Network Libraries -============================================= - General Documentation: http://tmrh20.github.io - - Library functions are mostly the same. - See the included examples for RPi specific usage - -##Raspberry Pi - PreConfig - - -####Possible pre-configuration: - If SPI is not already enabled, load it on boot: -``` - sudo raspi-config -``` -A. Update the tool via the menu as required -B. Select Advanced and enable the SPI kernel module -C. Update other software and libraries: -``` - sudo apt-get update - sudo apt-get upgrade -``` -RPi - RF24 Quick-Start -====================== -A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: -``` - mkdir ~/rf24libs - cd ~/rf24libs -``` -B. Clone the RF24 Repo -``` - git clone https://github.com/tmrh20/RF24.git rtemp -``` -C. Copy the RPi library folder to the current directory, and delete the rest -``` - mv rtemp/RPi/RF24 ./ - rm -r rtemp - cd RF24 -``` -D. Build the library, and run an example file: -``` - sudo make - cd examples - sudo make - sudo ./gettingstarted -``` - -RPi - RF24Network Quick-Start -============================= -A. Enter the same directory that contains the RF24 library folder -``` - cd ~/rf24libs -``` -B. Clone the RF24Network Repo -``` - git clone https://github.com/tmrh20/RF24Network.git ntemp -``` -C. Copy the RF24Network folder to the current directory, and delete the rest -``` - mv ntemp/RPi/RF24Network ./ - rm -r ntemp - cd RF24Network -``` -D. Build the library -``` - sudo make - cd examples - sudo make - sudo ./helloworld_rx OR sudo ./helloworld_tx -``` - -Connection Info -=============== - -Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN - -Can use either RPi CE0 or CE1 pins for radio CSN. -Choose any RPi output pin for radio CE pin. - -**Constructor:** -``` -RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); -or -RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS1, BCM2835_SPI_SPEED_8MHZ); -``` - -**Pins:** - - |NRF24L01 | RPI | P1 Connector | - |-----|-----------|-------------| - | VCC | rpi-3v3 | (17) | - | GND | rpi-gnd | (25) | - | CE | rpi-gpio22 | (15) | - | CSN | rpi-gpio8 | (24) | - | SCK | rpi-sckl | (23) | - | MOSI | rpi-mosi | (19) | - | MISO | rpi-miso | (21) | - - -See http://www.airspayce.com/mikem/bcm2835/index.html for BCM2835 class documentation. -Note: The BCM library has been customized slightly to allow use of hardware CE pins not -in use for SPI, and to include a millis() function. - -**************** - - -Based on the arduino lib from J. Coliz . -the library was berryfied by Purinda Gunasekara . -then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi -Network lib also based on https://github.com/farconada/RF24Network - -Currently optimized and aligned with Arduino fork of libraries by TMRh20: -https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi -Documentation: http://tmrh20.github.io - - diff --git a/RF24/doxygen-custom.css b/RF24/doxygen-custom.css deleted file mode 100644 index d7d0e12..0000000 --- a/RF24/doxygen-custom.css +++ /dev/null @@ -1,835 +0,0 @@ -/* The standard CSS for doxygen */ - -body, table, div, p, dl { - font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; - font-size: 12px; -} - -/* @group Heading Levels */ - -h1 { - font-size: 150%; -} - -.title { - font-size: 150%; - font-weight: bold; - margin: 10px 2px; -} - -h2 { - font-size: 120%; -} - -h3 { - font-size: 100%; -} - -dt { - font-weight: bold; -} - -div.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; -} - -p.startli, p.startdd, p.starttd { - margin-top: 2px; -} - -p.endli { - margin-bottom: 0px; -} - -p.enddd { - margin-bottom: 4px; -} - -p.endtd { - margin-bottom: 2px; -} - -/* @end */ - -caption { - font-weight: bold; -} - -span.legend { - font-size: 70%; - text-align: center; -} - -h3.version { - font-size: 90%; - text-align: center; -} - -div.qindex, div.navtab{ - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - padding: 2px; -} - -div.qindex, div.navpath { - width: 100%; - line-height: 140%; -} - -div.navtab { - margin-right: 15px; -} - -/* @group Link Styling */ - -a { - color: #3D578C; - font-weight: normal; - text-decoration: none; -} - -.contents a:visited { - color: #4665A2; -} - -a:hover { - text-decoration: underline; -} - -a.qindex { - font-weight: bold; -} - -a.qindexHL { - font-weight: bold; - background-color: #9CAFD4; - color: #ffffff; - border: 1px double #869DCA; -} - -.contents a.qindexHL:visited { - color: #ffffff; -} - -a.el { - font-weight: bold; -} - -a.elRef { -} - -a.code { - color: #4665A2; -} - -a.codeRef { - color: #4665A2; -} - -/* @end */ - -dl.el { - margin-left: -1cm; -} - -.fragment { - font-family: monospace, fixed; - font-size: 105%; -} - -pre.fragment { - border: 1px solid #C4CFE5; - background-color: #FBFCFD; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - font-size: 9pt; - line-height: 125%; -} - -div.ah { - background-color: black; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px; - padding: 0.2em; - border: solid thin #333; - border-radius: 0.5em; - -webkit-border-radius: .5em; - -moz-border-radius: .5em; - box-shadow: 2px 2px 3px #999; - -webkit-box-shadow: 2px 2px 3px #999; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); - background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); -} - -div.groupHeader { - margin-left: 16px; - margin-top: 12px; - font-weight: bold; -} - -div.groupText { - margin-left: 16px; - font-style: italic; -} - -body { - background: white; - color: black; - margin: 0; -} - -div.contents { - margin-top: 10px; - margin-left: 10px; - margin-right: 5px; -} - -td.indexkey { - background-color: #EBEFF6; - font-weight: bold; - border: 1px solid #C4CFE5; - margin: 2px 0px 2px 0; - padding: 2px 10px; -} - -td.indexvalue { - background-color: #EBEFF6; - border: 1px solid #C4CFE5; - padding: 2px 10px; - margin: 2px 0px; -} - -tr.memlist { - background-color: #EEF1F7; -} - -p.formulaDsp { - text-align: center; -} - -img.formulaDsp { - -} - -img.formulaInl { - vertical-align: middle; -} - -div.center { - text-align: center; - margin-top: 0px; - margin-bottom: 0px; - padding: 0px; -} - -div.center img { - border: 0px; -} - -address.footer { - text-align: right; - padding-right: 12px; -} - -img.footer { - border: 0px; - vertical-align: middle; -} - -/* @group Code Colorization */ - -span.keyword { - color: #008000 -} - -span.keywordtype { - color: #604020 -} - -span.keywordflow { - color: #e08000 -} - -span.comment { - color: #800000 -} - -span.preprocessor { - color: #806020 -} - -span.stringliteral { - color: #002080 -} - -span.charliteral { - color: #008080 -} - -span.vhdldigit { - color: #ff00ff -} - -span.vhdlchar { - color: #000000 -} - -span.vhdlkeyword { - color: #700070 -} - -span.vhdllogic { - color: #ff0000 -} - -/* @end */ - -/* -.search { - color: #003399; - font-weight: bold; -} - -form.search { - margin-bottom: 0px; - margin-top: 0px; -} - -input.search { - font-size: 75%; - color: #000080; - font-weight: normal; - background-color: #e8eef2; -} -*/ - -td.tiny { - font-size: 75%; -} - -.dirtab { - padding: 4px; - border-collapse: collapse; - border: 1px solid #A3B4D7; -} - -th.dirtab { - background: #EBEFF6; - font-weight: bold; -} - -hr { - height: 0px; - border: none; - border-top: 1px solid #4A6AAA; -} - -hr.footer { - height: 1px; -} - -/* @group Member Descriptions */ - -table.memberdecls { - border-spacing: 0px; - padding: 0px; -} - -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { - background-color: #F9FAFC; - border: none; - margin: 4px; - padding: 1px 0 0 8px; -} - -.mdescLeft, .mdescRight { - padding: 0px 8px 4px 8px; - color: #555; -} - -.memItemLeft, .memItemRight, .memTemplParams { - border-top: 1px solid #C4CFE5; -} - -.memItemLeft, .memTemplItemLeft { - white-space: nowrap; -} - -.memItemRight { - width: 100%; -} - -.memTemplParams { - color: #4665A2; - white-space: nowrap; -} - -/* @end */ - -/* @group Member Details */ - -/* Styles for detailed member documentation */ - -.memtemplate { - font-size: 80%; - color: #4665A2; - font-weight: normal; - margin-left: 9px; -} - -.memnav { - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - margin-right: 15px; - padding: 2px; -} - -.mempage { - width: 100%; -} - -.memitem { - padding: 0; - margin-bottom: 10px; - margin-right: 5px; -} - -.memname { - white-space: nowrap; - font-weight: bold; - margin-left: 6px; -} - -.memproto { - border-top: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 6px 0px 6px 0px; - color: #253555; - font-weight: bold; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - /* opera specific markup */ - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - border-top-right-radius: 8px; - border-top-left-radius: 8px; - /* firefox specific markup */ - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - -moz-border-radius-topright: 8px; - -moz-border-radius-topleft: 8px; - /* webkit specific markup */ - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - -webkit-border-top-right-radius: 8px; - -webkit-border-top-left-radius: 8px; - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2E8F2; - -} - -.memdoc { - border-bottom: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 2px 5px; - background-color: #FBFCFD; - border-top-width: 0; - /* opera specific markup */ - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - /* firefox specific markup */ - -moz-border-radius-bottomleft: 8px; - -moz-border-radius-bottomright: 8px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); - /* webkit specific markup */ - -webkit-border-bottom-left-radius: 8px; - -webkit-border-bottom-right-radius: 8px; - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); -} - -.paramkey { - text-align: right; -} - -.paramtype { - white-space: nowrap; -} - -.paramname { - color: #602020; - white-space: nowrap; -} -.paramname em { - font-style: normal; -} - -.params, .retval, .exception, .tparams { - border-spacing: 6px 2px; -} - -.params .paramname, .retval .paramname { - font-weight: bold; - vertical-align: top; -} - -.params .paramtype { - font-style: italic; - vertical-align: top; -} - -.params .paramdir { - font-family: "courier new",courier,monospace; - vertical-align: top; -} - - - - -/* @end */ - -/* @group Directory (tree) */ - -/* for the tree view */ - -.ftvtree { - font-family: sans-serif; - margin: 0px; -} - -/* these are for tree view when used as main index */ - -.directory { - font-size: 9pt; - font-weight: bold; - margin: 5px; -} - -.directory h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; -} - -/* -The following two styles can be used to replace the root node title -with an image of your choice. Simply uncomment the next two styles, -specify the name of your image and be sure to set 'height' to the -proper pixel height of your image. -*/ - -/* -.directory h3.swap { - height: 61px; - background-repeat: no-repeat; - background-image: url("yourimage.gif"); -} -.directory h3.swap span { - display: none; -} -*/ - -.directory > h3 { - margin-top: 0; -} - -.directory p { - margin: 0px; - white-space: nowrap; -} - -.directory div { - display: none; - margin: 0px; -} - -.directory img { - vertical-align: -30%; -} - -/* these are for tree view when not used as main index */ - -.directory-alt { - font-size: 100%; - font-weight: bold; -} - -.directory-alt h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; -} - -.directory-alt > h3 { - margin-top: 0; -} - -.directory-alt p { - margin: 0px; - white-space: nowrap; -} - -.directory-alt div { - display: none; - margin: 0px; -} - -.directory-alt img { - vertical-align: -30%; -} - -/* @end */ - -div.dynheader { - margin-top: 8px; -} - -address { - font-style: normal; - color: #2A3D61; -} - -table.doxtable { - border-collapse:collapse; -} - -table.doxtable td, table.doxtable th { - border: 1px solid #2D4068; - padding: 3px 7px 2px; -} - -table.doxtable th { - background-color: #374F7F; - color: #FFFFFF; - font-size: 110%; - padding-bottom: 4px; - padding-top: 5px; - text-align:left; -} - -.tabsearch { - top: 0px; - left: 10px; - height: 36px; - background-image: url('tab_b.png'); - z-index: 101; - overflow: hidden; - font-size: 13px; -} - -.navpath ul -{ - font-size: 11px; - background-image:url('tab_b.png'); - background-repeat:repeat-x; - height:30px; - line-height:30px; - color:#8AA0CC; - border:solid 1px #C2CDE4; - overflow:hidden; - margin:0px; - padding:0px; -} - -.navpath li -{ - list-style-type:none; - float:left; - padding-left:10px; - padding-right:15px; - background-image:url('bc_s.png'); - background-repeat:no-repeat; - background-position:right; - color:#364D7C; -} - -.navpath li.navelem a -{ - height:32px; - display:block; - text-decoration: none; - outline: none; -} - -.navpath li.navelem a:hover -{ - color:#6884BD; -} - -.navpath li.footer -{ - list-style-type:none; - float:right; - padding-left:10px; - padding-right:15px; - background-image:none; - background-repeat:no-repeat; - background-position:right; - color:#364D7C; - font-size: 8pt; -} - - -div.summary -{ - float: right; - font-size: 8pt; - padding-right: 5px; - width: 50%; - text-align: right; -} - -div.summary a -{ - white-space: nowrap; -} - -div.ingroups -{ - font-size: 8pt; - padding-left: 5px; - width: 50%; - text-align: left; -} - -div.ingroups a -{ - white-space: nowrap; -} - -div.header -{ - background-image:url('nav_h.png'); - background-repeat:repeat-x; - background-color: #F9FAFC; - margin: 0px; - border-bottom: 1px solid #C4CFE5; -} - -div.headertitle -{ - padding: 5px 5px 5px 10px; -} - -dl -{ - padding: 0 0 0 10px; -} - -dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug -{ - border-left:4px solid; - padding: 0 0 0 6px; -} - -dl.note -{ - border-color: #D0C000; -} - -dl.warning, dl.attention -{ - border-color: #FF0000; -} - -dl.pre, dl.post, dl.invariant -{ - border-color: #00D000; -} - -dl.deprecated -{ - border-color: #505050; -} - -dl.todo -{ - border-color: #00C0E0; -} - -dl.test -{ - border-color: #3030E0; -} - -dl.bug -{ - border-color: #C08050; -} - -#projectlogo -{ - text-align: center; - vertical-align: bottom; - border-collapse: separate; -} - -#projectlogo img -{ - border: 0px none; -} - -#projectname -{ - font: 300% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 2px 0px; -} - -#projectbrief -{ - font: 120% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#projectnumber -{ - font: 50% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#titlearea -{ - padding: 0px; - margin: 0px; - width: 100%; - border-bottom: 1px solid #5373B4; -} - -.image -{ - text-align: left; -} - -.dotgraph -{ - text-align: center; -} - -.mscgraph -{ - text-align: center; -} - -.caption -{ - font-weight: bold; -} - diff --git a/RF24/examples/GettingStarted/GettingStarted.ino b/RF24/examples/GettingStarted/GettingStarted.ino deleted file mode 100644 index ff871be..0000000 --- a/RF24/examples/GettingStarted/GettingStarted.ino +++ /dev/null @@ -1,136 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. -//2014 - TMRh20 - Updated along with Optimized RF24 Library fork - */ - -/** - * Example for Getting Started with nRF24L01+ radios. - * - * This is an example of how to use the RF24 class to communicate on a basic level. Write this sketch to two - * different nodes. Put one of the nodes into 'transmit' mode by connecting with the serial monitor and - * sending a 'T'. The ping node sends the current time to the pong node, which responds by sending the value - * back. The ping node can then see how long the whole cycle took. - * Note: For a more efficient call-response scenario see the GettingStarted_CallResponse.ino example. - * Note: When switching between sketches, the radio may need to be powered down to clear settings that are not "un-set" otherwise - */ - - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(7,8); - -const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. - -// Set up roles to simplify testing -boolean role; // The main role variable, holds the current role identifier -boolean role_ping_out = 1, role_pong_back = 0; // The two different roles. - -void setup() { - - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/GettingStarted/\n\r"); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - - // Setup and configure rf radio - radio.begin(); // Start up the radio - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.setRetries(15,15); // Max delay between retries & number of retries - - radio.openWritingPipe(addresses[1]); // Open a writing pipe on pipe 0, with address 1 - radio.openReadingPipe(1,addresses[0]); // Open a reading pipe on pipe 1, with address 0 - - radio.startListening(); // Start listening - radio.printDetails(); // Dump the configuration of the rf unit for debugging -} - -void loop(void){ - - -/****************** Ping Out Role ***************************/ - if (role == role_ping_out) { - - radio.stopListening(); // First, stop listening so we can talk. - - - printf("Now sending \n\r"); - - unsigned long time = micros(); // Take the time, and send it. This will block until complete - if (!radio.write( &time, sizeof(unsigned long) )){ printf("failed.\n\r"); } - - radio.startListening(); // Now, continue listening - - unsigned long started_waiting_at = micros(); // Set up a timeout period, get the current microseconds - boolean timeout = false; // Set up a variable to indicate if a response was received or not - - while ( ! radio.available() ){ // While nothing is received - if (micros() - started_waiting_at > 200000 ){ // If waited longer than 200ms, indicate timeout and exit while loop - timeout = true; - break; - } - } - - if ( timeout ){ // Describe the results - printf("Failed, response timed out.\n\r"); - }else{ - unsigned long got_time; // Grab the response, compare, and send to debugging spew - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Sent %lu, Got response %lu, round-trip delay: %lu microseconds\n\r",time,got_time,micros()-got_time); - } - - // Try again 1s later - delay(1000); - } - -/****************** Pong Back Role ***************************/ - - if ( role == role_pong_back ) - { - if( radio.available()){ - unsigned long got_time; // Variable for the received timestamp - while (radio.available()) { // While there is data ready - radio.read( &got_time, sizeof(unsigned long) ); // Get the payload - } - - radio.stopListening(); // First, stop listening so we can talk - radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. - radio.startListening(); // Now, resume listening so we catch the next packets. - printf("Sent response %lu \n\r", got_time); - } - } - - -/****************** Change Roles via Serial Commands ***************************/ - - if ( Serial.available() ) - { - char c = toupper(Serial.read()); - if ( c == 'T' && role == role_pong_back ) - { - printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); - - role = role_ping_out; // Become the primary transmitter (ping out) - radio.openWritingPipe(addresses[0]); - radio.openReadingPipe(1,addresses[1]); - } - else if ( c == 'R' && role == role_ping_out ) - { - printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); - - role = role_pong_back; // Become the primary receiver (pong back) - radio.openWritingPipe(addresses[1]); - radio.openReadingPipe(1,addresses[0]); - } - } -} \ No newline at end of file diff --git a/RF24/examples/GettingStarted/printf.h b/RF24/examples/GettingStarted/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/GettingStarted/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino b/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino deleted file mode 100644 index 8b2d0d5..0000000 --- a/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino +++ /dev/null @@ -1,124 +0,0 @@ -/* - March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork - Parts derived from examples by J. Coliz -*/ -/** - * Example for efficient call-response using ack-payloads - * - * This example continues to make use of all the normal functionality of the radios including - * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. - * This allows very fast call-response communication, with the responding radio never having to - * switch out of Primary Receiver mode to send back a payload, but having the option to switch to - * primary transmitter if wanting to initiate communication instead of respond to a commmunication. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(7,8); - // Topology -const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. - -// Role management: Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. -typedef enum { role_ping_out = 1, role_pong_back } role_e; // The various roles supported by this sketch -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The debug-friendly names of those roles -role_e role = role_pong_back; // The role of the current running sketch - -byte counter = 1; // A single byte to keep track of the data being sent back and forth - - -void setup(){ - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/GettingStarted/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - - // Setup and configure radio - - radio.begin(); - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.enableAckPayload(); // Allow optional ack payloads - radio.setRetries(0,15); // Smallest time between retries, max no. of retries - radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed - radio.openWritingPipe(addresses[1]); // Both radios listen on the same pipes by default, and switch when writing - radio.openReadingPipe(1,addresses[0]); // Open a reading pipe on address 0, pipe 1 - radio.startListening(); // Start listening - radio.powerUp(); - radio.printDetails(); // Dump the configuration of the rf unit for debugging -} - -void loop(void) { - - -/****************** Ping Out Role ***************************/ - - if (role == role_ping_out){ // Radio is in ping mode - - byte gotByte; // Initialize a variable for the incoming response - - radio.stopListening(); // First, stop listening so we can talk. - printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload - unsigned long time = micros(); // Record the current microsecond count - - if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio - if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank - printf("Got blank response. round-trip delay: %lu microseconds\n\r",micros()-time); - }else{ - while(radio.available() ){ // If an ack with payload was received - radio.read( &gotByte, 1 ); // Read it, and display the response time - printf("Got response %d, round-trip delay: %lu microseconds\n\r",gotByte,micros()-time); - counter++; // Increment the counter variable - } - } - - }else{ printf("Sending failed.\n\r"); } // If no ack response, sending failed - - delay(1000); // Try again later - } - - -/****************** Pong Back Role ***************************/ - - if ( role == role_pong_back ) { - byte pipeNo, gotByte; // Declare variables for the pipe and the byte received - while( radio.available(&pipeNo)){ // Read all available payloads - radio.read( &gotByte, 1 ); - // Since this is a call-response. Respond directly with an ack payload. - // Ack payloads are much more efficient than switching to transmit mode to respond to a call - radio.writeAckPayload(pipeNo,&gotByte, 1 ); // This can be commented out to send empty payloads. - printf("Sent response %d \n\r", gotByte); - } - } - - - -/****************** Change Roles via Serial Commands ***************************/ - - if ( Serial.available() ) - { - char c = toupper(Serial.read()); - if ( c == 'T' && role == role_pong_back ) - { - printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); - - role = role_ping_out; // Change roles (ping out) - radio.openWritingPipe(addresses[0]); // Open different pipes when writing. Write on pipe 0, address 0 - radio.openReadingPipe(1,addresses[1]); // Read on pipe 1, as address 1 - } - else if ( c == 'R' && role == role_ping_out ) - { - printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); - - role = role_pong_back; // Become the primary receiver (pong back) - radio.openWritingPipe(addresses[1]); // Since only two radios involved, both listen on the same addresses and pipe numbers in RX mode - radio.openReadingPipe(1,addresses[0]); // then switch pipes & addresses to transmit. - radio.startListening(); // Need to start listening after opening new reading pipes - } - } -} diff --git a/RF24/examples/GettingStarted_CallResponse/printf.h b/RF24/examples/GettingStarted_CallResponse/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/GettingStarted_CallResponse/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/Transfer/Transfer.ino b/RF24/examples/Transfer/Transfer.ino deleted file mode 100644 index b9b1362..0000000 --- a/RF24/examples/Transfer/Transfer.ino +++ /dev/null @@ -1,144 +0,0 @@ -/* -TMRh20 2014 - - 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. - */ - -/** General Data Transfer Rate Test - * This example demonstrates basic data transfer functionality with the - updated library. This example will display the transfer rates acheived using - the slower form of high-speed transfer using blocking-writes. - */ - - - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -/************* USER Configuration *****************************/ - // Hardware configuration -RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 7 & 8 -boolean RADIO_NO = 0; //SET THIS TO 0 or 1 for 1st or 2nd radio - -/***************************************************************/ - -const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. - -byte data[32]; //Data buffer for testing data transfer speeds - -unsigned long counter, rxTimer; //Counter and timer for keeping track transfer info -unsigned long startTime, stopTime; -bool TX=1,RX=0,role=0; - -void setup(void) { - - Serial.begin(57600); - printf_begin(); - - radio.begin(); // Setup and configure rf radio - radio.setChannel(1); - radio.setPALevel(RF24_PA_MAX); - radio.setDataRate(RF24_1MBPS); - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries - radio.setCRCLength(RF24_CRC_8); - //radio.disableCRC(); - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - - radio.startListening(); // Start listening - radio.printDetails(); // Dump the configuration of the rf unit for debugging - - printf("\n\rRF24/examples/Transfer Rates/\n\r"); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - - randomSeed(analogRead(0)); //Seed for random number generation - - for(int i=0; i<32; i++){ - data[i] = random(255); //Load the buffer with random data - } - radio.powerUp(); //Power up the radio -} - -void loop(void){ - - - if(role == TX){ - - delay(2000); - - printf("Initiating Basic Data Transfer\n\r"); - - - unsigned long cycles = 10000; //Change this to a higher or lower number. - - startTime = millis(); - - for(int i=0; i 1000){ - rxTimer = millis(); - printf("Rate: "); - float numBytes = counter*32; - Serial.print(numBytes/1000); - printf(" KB/s\n\r"); - printf("Payload Count: %d \n\r", counter); - counter = 0; - } - } - // - // Change roles - // - - if ( Serial.available() ) - { - char c = toupper(Serial.read()); - if ( c == 'T' && role == RX ) - { - printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - radio.stopListening(); - role = TX; // Become the primary transmitter (ping out) - } - else if ( c == 'R' && role == TX ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - radio.startListening(); - printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); - role = RX; // Become the primary receiver (pong back) - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/RF24/examples/Transfer/printf.h b/RF24/examples/Transfer/printf.h deleted file mode 100644 index b40dd38..0000000 --- a/RF24/examples/Transfer/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/TransferTimeouts/TransferTimeouts.ino b/RF24/examples/TransferTimeouts/TransferTimeouts.ino deleted file mode 100644 index 65fce99..0000000 --- a/RF24/examples/TransferTimeouts/TransferTimeouts.ino +++ /dev/null @@ -1,189 +0,0 @@ -/* -TMRh20 2014 - - 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. - */ - -/** Reliably transmitting large volumes of data with a low signal or in noisy environments - * This example demonstrates data transfer functionality with the use of auto-retry - and auto-reUse functionality enabled. This sketch demonstrates how a user can extend - the auto-retry functionality to any chosen time period, preventing data loss and ensuring - the consistency of data. - - This sketh demonstrates use of the writeBlocking() functionality, and extends the standard - retry functionality of the radio. Payloads will be auto-retried until successful or the - extended timeout period is reached. - */ - - - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -/************* USER Configuration *****************************/ - -RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 7 & 8 -unsigned long timeoutPeriod = 3000; // Set a user-defined timeout period. With auto-retransmit set to (15,15) retransmission will take up to 60ms and as little as 7.5ms with it set to (1,15). - // With a timeout period of 1000, the radio will retry each payload for up to 1 second before giving up on the transmission and starting over - -/***************************************************************/ - -const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. - -byte data[32]; //Data buffer - -volatile unsigned long counter; -unsigned long rxTimer,startTime, stopTime, payloads = 0; -bool TX=1,RX=0,role=0, transferInProgress = 0; - - -void setup(void) { - - Serial.begin(57600); - printf_begin(); - - radio.begin(); // Setup and configure rf radio - radio.setChannel(1); // Set the channel - radio.setPALevel(RF24_PA_LOW); // Set PA LOW for this demonstration. We want the radio to be as lossy as possible for this example. - radio.setDataRate(RF24_1MBPS); // Raise the data rate to reduce transmission distance and increase lossiness - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.setRetries(2,15); // Optionally, increase the delay between retries. Want the number of auto-retries as high as possible (15) - radio.setCRCLength(RF24_CRC_16); // Set CRC length to 16-bit to ensure quality of data - radio.openWritingPipe(pipes[0]); // Open the default reading and writing pipe - radio.openReadingPipe(1,pipes[1]); - - radio.startListening(); // Start listening - radio.printDetails(); // Dump the configuration of the rf unit for debugging - - printf("\n\rRF24/examples/Transfer Rates/\n\r"); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - - randomSeed(analogRead(0)); //Seed for random number generation - for(int i=0; i<32; i++){ - data[i] = random(255); //Load the buffer with random data - } - radio.powerUp(); //Power up the radio - - -} - - - -void loop(void){ - - - if(role == TX){ - delay(2000); // Pause for a couple seconds between transfers - printf("Initiating Extended Timeout Data Transfer\n\r"); - - unsigned long cycles = 1000; // Change this to a higher or lower number. This is the number of payloads that will be sent. - - unsigned long transferCMD[] = {'H','S',cycles }; // Indicate to the other radio that we are starting, and provide the number of payloads that will be sent - radio.writeFast(&transferCMD,12); // Send the transfer command - if(radio.txStandBy(timeoutPeriod)){ // If transfer initiation was successful, do the following - - startTime = millis(); // For calculating transfer rate - boolean timedOut = 0; // Boolean for keeping track of failures - - for(int i=0; i timeoutPeriod){ // If no data available, check the timeout period - Serial.println("Transfer Failed"); // If per-payload timeout exceeeded, end the transfer - transferInProgress = 0; - }else - if(counter >= payloads){ // If the specified number of payloads is reached, transfer is completed - startTime = millis() - startTime; // Calculate the total time spent during transfer - float numBytes = counter*32; // Calculate the number of bytes transferred - Serial.print("Rate: "); // Print the transfer rate and number of payloads - Serial.print(numBytes/startTime); - Serial.println(" KB/s"); - printf("Payload Count: %d \n\r", counter); - transferInProgress = 0; // End the transfer as complete - } - } - - - } - - // - // Change roles - // - - if ( Serial.available() ) - { - char c = toupper(Serial.read()); - if ( c == 'T' && role == RX ) - { - printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - radio.stopListening(); - role = TX; // Become the primary transmitter (ping out) - } - else if ( c == 'R' && role == TX ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - radio.startListening(); - printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); - role = RX; // Become the primary receiver (pong back) - } - } -} - - diff --git a/RF24/examples/TransferTimeouts/printf.h b/RF24/examples/TransferTimeouts/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/TransferTimeouts/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/Usage/led_remote/Jamfile b/RF24/examples/Usage/led_remote/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/RF24/examples/Usage/led_remote/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/RF24/examples/Usage/led_remote/led_remote.pde b/RF24/examples/Usage/led_remote/led_remote.pde deleted file mode 100644 index 80e2955..0000000 --- a/RF24/examples/Usage/led_remote/led_remote.pde +++ /dev/null @@ -1,255 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example LED Remote - * - * This is an example of how to use the RF24 class to control a remote - * bank of LED's using buttons on a remote control. - * - * On the 'remote', connect any number of buttons or switches from - * an arduino pin to ground. Update 'button_pins' to reflect the - * pins used. - * - * On the 'led' board, connect the same number of LED's from an - * arduino pin to a resistor to ground. Update 'led_pins' to reflect - * the pins used. Also connect a separate pin to ground and change - * the 'role_pin'. This tells the sketch it's running on the LED board. - * - * Every time the buttons change on the remote, the entire state of - * buttons is send to the led board, which displays the state. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver -// Leave open to be the 'remote' transmitter -const int role_pin = A4; - -// Pins on the remote for buttons -const uint8_t button_pins[] = { 2,3,4,5,6,7 }; -const uint8_t num_button_pins = sizeof(button_pins); - -// Pins on the LED board for LED's -const uint8_t led_pins[] = { 2,3,4,5,6,7 }; -const uint8_t num_led_pins = sizeof(led_pins); - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_remote = 1, role_led } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Remote", "LED Board"}; - -// The role of the current running sketch -role_e role; - -// -// Payload -// - -uint8_t button_states[num_button_pins]; -uint8_t led_states[num_led_pins]; - -// -// Setup -// - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_remote; - else - role = role_led; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/led_remote/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipes for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_remote ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_led ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Set up buttons / LED's - // - - // Set pull-up resistors for all buttons - if ( role == role_remote ) - { - int i = num_button_pins; - while(i--) - { - pinMode(button_pins[i],INPUT); - digitalWrite(button_pins[i],HIGH); - } - } - - // Turn LED's ON until we start getting keys - if ( role == role_led ) - { - int i = num_led_pins; - while(i--) - { - pinMode(led_pins[i],OUTPUT); - led_states[i] = HIGH; - digitalWrite(led_pins[i],led_states[i]); - } - } - -} - -// -// Loop -// - -void loop(void) -{ - // - // Remote role. If the state of any button has changed, send the whole state of - // all buttons. - // - - if ( role == role_remote ) - { - // Get the current state of buttons, and - // Test if the current state is different from the last state we sent - int i = num_button_pins; - bool different = false; - while(i--) - { - uint8_t state = ! digitalRead(button_pins[i]); - if ( state != button_states[i] ) - { - different = true; - button_states[i] = state; - } - } - - // Send the state of the buttons to the LED board - if ( different ) - { - printf("Now sending..."); - bool ok = radio.write( button_states, num_button_pins ); - if (ok) - printf("ok\n\r"); - else - printf("failed\n\r"); - } - - // Try again in a short while - delay(20); - } - - // - // LED role. Receive the state of all buttons, and reflect that in the LEDs - // - - if ( role == role_led ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( button_states, num_button_pins ); - - // Spew it - printf("Got buttons\n\r"); - - // For each button, if the button now on, then toggle the LED - int i = num_led_pins; - while(i--) - { - if ( button_states[i] ) - { - led_states[i] ^= HIGH; - digitalWrite(led_pins[i],led_states[i]); - } - } - } - } - } -} -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24/examples/Usage/led_remote/printf.h b/RF24/examples/Usage/led_remote/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/Usage/led_remote/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/Usage/nordic_fob/Jamfile b/RF24/examples/Usage/nordic_fob/Jamfile deleted file mode 100644 index ec519f7..0000000 --- a/RF24/examples/Usage/nordic_fob/Jamfile +++ /dev/null @@ -1,219 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = RF24 SPI ; -PROJECT_DIRS = $(PWD) ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= stk500v1 ; -UPLOAD_SPEED ?= 115200 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN = /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN = /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -AR = $(AVR_BIN)/avr-ar rcs ; -RANLIB = ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE = $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Library -{ - LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -rule Arduino -{ - LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; - Main $(<) : $(>) ; - LinkLibraries $(<) : libs core ; - Hex $(<:B).hex : $(<) ; - for _p in $(PORTS) - { - Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; - } -} - -# -# Targets -# - -# Grab everything from the core directory -Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Main output executable -Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/RF24/examples/Usage/nordic_fob/nordic_fob.pde b/RF24/examples/Usage/nordic_fob/nordic_fob.pde deleted file mode 100644 index 5a316a0..0000000 --- a/RF24/examples/Usage/nordic_fob/nordic_fob.pde +++ /dev/null @@ -1,142 +0,0 @@ -/* - Copyright (C) 2012 J. Coliz - - 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. - */ - -/** - * Example Nordic FOB Receiver - * - * This is an example of how to use the RF24 class to receive signals from the - * Sparkfun Nordic FOB. Thanks to Kirk Mower for providing test hardware. - * - * See blog post at http://maniacbug.wordpress.com/2012/01/08/nordic-fob/ - */ - -#include -#include -#include "nRF24L01.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// -// Payload -// - -struct payload_t -{ - uint8_t buttons; - uint16_t id; - uint8_t empty; -}; - -const char* button_names[] = { "Up", "Down", "Left", "Right", "Center" }; -const int num_buttons = 5; - -// -// Forward declarations -// - -uint16_t flip_endian(uint16_t in); - -// -// Setup -// - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\r\nRF24/examples/nordic_fob/\r\n"); - - // - // Setup and configure rf radio according to the built-in parameters - // of the FOB. - // - - radio.begin(); - radio.setChannel(2); - radio.setPayloadSize(4); - radio.setAutoAck(false); - radio.setCRCLength(RF24_CRC_8); - radio.openReadingPipe(1,0xE7E7E7E7E7LL); - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -// -// Loop -// - -void loop(void) -{ - // - // Receive each packet, dump it out - // - - // if there is data ready - if ( radio.available() ) - { - // Get the packet from the radio - payload_t payload; - radio.read( &payload, sizeof(payload) ); - - // Print the ID of this message. Note that the message - // is sent 'big-endian', so we have to flip it. - printf("#%05u Buttons ",flip_endian(payload.id)); - - // Print the name of each button - int i = num_buttons; - while (i--) - { - if ( ! ( payload.buttons & _BV(i) ) ) - { - printf("%s ",button_names[i]); - } - } - - // If no buttons, print None - if ( payload.buttons == _BV(num_buttons) - 1 ) - printf("None"); - - printf("\r\n"); - } -} - -// -// Helper functions -// - -// Change a big-endian word into a little-endian -uint16_t flip_endian(uint16_t in) -{ - uint16_t low = in >> 8; - uint16_t high = in << 8; - - return high | low; -} - -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/RF24/examples/Usage/nordic_fob/printf.h b/RF24/examples/Usage/nordic_fob/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/Usage/nordic_fob/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/Usage/pingpair_maple/Jamfile b/RF24/examples/Usage/pingpair_maple/Jamfile deleted file mode 100644 index 798096c..0000000 --- a/RF24/examples/Usage/pingpair_maple/Jamfile +++ /dev/null @@ -1,182 +0,0 @@ -MCU = cortex-m3 ; -CHIP = STM32F103ZE ; -BOARD = maple_native ; - -#CHIP = at91sam3u4 ; -#BOARD = sam3u-ek ; - -if ! $(TOOLSET) -{ - TOOLSET = devkit ; - Echo "Assuming TOOLSET=devkit" ; -} - -if $(TOOLSET) = yagarto -{ - TOOLS_PATH = ~/Source/yagarto-4.6.2/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} -if $(TOOLSET) = yagarto-install -{ - TOOLS_PATH = ~/Source/yagarto/install/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} -else if $(TOOLSET) = devkit -{ - TOOLS_PATH = /opt/devkitARM/bin ; - TOOLS_ARCH = arm-eabi- ; -} -else if $(TOOLSET) = maple -{ - TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} -else if $(TOOLSET) = ports -{ - TOOLS_PATH = /opt/local/bin ; - TOOLS_ARCH = arm-none-eabi- ; -} - -CC = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc ; -C++ = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; -AS = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc -c ; -LINK = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; -OBJCOPY = $(TOOLS_PATH)/$(TOOLS_ARCH)objcopy ; -DFU = dfu-util ; - -DEFINES += VECT_TAB_FLASH BOARD_$(BOARD) MCU_$(CHIP) ERROR_LED_PORT=GPIOC ERROR_LED_PIN=15 STM32_HIGH_DENSITY MAPLE_IDE ; -OPTIM = -Os ; -MFLAGS = cpu=$(MCU) thumb arch=armv7-m ; -CCFLAGS = -Wall -m$(MFLAGS) -g -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections ; -C++FLAGS = $(CCFLAGS) -fno-rtti -fno-exceptions ; -LINKFLAGS += -m$(MFLAGS) -Xlinker --gc-sections ; -DFUFLAGS = -a1 -d 0x1eaf:0x0003 -R ; - -MAPLE_DIR = $(HOME)/Source/SAM3U/libmaple ; -MAPLE_LIBS = Servo LiquidCrystal Wire FreeRTOS ; -MAPLE_SUBDIRS = wirish wirish/comm wirish/boards libmaple libmaple/usb libmaple/usb/usb_lib ; - -SKETCH_DIR = $(HOME)/Source/Arduino ; -SKETCH_LIBS = RF24 ; - -MODULE_DIRS = . $(MAPLE_DIR)/$(MAPLE_SUBDIRS) $(MAPLE_DIR)/libraries/$(MAPLE_LIBS) $(SKETCH_DIR)/libraries/$(SKETCH_LIBS) ; -HDRS = $(MODULE_DIRS) ; -LOCATE_TARGET = out/$(TOOLSET) ; -LOCATE_SOURCE = $(LOCATE_TARGET) ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex $(>) $(<) -} - -rule Binary -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends binary : $(<) ; - Clean clean : $(<) ; -} - -actions Binary -{ - $(OBJCOPY) -O binary $(>) $(<) -} - -rule UserObject -{ - switch $(>:S) - { - case .S : As $(<) : $(>) ; - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Upload -{ - Depends up : $(<) ; - NotFile up ; - Always $(<) ; - Always up ; -} - -actions Upload -{ - $(DFU) $(DFUFLAGS) -D $(<) -} - -# Override base objects rule, so all output can go in the output dir -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -# Override base main rule, so all output can go in the output dir -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -# Modules -MODULES = [ GLOB $(MODULE_DIRS) : *.pde *.c *.cpp *.S ] ; - -# Main output executable -MAIN = $(PWD:B).elf ; - -# Linker script -LINK_DIR = $(MAPLE_DIR)/support/ld ; -LINKSCRIPT = $(LINK_DIR)/$(BOARD)/flash.ld ; - -# Bring in the map and link script -LINKFLAGS += -Wl,-Map=$(LOCATE_TARGET)/$(MAIN:B).map -T$(LINKSCRIPT) -L$(LINK_DIR) ; - -Main $(MAIN) : $(MODULES) ; -Binary $(MAIN:B).bin : $(MAIN) ; -Upload $(MAIN:B).bin ; diff --git a/RF24/examples/Usage/pingpair_maple/main.cpp b/RF24/examples/Usage/pingpair_maple/main.cpp deleted file mode 100644 index b4f976d..0000000 --- a/RF24/examples/Usage/pingpair_maple/main.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifdef MAPLE_IDE - -#include -#include "wirish.h" - -extern void setup(void); -extern void loop(void); - -void board_start(const char* program_name) -{ - // Set up the LED to steady on - pinMode(BOARD_LED_PIN, OUTPUT); - digitalWrite(BOARD_LED_PIN, HIGH); - - // Setup the button as input - pinMode(BOARD_BUTTON_PIN, INPUT); - digitalWrite(BOARD_BUTTON_PIN, HIGH); - - SerialUSB.begin(); - SerialUSB.println("Press BUT"); - - // Wait for button press - while ( !isButtonPressed() ) - { - } - - SerialUSB.println("Welcome!"); - SerialUSB.println(program_name); - - int i = 11; - while (i--) - { - toggleLED(); - delay(50); - } -} - -/** - * Custom version of _write, which will print to the USB. - * In order to use it you MUST ADD __attribute__((weak)) - * to _write in libmaple/syscalls.c -*/ -extern "C" int _write (int file, char * ptr, int len) -{ - if ( (file != 1) && (file != 2) ) - return 0; - else - SerialUSB.write(ptr,len); - return len; -} - -/** - * Re-entrant version of _write. Yagarto and Devkit now use - * the re-entrant newlib, so these get called instead of the - * non_r versions. - */ -extern "C" int _write_r (void*, int file, char * ptr, int len) -{ - return _write( file, ptr, len); -} - -__attribute__((constructor)) __attribute__ ((weak)) void premain() -{ - init(); -} - -__attribute__((weak)) void setup(void) -{ - board_start("No program defined"); -} - -__attribute__((weak)) void loop(void) -{ -} - -__attribute__((weak)) int main(void) -{ - setup(); - - while (true) - { - loop(); - } - return 0; -} -#endif // ifdef MAPLE_IDE -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde b/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde deleted file mode 100644 index 2d3925b..0000000 --- a/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde +++ /dev/null @@ -1,242 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example RF Radio Ping Pair ... for Maple - * - * This is an example of how to use the RF24 class. Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current time to the pong node, - * which responds by sending the value back. The ping node can then see how long the whole cycle - * took. - */ - -#include "WProgram.h" -#include -#include "nRF24L01.h" -#include "RF24.h" - -// -// Maple specific setup. Other than this section, the sketch is the same on Maple as on -// Arduino -// - -#ifdef MAPLE_IDE - -// External startup function -extern void board_start(const char* program_name); - -// Use SPI #2. -HardwareSPI SPI(2); - -#else -#define board_startup printf -#define toggleLED(x) (x) -#endif - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 7 & 6 -// (This works for the Getting Started board plugged into the -// Maple Native backwards.) - -RF24 radio(7,6); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 10; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - board_start("\n\rRF24/examples/pingpair/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(15,15); - - // optionally, reduce the payload size. seems to - // improve reliability - radio.setPayloadSize(8); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - toggleLED(); - - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - bool ok = radio.write( &time, sizeof(unsigned long) ); - - if (ok) - printf("ok...\r\n"); - else - printf("failed.\r\n"); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\r\n"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\r\n",got_time,millis()-got_time); - } - - toggleLED(); - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu...",got_time); - - // Delay just a little bit to let the other unit - // make the transition to receiver - delay(20); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response.\r\n"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/RF24/examples/pingpair_ack/pingpair_ack.ino b/RF24/examples/pingpair_ack/pingpair_ack.ino deleted file mode 100644 index 4cd3fe3..0000000 --- a/RF24/examples/pingpair_ack/pingpair_ack.ino +++ /dev/null @@ -1,123 +0,0 @@ -/* - // March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork - // Parts derived from examples by J. Coliz -*/ -/** - * Example for efficient call-response using ack-payloads - * - * This example continues to make use of all the normal functionality of the radios including - * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. - * This allows very fast call-response communication, with the responding radio never having to - * switch out of Primary Receiver mode to send back a payload, but having the option to if wanting - * to initiate communication instead of respond to a commmunication. - */ - - - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(7,8); - -// Topology -const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. - -// Role management: Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. - -typedef enum { role_ping_out = 1, role_pong_back } role_e; // The various roles supported by this sketch -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The debug-friendly names of those roles -role_e role = role_pong_back; // The role of the current running sketch - -// A single byte to keep track of the data being sent back and forth -byte counter = 1; - -void setup(){ - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/GettingStarted/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); - - // Setup and configure rf radio - - radio.begin(); - radio.setAutoAck(1); // Ensure autoACK is enabled - radio.enableAckPayload(); // Allow optional ack payloads - radio.setRetries(0,15); // Smallest time between retries, max no. of retries - radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed - radio.openWritingPipe(pipes[1]); // Both radios listen on the same pipes by default, and switch when writing - radio.openReadingPipe(1,pipes[0]); - radio.startListening(); // Start listening - radio.printDetails(); // Dump the configuration of the rf unit for debugging -} - -void loop(void) { - - if (role == role_ping_out){ - - radio.stopListening(); // First, stop listening so we can talk. - - printf("Now sending %d as payload. ",counter); - byte gotByte; - unsigned long time = micros(); // Take the time, and send it. This will block until complete - //Called when STANDBY-I mode is engaged (User is finished sending) - if (!radio.write( &counter, 1 )){ - printf("failed.\n\r"); - }else{ - - if(!radio.available()){ - printf("Blank Payload Received\n\r"); - }else{ - while(radio.available() ){ - unsigned long tim = micros(); - radio.read( &gotByte, 1 ); - printf("Got response %d, round-trip delay: %lu microseconds\n\r",gotByte,tim-time); - counter++; - } - } - - } - // Try again later - delay(1000); - } - - // Pong back role. Receive each packet, dump it out, and send it back - - if ( role == role_pong_back ) { - byte pipeNo; - byte gotByte; // Dump the payloads until we've gotten everything - while( radio.available(&pipeNo)){ - radio.read( &gotByte, 1 ); - radio.writeAckPayload(pipeNo,&gotByte, 1 ); - } - } - - // Change roles - - if ( Serial.available() ) - { - char c = toupper(Serial.read()); - if ( c == 'T' && role == role_pong_back ) - { - printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); - - role = role_ping_out; // Become the primary transmitter (ping out) - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else if ( c == 'R' && role == role_ping_out ) - { - printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); - - role = role_pong_back; // Become the primary receiver (pong back) - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - radio.startListening(); - } - } -} diff --git a/RF24/examples/pingpair_ack/printf.h b/RF24/examples/pingpair_ack/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/pingpair_ack/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/pingpair_dyn/Jamfile b/RF24/examples/pingpair_dyn/Jamfile deleted file mode 100644 index 901f8da..0000000 --- a/RF24/examples/pingpair_dyn/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/RF24/examples/pingpair_dyn/pingpair_dyn.ino b/RF24/examples/pingpair_dyn/pingpair_dyn.ino deleted file mode 100644 index e1b1319..0000000 --- a/RF24/examples/pingpair_dyn/pingpair_dyn.ino +++ /dev/null @@ -1,232 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example using Dynamic Payloads - * - * This is an example of how to use payloads of a varying (dynamic) size. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(7,8); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 5; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Payload -// - -const int min_payload_size = 4; -const int max_payload_size = 32; -const int payload_size_increments_by = 1; -int next_payload_size = min_payload_size; - -char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_dyn/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // enable dynamic payloads - radio.enableDynamicPayloads(); - - // optionally, increase the delay between retries & # of retries - radio.setRetries(5,15); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // The payload will always be the same, what will change is how much of it we send. - static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - printf("Now sending length %i...",next_payload_size); - radio.write( send_payload, next_payload_size ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 500 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - uint8_t len = radio.getDynamicPayloadSize(); - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got response size=%i value=%s\n\r",len,receive_payload); - } - - // Update size for next time. - next_payload_size += payload_size_increments_by; - if ( next_payload_size > max_payload_size ) - next_payload_size = min_payload_size; - - // Try again 1s later - delay(100); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - uint8_t len; - bool done = false; - while (radio.available()) - { - // Fetch the payload, and see if this was the last one. - len = radio.getDynamicPayloadSize(); - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - printf("Got payload size=%i value=%s\n\r",len,receive_payload); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Send the final one back. - radio.write( receive_payload, len ); - printf("Sent response.\n\r"); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/RF24/examples/pingpair_dyn/printf.h b/RF24/examples/pingpair_dyn/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/pingpair_dyn/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/pingpair_irq/pingpair_irq.ino b/RF24/examples/pingpair_irq/pingpair_irq.ino deleted file mode 100644 index fb3f429..0000000 --- a/RF24/examples/pingpair_irq/pingpair_irq.ino +++ /dev/null @@ -1,137 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - - Update 2014 - TMRh20 - */ - -/** - * Example of using interrupts - * - * This is an example of how to user interrupts to interact with the radio, and a demonstration - * of how to use them to sleep when receiving, and not miss any payloads. - * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter. - * Sleep functionality is built directly into my fork of the RF24Network library - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" -#include -#include - -// Hardware configuration -RF24 radio(7,8); // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -const short role_pin = 5; // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver - // Leave open to be the 'ping' transmitter - - const uint64_t address[2] = {0xABCDABCD71LL, 0x544d52687CLL}; // Radio pipe addresses for the 2 nodes to communicate. - -// Role management - -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// This is done through the role_pin -typedef enum { role_sender = 1, role_receiver } role_e; // The various roles supported by this sketch -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; // The debug-friendly names of those roles -role_e role; // The role of the current running sketch - -boolean gotMsg = 0; // So we know when to go to sleep - -/********************** Setup *********************/ -void setup(){ - - pinMode(role_pin, INPUT); // set up the role pin - digitalWrite(role_pin,HIGH); // Change this to LOW/HIGH instead of using an external pin - delay(20); // Just to get a solid reading on the role pin - - if ( digitalRead(role_pin) ) // read the address pin, establish our role - role = role_sender; - else - role = role_receiver; - - - Serial.begin(115200); - printf_begin(); - printf("\n\rRF24/examples/pingpair_irq/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // Setup and configure rf radio - radio.begin(); - radio.enableAckPayload(); // We will be using the Ack Payload feature, so please enable it - // Open pipes to other node for communication - if ( role == role_sender ) { // This simple sketch opens a pipe on a single address for these two nodes to - radio.openWritingPipe(address[0]); // communicate back and forth. One listens on it, the other talks to it. - radio.openReadingPipe(1,address[1]); - }else{ - radio.openWritingPipe(address[1]); - radio.openReadingPipe(1,address[0]); - radio.startListening(); - } - radio.printDetails(); // Dump the configuration of the rf unit for debugging - delay(50); - attachInterrupt(0, check_radio, FALLING); // Attach interrupt handler to interrupt #0 (using pin 2) on BOTH the sender and receiver -} - -static uint32_t message_count = 0; - - -/********************** Main Loop *********************/ -void loop() { - - - if (role == role_sender) { // Sender role. Repeatedly send the current time - unsigned long time = millis(); // Take the time, and send it. - printf("Now sending %lu\n\r",time); - radio.startWrite( &time, sizeof(unsigned long) ,0); - delay(2000); // Try again soon - } - - - if(role == role_receiver){ // Receiver does nothing except in IRQ - } -} - - -/********************** Interrupt *********************/ - -void check_radio(void) // Receiver role: Does nothing! All the work is in IRQ -{ - - bool tx,fail,rx; - radio.whatHappened(tx,fail,rx); // What happened? - - if ( tx ) { // Have we successfully transmitted? - if ( role == role_sender ){ printf("Send:OK\n\r"); } - if ( role == role_receiver ){ printf("Ack Payload:Sent\n\r"); } - } - - if ( fail ) { // Have we failed to transmit? - if ( role == role_sender ){ printf("Send:Failed\n\r"); } - if ( role == role_receiver ){ printf("Ack Payload:Failed\n\r"); } - } - - if ( rx || radio.available()){ // Did we receive a message? - - if ( role == role_sender ) { // If we're the sender, we've received an ack payload - radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu\n\r",message_count); - } - - - if ( role == role_receiver ) { // If we're the receiver, we've received a time message - static unsigned long got_time; // Get this payload and dump it - radio.read( &got_time, sizeof(got_time) ); - printf("Got payload %lu\n\r",got_time); - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); // Add an ack packet for the next time around. This is a simple - ++message_count; // packet counter - - } - } -} diff --git a/RF24/examples/pingpair_irq/printf.h b/RF24/examples/pingpair_irq/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/pingpair_irq/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino b/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino deleted file mode 100644 index 8a8bc4e..0000000 --- a/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino +++ /dev/null @@ -1,225 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - - TMRh20 2014 - Updates to the library allow sleeping both in TX and RX modes: - TX Mode: The radio can be powered down (.9uA current) and the Arduino slept using the watchdog timer - RX Mode: The radio can be left in standby mode (22uA current) and the Arduino slept using an interrupt pin - */ - -/** - * Example RF Radio Ping Pair which Sleeps between Sends - * - * This is an example of how to use the RF24 class to create a battery- - * efficient system. It is just like the GettingStarted_CallResponse example, but the - * ping node powers down the radio and sleeps the MCU after every - * ping/pong cycle, and the receiver sleeps between payloads. - * - * Write this sketch to two different nodes, - * connect the role_pin to ground on one. The ping node sends the current - * time to the pong node, which responds by sending the value back. The ping - * node can then see how long the whole cycle took. - */ - -#include -#include -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 -RF24 radio(7,8); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 5; - -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; // Radio pipe addresses for the 2 nodes to communicate. - -// Role management -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - - -// Sleep declarations -typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e; - -void setup_watchdog(uint8_t prescalar); -void do_sleep(void); - -const short sleep_cycles_per_transmission = 4; -volatile short sleep_cycles_remaining = sleep_cycles_per_transmission; - - - -void setup(){ - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // Prepare sleep parameters - // Only the ping out role uses WDT. Wake up every 4s to send a ping - //if ( role == role_ping_out ) - setup_watchdog(wdt_4s); - - // Setup and configure rf radio - - radio.begin(); - - // Open pipes to other nodes for communication - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } else { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // Start listening - radio.startListening(); - - // Dump the configuration of the rf unit for debugging - //radio.printDetails(); -} - -void loop(){ - - - if (role == role_ping_out) { // Ping out role. Repeatedly send the current time - radio.powerUp(); // Power up the radio after sleeping - radio.stopListening(); // First, stop listening so we can talk. - - unsigned long time = millis(); // Take the time, and send it. - printf("Now sending... %lu \n\r",time); - - radio.write( &time, sizeof(unsigned long) ); - - radio.startListening(); // Now, continue listening - - unsigned long started_waiting_at = millis(); // Wait here until we get a response, or timeout (250ms) - bool timeout = false; - while ( ! radio.available() ){ - if (millis() - started_waiting_at > 250 ){ // Break out of the while loop if nothing available - timeout = true; - break; - } - } - - if ( timeout ) { // Describe the results - printf("Failed, response timed out.\n\r"); - }else{ - unsigned long got_time; // Grab the response, compare, and send to debugging spew - radio.read( &got_time, sizeof(unsigned long) ); - - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Shut down the system - delay(500); // Experiment with some delay here to see if it has an effect - // Power down the radio. - radio.powerDown(); // NOTE: The radio MUST be powered back up again manually - - // Sleep the MCU. - do_sleep(); - - - } - - - // Pong back role. Receive each packet, dump it out, and send it back - if ( role == role_pong_back ) { - - if ( radio.available() ) { // if there is data ready - - unsigned long got_time; - while (radio.available()) { // Dump the payloads until we've gotten everything - radio.read( &got_time, sizeof(unsigned long) ); // Get the payload, and see if this was the last one. - // Spew it. Include our time, because the ping_out millis counter is unreliable - printf("Got payload %lu @ %lu...",got_time,millis()); // due to it sleeping - } - - radio.stopListening(); // First, stop listening so we can talk - radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. - printf("Sent response.\n\r"); - radio.startListening(); // Now, resume listening so we catch the next packets. - }else{ - Serial.println("Sleeping"); - delay(50); // Delay so the serial data can print out - do_sleep(); - - } - } -} - -void wakeUp(){ - sleep_disable(); -} - -// Sleep helpers - -//Prescaler values -// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms -// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec - -void setup_watchdog(uint8_t prescalar){ - - uint8_t wdtcsr = prescalar & 7; - if ( prescalar & 8 ) - wdtcsr |= _BV(WDP3); - MCUSR &= ~_BV(WDRF); // Clear the WD System Reset Flag - WDTCSR = _BV(WDCE) | _BV(WDE); // Write the WD Change enable bit to enable changing the prescaler and enable system reset - WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); // Write the prescalar bits (how long to sleep, enable the interrupt to wake the MCU -} - -ISR(WDT_vect) -{ - //--sleep_cycles_remaining; - Serial.println("WDT"); -} - -void do_sleep(void) -{ - set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here - sleep_enable(); - attachInterrupt(0,wakeUp,LOW); - WDTCSR |= _BV(WDIE); - sleep_mode(); // System sleeps here - // The WDT_vect interrupt wakes the MCU from here - sleep_disable(); // System continues execution here when watchdog timed out - detachInterrupt(0); - WDTCSR &= ~_BV(WDIE); -} diff --git a/RF24/examples/pingpair_sleepy/printf.h b/RF24/examples/pingpair_sleepy/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/pingpair_sleepy/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/scanner/Jamfile b/RF24/examples/scanner/Jamfile deleted file mode 100644 index 1bf541e..0000000 --- a/RF24/examples/scanner/Jamfile +++ /dev/null @@ -1,210 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = SPI RF24 ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= stk500v1 ; -UPLOAD_SPEED ?= 57600 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN = /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN = /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE = $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -# -# Targets -# - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Grab everything from the current dir -PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; - -# Main output executable -MAIN = $(PWD:B).elf ; - -Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -Hex $(MAIN:B).hex : $(MAIN) ; - -# Upload targets -for _p in $(PORTS) -{ - Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; -} diff --git a/RF24/examples/scanner/output/core.a b/RF24/examples/scanner/output/core.a deleted file mode 100644 index 6ae105d90023b9ce818bcb755ee9245f24c98185..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187630 zcmeFa3w%|@xi-F55<(L0SCQ)uBA{Y!L_|ak2ndRj0$xhdu#;@a)+9S7JBZXuKt!-! zasj2*daLzVk5#lkz_G^Ft z|G?yZ=AC!mxvrU6GqYyr&yF@$)y&B&jHH_4=|wZ9&zwv?K#xy18a`<1PpNBga1M{Irn1*o^D~FR-ICyo`Sw+t6Iw6kA;HMeC!riM7jOYhsPD z`l^_?D;m=n%VN=L@w=cgo{V`7iN<6_GEq^NsE$>XpLtHeNHixKnv*H4c)fC}<7?x| zXl+G9yq-y7lzzp^;*#okQ?#-+wu1Sp z0cS>iVnw1p2B`Tv*=DE&MYeWrZA`LbvRq<{lI5r}ELxTwG?gZ)=aM=t4|;{7MJA{b zHL8l!R9ScEU(`eN#i_iyXq}09P&`f0@|K-FV|fz3YeVVcV&ipg zb6sVuaX}r^m8z#U-i=k)opnifR!w(SygRGPVV!J}Qd_@ZT^MD+b0ZCyZnOc@jW}SY zlv}{}OhSDbGvI&AmFGrh+36_w3v z&^fV0ic1$o8>=smHpZ668spL0GfEbe7Z^pHJjD?U7J z-I8%qWo&J{enDMNN~g!_t8I0rC*hRnx@f$XPkzt-r46yzrM-AHqBri@>!dZc%}q7E z_}M3@#UV(O5y*$1_5^91Km#$6qGLN6}Dk+XP&8@mr!p%*sF&lV> zY3~(@%BEQ3x>!YWy)6RkSzxUI zs;S-;v{?^Rw4kQ^>~*F(tfG@-gjf?pgpIK#M^*7DEY6AsTLcxgvHG=1X(%>q5lW## zQW}=POw*v4t!}(Vmsecks$WpF$tfFUO|-r~R_oMBakrF|pY0~*q(nok{sIg_ zsG{r z)PxDKk4#G8+a@?4^OJb3+a7{PPoEwEhuQ2^JB=x1Fn`^qfgG3^$ z^Dd8@-dbi-1&PAE)E=E2kueigxFf+^PDvzkQc*GV&)}a(aZ%BX(o(oi))-djZCtQq zq3`>6*|Em=mV2Sg^RDdYdDp^n2>0#dW#-tB z8PJ&nrzoN`w>0qeygWF9Ncx2i3ij`Z1P=A7ae#%MH!uu57C9O;*<*@@Raoa#4P%|tQk^HNvp>VqoPo4hoyRVK(}prJ8Sg;u(m-x< zC})z9Dt8Oytdb(i(WE)OK<9r1vL~g=22O8)RiR2|rOE|PG??Yfz06sEU@8Z(LcbfswONT%MQGA64;@X-cB+O%~L!Ye7S8st{Rl z;ETS&{6!uGl?ojtbVM2*E$*Yz_`}4l{^~wDO>U&PhX@^?=8u#djbvq*it+tF4IB`Z zKkCEtZfd`y{h{`!+h1&dwS76C1kL47z;6>hgxN`zL*U zQq2F8|E0D^+q}*Phd-Uwnf=3Tf4|q6v*WI@50Cx#u|FJpecQJD?IZlXz4?2cHoVB~ zk8~AvG0s2v-)Z|1w7%?r-@k6a=f^!T?i=H79C1gVJNrJ|*WX)Mar)AwODmRc+hnyf zwNtxFeyOQlec@$GZd!WBQa10sxfz~~(ueQF} z`gH3K_RoUhrq3WYzc{2F_IDduvh|c>KJ|4!^3yukP?; z9ZqN~=X;;j01i}E&tZG!hGABA_@Clg>I}DeezJt5Na0l0)?WOvM;SN`S zZHHeM=4Uh%*iNPnqy;Wr&!gtCu+}$=PGj8GsJwJ~=DFuZ&WM<<6MbA1t9*6Q^|gsa zLz9P&F*&m$iFfm8b#)^a+%At-Cu_XgME%-``NPRT05ma2(9ag8b+gU&P4Tt$vFb<_ zI(*N?VVr>sBTsdJQ#`|@7kzniZPGJp&RM*Csm<9rTp=@SVMbj_;IswDV3E=QtXlTE zoSCw0S>Ie|i|(AI%NLs!IIN6%UP03pb;)QYXtL4L8b!(2`lMG-jW=nppt7k6Fjm>T zwjx?t*%({r6*OYOQdsf&HHj2PZM;4v3~DLR3UC9hX^hsXkkyR$@)Ru%&6V|#LCj8E zQLM(TP2(xcszLhGThhVYW8lfo%}gvo?OZ$zp&eho$eFq4LFA^uj}yd0$#KS_x&ckR z?-PhoztiDXOs{Eh@F`QHezU*`pD*B{=)sYQqrmSYejFb-H`XwK-sd3m1&5*VZ1}nQ z29NN(pMpnyoITTz`l7H5qvbaPk-1N|>RSaFSKnLE$GlKquJ}X2AI%)-t5PL!6Vfsb+U5bhyYQmI_M%3EkUGWh zRiiLo0+VS(lQ8k}9e{1Z)mMrLUI0&x>QlE;m!IYBJD_~h%#@O$+f51GndV)D&zv>; z4sfbEN1f0&5%@I6eFAXUXY4yLJ@6ygcOdooO;3+o1B{wD(I|@MH?$Ppy{ZTHo&F~n`wT*;9F5qhaJoKbfl`eI=&9uj!p5>VAC-i zl5M}}vqOU6^XU&a(9{I+C*T}MzIjdcynJGLlb%WpnOi|m6wL8zmf-6_7YcqBbh+Sf zfUXo=1{xLodr;2&sj~!>eJC;OXr16!KsO6s40;1GR-S0uV2N4x|3b_Pgn0m#dYHej z5rfa{d|mjTg8X-cKNVaH-(`13ZTqI{y=Rz9YX#Z~^$A5P61OC75B? z5F_j_fony+9JoO+pPeg2o?)*R`S*dh37>k}h0k!ih>`xg!M{!LeZXHLMt-U1A!5XP zALygPr=D*LzYFx+#L)8u_|FLL1b&`a^!$VvdPZe<-YdeVo;QVG2L7Lge?Dj~+zk5~ zWTpw80RCdZeStqExER>fDcm%sPLUoOF1~0TTT_qF!?I-@ z)YAfdis0*j=L)_Xc(GuXgQ-WzcLIM>_&)}Y3jR58Trk_`WrEqJOg$njp9PNpxuft0 zCIXu$n0d(M00P5jIZtpAa0bH804;^($}aQ8c0Uy3FwG*^p?uh3E`-hHYZMHh>pa1H zmKnAMl>IPut^-ZNl0O`2+bVpP&o<$+9NLA?XJ?1-DSxNn`#>LrrB0UNH-*nK`H}D` z|AydB(05@q{ajDg^bZAQdRT75A>$9i9~c%B>r0K|Ea9_UhY6+(=O&by4a%}r`Qrt% z{AkH91!Wl$vz(U-W;ricKGIVu_zBQ8f|-x?f|d2eE4l`PigSpelZGmY8J8?d z*h5m-j5_pQR8djHm_zSHeLF98FKWCu=C8UJmAVt9TTh5Ir?(xe+|(T_?n*fuSUvAc zVIC0fEd6zMriOPrBJ75By(sw}+E1yE8OfsP60Ji+ik4-3L;C-7gv^{-LJJ z(ln{0?lh@}i6W=|V~)^$%pv3})BJTuO4ET9b0*g74pd7Ax}}}F3U4g(_TFB8OG67@ zd^YdgHfYcFz1^pRE0x3Rib|otp}6I_(})&Q9NqKfUcKaa;=?b70QBTz4njYMebM z--|0(sc+^}>N3>#s@0wCVGk-E?l)w6H7RbDONk?@-0r(P*g#6z^-10J2*aiC?xgN| zs9Z{{HGe(s`A}tt8NT1Oz?FVFnw`G#qSYRIcI)K1pqP1Gv~K4BHDo;YjxvDJX$s=|_E0SnAXAn})~~gB|Ew1?=kEj|F^&XZpBO zt@^G6cJ-A&-+b`Y(2wSFSXUp8!+Ddyqdra@RbM->t8X3jb%Lix^>OXe)i(+Hroof? zI4f0s_W(1D*6$2tR&{_K;7$jVKNE6g1Y$HUu6rt`S2-3g7J)KezW-^wW^Dj&HS3{| z?|EtzJ`aYgZx{5v4^QetlL+*^4y<7Sy+=@~D+$D?-_PK7^|fL)r1gd-5$O8^u!f+9_nMA@`!tOV?rFeb-?#5Th$*!1KpFmqY5IS?H^q_- zO{QttuA410G?}Jlxo*}_XfnOg-Z`PuJa?Z2C76#l;&Ggf_ zaBe1(HY<(%vFuXzx6n5=fycJ$1G9ng`SIBiz~sZG>~BG4KX@mS5Bx1qj(fz5Ko<&T z-)7cr!M`1t{TgL{0~!@fS|j*H(1c*t%R0e71Kli`&+&F*S!=sNF!S^|V(5Ps^ghAo zgW?S;SigQ;FxvXOh1W^pC{S{QoSt6LgqwbyfpU z6#NMA6k^Cz{|SPp0v8cOC(WsXKMTBo81cRgY}Rlg!~E5V%nsns3N8fZc@XNM{`-kp znJ{}{b&c`^Vkx%_$_UOAoCSZb`;*V|97~M;l!kpTF}hYW9uwyPmkOW#Jo|C-Cj-wH zTmZa8a0&1V!DYZEUyx^AtRY{@vtBS4?$!yW*HwbqEVl|~es2`K2>52f=K|j@cp31$ zf-eMqP%!(JuL)iQ{7u0Pz~2?T0r&;MTY!Ho_(tHL3uaxsDfo8acLd)9{GQ@?rfE&jMxr5wo0U2xcB!sZOnep+%qUoG|C5M? zKb06|OH&G~eCAQxC;e0=OK=WoKL=9rcEO#Xj|c_{&JeIY zeoOeo-xd56=*xmx#y=N)7U(YpF9UsB@Cwkqg4quD3tkQSC&3M%2LvZUv*r2P0&My; z6v8&(A;RAQJVNj;VAH=r<{sdP@E-uyzIr#X_SMX5HteDHhelBb^+FSY)q0^@`(e{& zGRU!gsJ26vm-f9#R-k9N;Q63NKRsc_!cu=HXv$Xvhw;tR^pfHkW}6A$R2}M@r@_eh zHHf1Wmb3rCcoTEJc^c|d;E29>3ipNhol#%=^-)f^YeR0XetndekB^fI!O{JrK8uIR zw$5tQnk~p)wsqVs9e=}gBtOIFql*2*@arT0$|dwT(yx#3MNG!5YY{T9FI%)xn{g@` z{VbkM+&`ON#AKXCJRo;2{&vFV@lO`ky!*1#@G{rIhZyLMZ zE-~`COv4zIhfzID!e8YVfTeo-0b9L0{e?a9A23@+uDqG`{F8oobMVh>QXUzzQR94g z!?|Ctn+@>8fH}qEK3B2g7igmMD1tc z`LX89AhtnQ!{a~T(lg^+-ZU$%|Kl*jQ)~b0aGeV@*dWr@Y=Z-F)4Thn9HcwDyMOpj zf#`yyIq~%tR~kPho;1HOj&4~$lEvDz+6`8c&6;rcRb+Qj`lGEs8=b)#^I^Ht(3n_T7ps#G1Duoj;c@-H={fw1(N@rwg7p_Wj<5w;LqD2)SoWN{PScy* zWXNb3Kr=3FB@m;2r@*b4UQfdEu~MUu@o-F5GZ=j&-?IjSfn~~I?TT|14MW-ScvAVk zr$*rdFkF4>F|gym(bre}=sq8oVPq=c6(i#-;Z~#i;$SGI-)ABB6%}&)s)5~jw>a_U zi67&=Lc$b*(#%94o~u(sNXyp{tqb~XM`3Is5TijzH4pS^MJ4j=5T`-Z!8CEok_+qV ziy%?I15b_WQ@2u=|54W9829O_3~OlY+vhm$iNL2hZg%Ej+xvT;4`)qu!9=LG*0 z^xJ}0gZ?KmJ2;qEV40rN5%$-_;Il347e0uYr;yJ!rSDC{fSEq>*`CZ=5cKf*nJ0X< zE3^Is{+Yllh0pfIbZfbpGH2%mW6#X-<_2pHb!P6A>4*O=GZUSX`z~`V-pHMInIpNs zA@vYkq#u{-vcoqU%b7rVBTq7y zNO+YP3-BMNmzOenxi4lJf4yrVsMMgUIRqk{#|Vw-zd;cw;^F(( zfa`@fV5fpR1!&qXantrYI0n`=d461DL2E`VmvY9DZI8YTcH{9ceH=m9Zy9s2_`IPh z0qYMhkS6nTH%0N zBbimpKF(o#D>0MEFiZXaq(=mCfCRa8Us0XcPdN=&CRES!>FGN?9jETzXnMGGIh?AK z7?ulF&3f)GJ%nluL#|E>sTwq-y7la#^7Uk4ca5JBjhQh`o$AoA>h4Xao=(ifbl;ws z)DVZOA^ipn4W~C`XzIgqxK3iykSd}1J3;)z5MLLLN@xg;OHXyCyETUNI?`NgsVFUy zcvO#4jaTKgjF3W3X?k7$n9x3`ebCniwGWw8*l~KtO&xc1Jk;@Y2TxqK!rlqH8@346 zzxl3l505*h@WhG}Pxkhnc!70CIy{f(HCtitgxyUyq}|X~GF|HFwujp8XuGNH^tQsb zL9K(nIjD6)>!_VKJ^ucVh{RY1S`K^D)4X>hXXhO~_-%dczR!D|H=Q2d@Nvh}D+#LSK4X*BE^X=(DNb`1F&7 zUoPBLxNXYTL2ct$0+n6c27PYA{S&@E;Sy+j3b%tC=t0k$M$eu=&w)VCjiTrEK+mtC zXX}_-`faet@d+ckJ|%cjl1meU1}`2B`x3w*i^ zqWrIar93xG%O;$4y?Su_zvE;sr*^%aNo|E6{ncn*}b3+zLuG&t>(wrsh69COO=6go{QC!w4pb-y{u$P8r`$Bl6ssctx6ur z4Owz?%ir%rty|>Ixmn3l3dh{^>~L(Iq%%@lZh<+PYuYYvVJYajA-G-|u_J8@(Z*vJ zXa6CN>Ea7B_8yqk%Y#vx_JGijCLfkq*ZX>tn*y1`S@WGH`aq1KzNH!&utKi$DW)L5 z*Jh`rM*U`i5%v%05xB96#2y2JWlrPAF@WjispO#-WJhgWMJBVu)u&d9**^D>9WBRy`s`}OgyZZJ+pZ0R_3iLHe81iV|fW9J7 z>f@`b>bnlu)i)TGqrD}(0)1RVanmKll{7!IBq`qA76>!zZzvgTM zsG1xs@6NG$yV;s!_iZ09W5CC}Mc(W1ph> z%vzxa2F-xbBFC-z&P>x+14Y{Y#yR>{Lr%j`%~{Y_Mj%H0c;AJazNes%ZHVc^qY|XA zUi9(Vp}7wFxbCTjkd|#MQ<GEIYFQSB-ET2QIa zfu~0GsavVb|0sJWXdWqhC&S>Lqsp?{>bJJzHo#{#`q6Ps92!>rz1k-Oo4k$qP-Ud2(DgTYhLV&8b`0&6XFMOpL0_ zL#JsMm1*wF#N7(Y^UppH$nLf5Odt)yn~p18rv@M^PsF(;u}F% z3jQ%@RB#O_EoGhrbC1!j10Wmvw z815Glvk!We82U+nP0S7n<{emKX5XAAhWu>czP^=byyom3_{0II84!npMX_yDhd7qxkTrQYpYtBX^?8Cqu(6sO3Xro`3bD<&%H)0ZRTyk6x%0wGUy)#vuv@jY~+i8a|KhsX%Emd6WDwU z1k7h5Uu4RFSw@U&K5((%6~Lzm-T-Xc24vW7c~D8)q-hV*J~^h7k1Uz{?}*t3E)_nD zu1PTS@@c^=+iQsVpu^nYV0>O}_|$36+(U*sza)I>{EA@ed`vK(qi+aiS$#+FLg42G zvn~CE7;Tc~Rl(H%w%}~weS&j<|0tNRFRUwu9Sv;mc>0Q}U-*od z*K||nT;S<~%Yjc7yc+ln!Ii*g3%&%H>o5%40Br7#0&WDZ68^2gmkPccxJmH6z@HYp z8~AF$j{x65EbV`%gTElyl)2#kkpGHcGzsr9!K{m?i3wo7=inCwgLpp?%<_CyF!lUO zFoW+A%(DH1;7Py-1hYK*V#3F`ihu_Ro&kK6;4+?>SS9Xrp{%8sq=hd>04F`W*V3`@+n^_nDVuPK};K>4CR?Om0vHI@_Ys~J$ZsD z&pOlepa~ir$V+;Ryy!Rbk{%-uOnH`JCZ6lbpe(}-!Se;r0#!YLHeS)GdYDHPjY&Us zQcos4`3y#2GX%42iD!edY$LoH9{QOlG1wLGbxZG~akHfmt0llVr#TR?XS z=JWobgQ+V6&l-Z5IVCa622I)XSr{-R+W$`?7C!T%eQ&A5pGhqJC(BLc7ZEcem=&M#(zJEZu(L`Xi4VZQUoir>vt!vXxSg>L^hS8Y%6`TXNwmtUs#7qyPVI9#> z1u>s9mY2p$H!<6MgjmurN$?|}CT-I8rjZX!eMQ8Qwo<|CKnsf^Ddv#plZt1S+5m_C z)?yVFyRJbTjE^ZBhgR_s&nG#*FQdOMV-G3y4Sz_c_j5_k2_V#aUKoT2iFg`GF80DQ zPIwRqYk%R7&G0v7oJ+&pvq;c5SdShJ8HAt8;jhmOwdaS@&mOralUOq&`rxb+I@jJ# zN*xuZjh~T>vLBZm9VUj{(fV=8n0yrT*w9mc+U6;=Kim5L{%4rXPusBe{RO`NIZ!O= z!ZDvsQ_tJ*^~oBXqWK~m_F^w{i5B@Bp7c(wHT`hh-t)~RV|;u(KcA}l;r3n6pUA)i z$fe;K9V-%904JU^NSlCuWhgZ}kjkeNTCD0M3gx&~W>rj~T(-Tl$K}XoV-Wvm5yGkq zZZK1PRIJ}U#MjiHoL?^lJTvpJAz2}{w$%(QyLTMV&( z{A)iC=@B>36%^LC`Tr!C6Q6I$+Vgh7(mp;9dW)-cZ|pwS=5-4@7wJ}g0_)vpevgrF_`a%kL$X9OA^2o_x|1PkJJ%H+m>J zE_c{-X05%%teQ1uJ-e0p^hOP;UP|>cKNVHJ($giXLnTh5K9pRTi3bjm3Du-2%e?0! zXJTbqCXfg|R=u7*TU1!y^{o7&)TFku^766|{4H(Uj`8=tg`4@_8FXL*Z|CE0Me$dr zc>3%5w#d%S$6ViW$J6#S*!69jPq403U8=QIwC-s895i^H6QTPU-0C+8cm3sqPBeG@ zO$M4`xRhyWYw>QnT zeh}nc(fl_4O4grzn_m9v)ZZ@6+Y4LG2{ikQUMyvlE@Iq*GRg<#{fV}R@9nJD5>&1k z0YGVX!@BN!4cGqZ;dGS4a-fFk@s`3G^;POfIj{%ckkx~Z%Fs7}AqxYjH9f?Teih=W zkVJP4(}^CO6Fzit=0=wXF$PONXfJQ!q(&J0{Z12zV}%Wl&|+B1G%Q^jw^S|X(0|=8 zBKbW|a6HKDtKfu}{EicTCboX*nlCQ`rL3BK+LP9K>wMo02>r(CNX z4ifG;r11Pw5P><~q(2Bnho;5(uqe1FFt~`&B+Gmano{EPgJQ=N(n+DB?rRMCDo4S9zBTWBLXPCJEphn^8 zU?`^Fzrew+L5=#&0>fdO-&2~}$DXGEF@Ee@%V9MPptlyU-~Vm93_1h9vw>ZGvoY1Y z6LQ?8<~u0;s4ogjeNbaeS*~4Q5oe%p6&S9*SFljG8a(PlkU(D&*wuGEJhkV7SD-H; zVaTJIJ;5{kh17?4`as|Hz^=a8cpG+mK>E>K3+tvY8430ARYddqMPLmB=)D1bWdveW z2iN@+)9ZnJyU)w?@l`|hnYBL(h?#`M?@Fyl_3ekga(Gf7mb3zW-*LhKdc#mj zY(r{PUndx@zWK-4`lLRrs08|62G%fu-ZJPbA`qi~{{^?JuN?X)M|~s2kL~3hSPcW{ zZ3dHTZfex;Z7^JYEzq|bJnG{X1^uY+Ls*8b#Bn6$oDDZlVEFv1|nD^g;xp%|yK*Rp8 zV5%~GALKFg*}Wg&4}eD?4CN`K=PKZ3?;sR;v-czN=G-RIfUeFvO~Q@_o-6ox;IoJ& z>?*;GD-AWAo#o%8r@|5Ad5Lym9iJ_nJy9EybeVmvBCd}6b z^V$0$F?7xc{sl3@vdqmH2w;}mAoSZBc9>w6iTPa^$;%Y-fti<+9GO!DbN^_b$grHx zapcYU2BeKL6(S!2u5ske`3BMd8S;_l3m|i?$g^xeEBGql+Z@dK7Sn$t_+KWLG=E(% z)BJ+Sv;+UA;JbnUM`T!@za&Py&EWsWkum2Opr7Tr-;vM4z{a?Erp26HU`GfujC^3W z-{Xj(a~p7h$g}+C3ugJ7a|sf5xrAl;uM!!S=UTzc%VosS$>-*BN1kWw886Fui^wzV zO@bNr3y#cPf+OJH@5n#q$UjYtxc&_?-xv7;;O7Nz2L8E&Ul+Us{9h4EUF;)BGM^Is3GnL#{~Y}51Q&yUo8T(o2L)q+&7QSFozeUW zR{IC`M~eFiW|J4OCt;AaF^1HUA=7Wk)vIUnc}%;)hf!8Zc$72FQ|zF@WoP8gY< zn*nnK-ww>Xr^&w?*!(UP@IAnsPm<5)$owu9@B_d_!ru)%i&&l=)BgxIXZ3(tKFdUg z<->g@%13}J1+z>p5nKS=Ecjet(-%SheBf(@UkPl^T7l1T&z!XaZU8oCt$^19e@Wyw z1Dmr};NJ~w&RPL8U)iv^Y+x`suz7-+?p#h^%HD2o1c$pN9mvQl4)ybfYi+8C$2s%~xSAm*%!Drfvgg+a! zRPe>1GX+zg&j$6djm;50+t@t8Y-0-rQ=ZQT<=Ixt1z!ldN-$+A1+!i*arljbH-K)1 z)jM3b0qY&Et-#FV-JpNfazkOWjj^mUfvMk=74T-@BG?Szb3sj60ha-rvI4FIW;s%Z z@+>RrVIJoRzX8;g75M9cSqCb=1eo${!z%={%vKAg&NYHr{`C%jonV&jb+Ebl_yco2 zY@XoTfEm}_pu=JH9LXqRv_l%U8RfGa_*~O4zshHR^;xE$hRqVpI^o=l{E?u1t0A5U zI$kjIO*i?>H|J8sMWB4ABWC%{CzdoU5={M^bC4ea{gmLXpp}9j1Z7!KW;ZD33dB1= zuMo^~*hY*pp|E-3T8XGUogv?<1WKSK))fF>G`%`#`TO~>frcF zdCL5c;G03SiTmRZjEk8>mI;liFDa7<+?sy+9qt(;mbuX>dQ5wevNHPV2{RU!`kCen z7S|_Zjg8F>$tH7w(V@-;oiyX{fA6m=C-rPl1aCzj_iT_JzUe!{n}QND*0}c$WzK0qtl1e` z@wcy^gLI%Crx-$r!wx?J;i?bV(@0?Q_{X2pg1>U(j&S%SX5&Y%VjaF7NV(kA^#S~? z=;hSq*LeMGjK6ZFg!fyQ*Pjn2KcE}HKin?vYg)NKi67D1i*p9?gFSOo`9JCJ=D80} zx0`WfyQiTszAl=K74)*}KMN&;LcrQ%D%JZUzT=MbR;@92A99=={OKv z0JHrBe{WYCrp~arI}fz&Y13<4Hnt_&D%zH&UElJz(~P!&KCRb#M;3}dh%uA#m-1>> z0;Idu&)I$M8Loh&?bi57D~Hf%G8wI^3C0JHolA8rmB&sZ{G!bph@gS91b#B9GKf8W$CG3mxxy{Y_6{=Pd4H@izH!q$%NTBHQTT$JFxDSa&uFx zF*OjkWqaGYh2PAIq-XY_Lm7rJQ3YjG>=u_1q>Q^cd?dTfseKYO*^&L+<~r}tSM^p$ zlTnYiJa`560*7d2Wn*le{7{=^#p~B3QW&-I`dG?Oxuvxkck?x^={R0aarDG3G07@G zeoO`Y!|l-W^^l`kwzSyNw3bUhntWJdEWsGlo7@!mV(N5AIWsI5Ic|;jbnq0@ z`#o4b;=%Xfc%3#dk6`u=PLFT3+a-@*20i;)QJBB)dK z@x94SUo~cHi@;<0IP0Mw`@bt;8Aj{la(HN019qVAa$r~A1JK8Akow&H*;^!x>iYr$ zxm}f}uRTp4U&v;QK6iiiD{1=v(2KtN()7)C^l^&Mv@^fogmv4?Cs0{#zBGL=K~6EC zw*qqI1Y$JI58zfzul0y`H7LskRT#wkIa~Gca6zKZQQ6H^4yOyTBTT zI-ZBV3kk%i-|ygd^+iV5a$x%SZc0Cv3-?Yn44mGvs6f8ws!>0F3+(Ef27Ov@d~Z^H zW770p1bs!S3{>^;D6*@s2Kv^+pXnP7tNM-;eX8RI=;MBl8baD3u-a$P?*TkK>32Ag zPgy&J(}gucHaal44z2MH2IjNk>gz&d`WNujI7v{Jp)UWU>&8Dm2)BO4P`^0>5LX&A$Yg{*5ZfG*i zih%284TdHYZ}H1Rr+M$MOmkl*?uDS-N8Tjv$;RE!!+h5m_GR4ny)DI!+De^Ie4I^R z9lH;HH$wWHEqzYs%o75LMuEpBbw3uHJS)KCbHmfQKLs`Wv21WKY-cI^v5 zz5#TWV75Q@RpiG(mkRz5(DMb)1HDM_t)SI{*MYJQ7?yoG-NX^l>jd+8WIgCQ&`!au zv%82PKh(Fs`=|TKyM@0R^jm@%?#F^b%q4cZhV&Zwh=}#hzEgF6DEKR&W>1C%24nh7 zV780Fh@0|H03Ssx&DivXz?7LGGSh(P5n~*s*$b;{S050|T9>(dK``$pCZEq7^GVDx zfqQer>@&GkK+OK&c)m(eq(_$%v!C; zFbzq;EVrwOrF_g@G4Mq2ZxlY$bF<)?z_$x#`P@e=<@Qy<4EuG#4ErB~8TJQ)88(9! ziJTA0F)dfhmg&wDJW^zId{Oza;AcXR`i&msaT0Jg-I8~%@DMY<)X%kQCKbeB@~KDT zWgHqWb!AB!rV2*V?fN>(hj}S-WJ(!9r;LkjEJ7@CO%hDL ztYS6Q#MdObIDE*p;gT7p5n~Sh-RM*#{u;!=x?u|#gVcS*wPE+e%J8E4?9?S-enTo> zfM%dx_}g8<2cfyH2ED`{%{=_dl0m$wXxp~rv)IZpMd-n}>$N@I6Mq;BzoMd#jpT$N z8m#X=j~JO0&kw#gJh-#3`GhbK&WU^eG(KYHjuWUE;~N(_Fo?mb8>8HVJK1@7{#X^J z_Hov+1F6pNUZS`E-tY79*JnQb!nq9%wehNGGM=b!DxBL`jYI5-f@niS;TZ`mSJWq) z3YW#263vZOv8KYaqU)lCHPOcE%cG64!ek;*+f*1`*H~B?uP-bpD6EZF7Ot(TGOW^q z=>;WZ2LCA-udk|Y#+Lv`u!fS>>btG%;!W7L0p!P`IYQNaw77=})kRYM2uZ2(2Z)@m z`l76aQy^5`dSCX?G|j3hUGu>1nlX~2Uf5^*{+|ZoXYr(7;Ktn}?Ju^!+PMJM z2^}wXyxQ?r!FvUd7JRSkrLK&^!ClvOJyY<*XW#XMC@7=gw<)Ua~X8 z+!6hR-@m}?Y~HzcXWovC{n3USPaL=dCtyyDc3HaG(EMBO*z@A)|J~Q$|7zQdZ4=rC zwH;{P)B0-bi>*($=Il&Q!TSZ-g>MuL?qV8l>H0q6Zz;6;-a$HUGBMojPaAVuKd+Ow zMjwx~;I3@LXKKh}YDS&VQmFCYg!q4o_%{`%r{LiCK5s#>^@ml{q5w2yQT1ZS!$9Z$ zV&ALi?pv)a-)wxUOTt#FJKI~`;YYDwBgXeuD^!}2jviuK6AJba)=5VXd5CIQE>PL0 zU>eerfXj>#OYs3Rdnn^rTyLZ&1)^ET%|BZNwFo^NqB_x^HOD+02VL4&9c%QeY7D7mn%rsOz>V18tS}cUr3rvgE9}YBB%+ zYpl{JIh+O08!+c9cZeT}`f*)DG5P-omJf#-g|om2p9I{wi#gjp@MOn~p!J|HRjkQ_AmsisgLt%`Z2%TU^NV&_XIqO2*jw4tKn8muNur-DMx(+U{&9( zz^=Zx;PHF#)Tkfdn_Ydopsxj<)WboCU!vK0eLuH*nAV&SZ1h=bi6ei%zAN6tS zt@@q@)-Zrx4hnKAff)6B5^h)DBZb zdwUqvtRv!?p~;6$Sx1BnpZ^od2WG#?{+>7j%IA!D1*lm^1ilUUeBl>>nsr3*w*yxT zKMH!OVD3d-Cb$KZap)Srm4aC(EN{ledTb|VCSc577WhX2-vvuP*L3bB1|N@`Ia^2m zY+%z*0j~spQDi{uZ=%c#Q)iuRm5i`LsdtiyV#_+Onx+d`Pz=Nn0xD42=YXY+# zxc@>vr@vDK*8mp^ZU8=6Fw1!^v6KV9V<2XK#`E&TV}U;@cp`9AFw+nhTmXET;Dx|f z2xggY5?l?uU2rXMo8T7Usjyu0D+4Wo<(l72P_{ktOF`!d|6I`dg3khF`=AWtT?RXp z0|5-r2ef#-(HK?zu5b#+JuM3}b z@}}Sfh6d+n`{<8r4h!PymlD_T)KysLH#oVZIx=iza-7w6$7Jl>x- zI=rZFjQ)Bk@4=Hi^OC%A_UgRxl2%;ONA3hY*c-`5JID_`?bUe`T}DP8zBaPwd?)Ei z<74$a@Z?aEa^Aam1+cBrt_QPub~8pG}6&2;p%}Ey^=Gn z%nZJ}d6`*=LPK1|$0sw(dgK_7%pBvgCxwl2f5Wkm!VkOIw?EG}KUHdC@vo#;?ko6{ zhlSdWivjvspH1b!0#62n821Ayw$%~a9*S)jF%E?`;1@zq14XsVmN3hMFt>+cZa1E} z*9LxH4*d)ieJ)${eK81ge;DR|tM4a)-(#Vlfuhf4TYcj3wNUX8=3Q;<9pR%X7N^eh#mP2DgBcx!_o%RS?yV#jW@MtO(ew9l zoT9P5?`n3Ay2-N5Uw{9oI{=QpAAitoT2q5J$fKVG4zhXlx8XYaMdKZa@mtEHUrQ1D z4aMF|75g5<#@N&G0Ja2N)tusu;Xbl8f%;!iJ@1MfbEbL*eq&HzV=ff-m?{Bba`}nz zm<<{%%-P8Zcl7o2yhS|&oeApX-Z0IWuL=ltKFwgyYA~m4_#NPwUj!cBSaav!4yel; z#~R|C1$X|D(a+-9#Qn$GJO79WOke|g1}Ng?8}a%71&8mC`v%AaYWbz(x$s@<`7($V zU&O8n;^a)E0>5kwIren_F>D!@J+~xePs*4H$1&7yJ?GIaVJv5Xqs4L-Itnc3%x<9; zbqlq)Td1>~P$q*vK-|aj5BGHXPA2u6UWMD{VcgdNEjLMMxgJ|dhW!chf3}@1GY6__ zXy8cW{*Ev1TxhDmHk;Xt>*ZU8%r9a@up3^w&7>b0=)HgXbFg~fUphr|5e=FD9VpY? zGTp;cs>(GoZObEIyinrQV`xB4H7YI+VO zr~XGOzv2+(qUEDh%TL`}PU_Zls9R}oaw@NS)vfu^@LGNo($cH`s$ca?6*=|S9~SPd z{8hikuTSpzV0r8{Ga?NKEJ$RoK#o*ZZUVy?5;KI=i~|cI{}pX-`WBUh@mN zUAjrOD<`xa$+s+bLg#K+@0PpzJlrSzuBByDE4bUI;OB8ciB)pc$fL7IXOGFw+Mn*@ z-*Q*(!?{e6`@_c`E|0`A5mI>QFJ%hbPj9biPqc4rzp4F>_J`V2H<(|=7Bh|ry!8Cz zKY#qb!ZH4FSI@fo?5kIq6kd0fzn^8YqSgNJvDbMg(qTD;dzOvl_d=VxE*$CaZSVS| zah(_O_j;YfpBnL${kdTK6T!z97Ipp6=M|f8w1wAgUemUtjd^0;BKRrdKjKPGVy|;> zdkbo46KZUew+;1W(I$hAT-R>sdKkKPdz+!bwgc*V*gH~P7kub-UMCyh%;~#b70A2S zS?q6{^tnl^yBc4+?pLi{_xK$?Yvz;wCH_{w+0WVWTF$;4KX*uO;hxt=?#uG~3_+Xl zPiaBVC_DPPPE;+4D|H`0Ce_ds=!8e@)kcJ$pj=y_K(R_*L)HEVHeMV@p>`N$mZu z|Ck@?D(x!kDmRA{QpA9bI>Itwi~oHn?%u39V>_+c(|1>0dWJwMm}q z{WtBoW6wi-o<`3xvH$e35BtpdahqmsI(yTqO>6;E-HGf6{%c(qqm*r{VLpEK zRQhw>(pGO;X+3hbw|%?U)xOQ^E|V}a3CMJzAM5VlVEh~4&l<%~o|}I!w!NBm(Bq-j zJ6dmQ-PoFFt!O>HwGanC@WV7*$LZ}J)_L%gWg{TZD@b!ck>;L;d8wL|GZ8gCaC>0+ zUR1eCovTqD82UPzxk71-A8eE;$IJHY4`wSP+gsM*FYoX}cdEbgd;Ur740BtN%2;~H z{5r``fldzeUCv8U7H~Dfo`1>vZ^aE`y|}}#=382M-!~UEsLRllf(lAfMgDms;Aw5VFu=Trw;--8RmYp6m9?=I@j9Hq;p#_yg0B`H z*EWK4`6qk#z?mdJ7vbc`4U>trYinZ_Rf%RSctt`!UO_5&(VBN!Dz7fubg8#gdBqhe z;iDlx2;Y>-GvTjMUP)^BTS9&i{>!O66P|f5g|(#+b}aMg0mHtChJ6r?+xJ+`;s5Qa zoUk~^k4cZ<6k}oVs#meHlyy*ZNge9KDSJ~fQg6h(1-L1=GC8;`*{RNk##mETv^Ls^ z!&gmS#meFmw;j|t2dKE1Sz$6_nRH9Of^s&W)}C-e#2nmoeNBl6N3hKKDor11&#E=o z_PK|x!b4d>NJh%TThmeL;ps4P^+G@AzCQ{U&wyZXm9n`-*(AEeT##%Ur<-A~y1CID zW3`D-Yg#5jDQQW&e$XVM{LFLA6EZKh7Dw1jY{g-0M0$I+;UfDe`wEtjDFG*dDYZ3? zvCEoc^;K7>s+1Zx_b@v-GC?X<&ajOD9o8YQW&W7o26t~V&cQP$AuhLKU92jZXskHd z{m9s9)9$a*r{vc42pU~F(-c9-OzU1;j~-Omno3iV)(k=wPAf`hS`|E$SWvNY$tl{} z@f~(UV`6PxtggU*nr-f@w%=i|X^htCch{DKbC4+?W;Zlf)D}GGhdRSNAaxislf>)rg3E0)Q3;I@r zM}62Z3(|KZu&Zwq^ywG}uRz~+2}2&u`_LCbKI!vjX;egT}WPyJlE_j{0Y)2m^I0Ho{d zDc;FF$f*uD-cx&!>nYw9J;?PGFVCN(r?01Y&9`qzh+ED*$+d$?zQ(l%_0f}@xet*U z6H@~@ZbzumJl%=Fis^SJ9_ZDeQ(?!#(vR)_afuhum?xlbp(Yj_O~WH;`gk~{1xl!o z`y{IGdujUqG91sSDsl9ED@|X^9Gf2Mi#YmTNz=!BxM#v&jjH}hn!f9ykLO9KZz8Ow z&)o5-fk9_OANvC}s_!>x`ksM4_7T)K30C#}1#%3dI%=VBgH|H=^rJX)y417Q^%Q){~da$o8{BboT(y@`S!85xIzT1o1<`O zj{Gd>&@eDira4s{vbuk#ZgzJAflPDSEpQ_IQK18<`*-YISr1KSxL0bJndbI<*Ppc! znoPU9889-vNPr14?apGr%QP)LaKeAG`CvskY=>8=1C;I?#61s`XInWQ(Qqu~Syt{{ ztD*ek;&=j--_d_d+-&DVP#!-PH=l_iIHmTcxLJQgaPs;+adRvjiV+T5Qh}j<-jA(@ zZvC#n>Kp2D&sq)rb)VYmA7*}Mp8i&TgtuPgt^Y`GTUz+mH1}<3ZuZ}57$5sp8k^rC z-gi^n=sZ{81+#z~k00j>fz-J%TqVUiRZe{|Jop_0-QPl-SC5nEr|RTn5KRVm_A(1atiTgy40+7YjZgI41Z-P_Dc(>^b0HE|}vvb&>xh=rw|8 zfZr;3AMl-m`{6buh@L&K%M0O!Qtn^Px<^7hnVsahtJP?m4BMU zU*z!3xna>??eH5M`A<9i8yx;FhyP`V|ER<7bof7T`2X$j-*EWv3g+F`e-wNl^8IJx zfdDW*%8qy*@Bm_@pJiz7mW}|xOb~t@@MOUW(Bp~a{7Zpg_POSsY3O0SEFeE(=Mv_g zY0-b4@Xv(2xn~-BvVboZJsd^q1hdb&OfaT8-a2CFVLfgZ%=&5^~y;EeEmuHD1w*9^+{Mn%Vtt0Yp^l-Lb`1d0{Zwddq z;Qvnew*vn`_F~d#@vyQuq>aA4!>3KX7G0iz75#iksrZ+- z@I#LLR|T`I{+$>y%OUfO@b3ZtIl+$roBQ%1vj#G62>%7}-xB;5@b8Hup0@=ue-X^z zJjg`-G4GV`EBIyL5yTN&&trwpx`+t=3vh|Zd=Gl~tsM1?M%m65TncRN-G`oELS~uB z{~7oKN8a4WkFb1ajEfBGwn1b#7f1?b-E9_`sgSvqc#?fS?iI|j|NDZgfZr6%KHz=9 zgOLVYp=-h-E8bDW?6hI>h5u9F0%DmS&JjN6HfIy_L4zq5{zB-yh*+kJmBRlM;!O~b zvhqpcPlNm>VwpZ}6+Vy6br6rX^1Fo3zVJ)LGCjUu`1}ow$BAWH`J(XIPyY|`c&q1^ z!cW4vkC=lP%-gW+<7oaDR{K5Hv0{!@#Q1M+#3W{MjTC+ac)Z}_fmxT7DFQAKJR6wf z6Z!Li%ZR0KJXyq-!=B0us0yha}yk_ixOabsU;3ov%3~a_1$grP$mVC5Zn*S0!1Gq~t`;fN;vv1ie zm~p)?nEeJPvD9-up!ppG;ERFTM#*mkHe(Gi>nP z5m3rxNxzf_%x|LD4=@bBq2jYoclLPvfx)Bb^%czSQp1upl!0I-!w%&_4a_9iEa_LL z3P#iLrU_D5`9yU|rY9(LFxgB^Y%9ds# zY$ocC-4?#Z= z%&~)R%CkSugH`$Qz|_fcWFjnOICe}GKHvE`&r|+P;q#q{?|=+D2b6Bgu>a&cpz5y? z8OD2wgV#GU*9)KTb{uo5hhx+2!e`h!9sD4$>i?GTIj(*emSH(IJu8@F7ROnpll&hE zpX1Ie4(8aZI)}oK@}odU!cylUf{|SF{zVzEyszO8<7L=yQAXzQ-xbWT z&kAPP7X*WNFXNAfeVu&t&opleX4qc~X4rQGgLog{kA~%Y88OS5N%tq<4-C2hGiN5A zL@fNN#L{nF}IS;u}`JnBBId-vcr+(tQ1s@Oj2Oa!~VCwm4q=ypUm|>B z_Vd)qKF6%r37#u_mLdCd$`G?ZCuY9Pdl_&g!d3{M&+lb|`TRBu=DhM+!F*;vEBHsi zTz8{RKEHPf-Ve-oV2#)8g-X0!>mk1dRNuK+KED$F`@p{uJRIrd8W82@BMaQuBj&ti zuwb^e;esbap6fD{*#W-!jY{DAfw@LQ{%ydg3w{8Y>oVlCeO=&$c5}zXi-boHFkNKPvcT;3ouk0dp-zWqu&|FW|Eu zC!g`Moe?u$uFDWl1AbfZJYcTTkk2(7AN4^zAK0w90Z#@#TKEh*)sdMdm~+4Bf*J40 zf*Ef%(sU%>%tt6c6n|i*!ZJN-bnODw7p!Bj-(ep`(;rs*EA}Zpi z`~+x(SmK%_nC-tV8m~W+Z||oU$*1>+@$LO?=)VSWmBKQ&M`1MR>D&A8bNkHjb9+oY zGY8J=*srhMJa4<<4G z`m9a%5E#EUEWzIlpXKjm*e|;??3djc_RDU)3Un?6*xR9eGpCV=!Kb`^=N5Sx(-U9} z>^ryE%P1ur)OT))mvIu|;J$OGdl@qbhxDCW>SdfvIJEEFle~^bGS%f3ZZOj?w?(BcMD?CkulMfv)D90f>9G9~{!sLGTXgfUQ75lC1`Z#d+O!fOS zNb;4T*YCpk<%L?W(>zPPeb2zhjngLEirxT9!ZP6?_SIb)m~r!uJfVTRClDTlHRoUJ z<&Z2Lp;yYe)FU}n9F(ixiWq1z=X6c#O*BD#fdDz~iQUxmzb5#h51{^4exk;t(mfT~ z{V?U<;qWAvM)*LR-T>70UtyJ+haJv*SYiwZ#`GpP1-|S@4kgFlLv>Rj^;HsxQNPpS zR!py(VcAO6s2}H-rZW?d>O6awKDPnb;n9y{N;#~C0rWP&G2LM(Grz|vo=|{^j+D7T| z8#um0AV%XQMUe$&5gPZMV2%W5I4t8G49hxn^}Pkg72v5+ed<=~^8deZhD`HKdgN!s zhMD&O)xmNo8=6ibn9(HEpJBDG1|Y*15{iPt(k}6jQItPz5p5cELH(WR3Geaj4%Vm~qPCzfb%Y5C46_+=69Z6mScczCN&d^hy2sf$Y?4vHGUmp5=z)?-T@YD9-N`1ea_f zkzYX-+8RI7mys;LF52MvY`5NY{t<)MiZ258Sr0$un}PNtI!!A>{hdnv$4Vq5i0CDd zK9|EU;Im0WcsBnAOkfCem8VY){0M0J)H)7*l@bLetR|nrP*8?&HYdjhJ|fN)x%5)t zjf+oa{zk3pbR-V?vc=0KmLB4I30^I6s=vfP+CSc}+kVydo3`(|;gX|2oqK)m7ji2{ zH|1@~+nJ}ACcHlCe{-^ON95k;-{SKn?)U+xj$1PBqH+J`KkFa-1NNoz346}YhOU`~ zk*>-UnojtEpR-ecN#WD}^?r23WqmgFY3t+fk8~B8PxHM_#-g!3;Qs)h>EGsm#biU~89Xy);^2K{ihac}St<`eR@9;0^@GtD} zKN)gO028LN!>{V_5{=d7SZ?6p`aeEvtWMOKV^adzms_|z z7OOTXVk4H3)42JE4+4$lhcA_sr7^=2sN2Ae$H(}CGwDTP(k@B;Tp6D!3mOv&Mt^rn)G?{+*V9+~gJ{W9yUn@YX5ZZY-qQ{U|vo z`<}VAsqECMJ@~@Y|BJ79?Ry6MA2U$_Elq36^rOj#CD!-t-sC>cTGSnkNQ}mtz83X= z;Ln~wjl%S`s3pU&_lf6t0tEZOX2EidDu>lDfM!2wp~FykHvAYy%~R<0xqm}_2V0Bc z-nVum46FK90lWI%8fn*Ds1M5)LHe2`jMfLg9oFs$UV*-?X>u1sj^nf%jrR^Ph*e)L zM^)oecXf5ZjpgC-rc~iz9Q)3yCLHyU=z3yOouZF%>P^J$}C#a7<0&5xoy;?M; zMNWmN-+l>08JfM&ccHXDP8b+3+aKQ(Gz^^Hqi9T@BoL#1e4W#@(reLZ8=mQd>OkN4 zG=2OoeGvnQQGG|J>01GPDQnl_?Tdmfm?p3s{MO&A;)vB#g`&Vn-P+PUt@;%SY zIrq-JZ-QvsUw_~K`zE>XJagvEnKS#`x%bX+s;&B&=iU;Q*3pnR-f^qEAu)M3K;8}D zqdcx%sJ!vuW0+E0G}|E0-YZ!*{dmRK1}{gWm_ZLQ3TZpp?{!CInGZq=bf{qB1u1Q9 zusPTBeh0c5m8WjCUHaEq8^jW6LipUbt! zQ`8eS{a41^>^Esxhxk3`7UQygXD-rzmAEGx|Bhy@Tu4UV?c!jc$L}>C6gT6_MJ;|( z+_XrO1*`w;;-)*NaX#t`zv+uiaU2IGH{onjCzBzi9=_2jOp~43+3_T@Zi=FO7s)m2 zrodd&WL?*9Ir53!byM&#f4Kg{2dm82iGm-3eK@fUXh#WV880A~YPEr@og9{VCW#5c(Y0Hwx|xo4V?H=UswXpMFh@ zxc(0NX=231urCOH1$GOusTlT|8ZndkDd`BCi^iUbi+Bp`j>M3|dfH7e`xEx(q_fY> zCuYXOaD9oG^|wrTt^z)o7&7@zDc@URSjsW)EddV&=KChP&OJvk>-l`)XGe4*G30E3 z&5f@q=PK}=CiLfFpDFZzfNtJl0?)I+n}iPNZ6oeRgzxJFGh40_p8J7sBt~5Ak-2R5 zx;8wYSn3ALm3S2F2EnAQ5X?GrHZdn1FyFqk`F);I*Jj{PCcxqm;ekeXba0Ynz zzB1{|!y|CzvNFRQhby<2fo7dP0sT{Ol9U9do^*(QN-!+bei-j~;FobJZV_AooBfXF?LQrC>I^Fe%zmsVz|4<> z9Bld!&^Zt1+>SiNrVjz8+U$?CjHU}_`Lj>aGCEFhA?QmS+$eY==^2JS1Nzkt zzD{r{=yyB#e!*-P>}ymG-%Tg(2l_jLi-A9I_}PzW-RLBk`XAunUV@q4QU{j{Mpl@7 z`O5F)F(6D4F3l^ZP4zeR&8@eHkH*wn!P#)L+-Y)f5pRY~(OT|oX4qZ=zE%wSy9jrnZ!6x~c40jAiBM!ebc6`sWnD;r%lB1QA%=jyzGh}R(w z#z)N#^Co%4(A4kSsauC6PPCcjADz_&H8kPA$EWZ2A(igQI(nQ-R>Dtq>d=92{jnaj z>li#>8`XX&X71VHv$(N2$TTE__I&<F9H%$UrqKEAq(c#GU2X(VCc8~BgzJ2%g zryIHZu_p9jE>Ju==VE`VoGr9JpGMj=9uPjK#64X2loD^Gkv}{{=pYS~K>qg%L($ng z3`b`we0h@2e8-8F*T+5dhp8*D$Nm($bk1oc9sTq;p8k4PQc;2l1DQ@}CMg+M7fQ!{C5nSL+PL zWH2&wQT+S-(d3^ip(J1!);a@|Y9-8RouMpX;Ct<@L@_jAxTtl8VFANktuvTTlV#}h z^9YH;C?@$ZoD%-T#C=8vTTNVs(PS_ZxM4&q62_3hWYobSgPT$2dH*qB@{ensfg_7& z7{1*)!?=KfA1SqxQLHyshX+FjQ*|YohXxG)C(2;I;1jO#0aq4A+g9S45HRqKc84J% zW@5l~WXR=eGbvzbiZVpROb)nsR*w^zR`MwU!@W_4h?v93#S-0C__Yv!^yP@mp?z`a;w>lfPs5DNMgwl5pzVqb#=g{ zxxssX8^r@@en$mt-;1B92JJ67s$fF4mhIMH^{+C=#L4w zRIgd|GhNZQ0>4OCG@A^oaQUl429`mjAm)HzxvmenDAvm2>Hzw^0PqI&8KGLIUw|O^J%5=oDq7a<AQKqSzNxM;SX5v_Qc49s6$?E2>hjV|j68HyHqaR_m@$m_uDVR=dXgyhX z{5zxEOFVx%lW)1F8}6R0hW;yQjFahW84dJ$vPS#axaD!;U*P$N9uqJgY9+El`DIZ? zL|x51u@b8-BL}18VaiD0d!0Cc%3~W>gID6d4KK4eWl~L#Ufg-w&o}jn?mRYlH3;!Q zT8Q5C^^4fhX~b!oJl%P0>S`e1gERp)%Hd&@>^{^W#E_hHB|X2GWSSHXXDnsz8`Fu+ zB(Gys!V~EdkZ;$qw$WIXuVZDP$zQN+q37SgK#2`L#s5$_`&3FQ$>-m6Ggu1F0I8sf zKXvphjIi7t_xAd-SpSHC{rJ-xA8fYEV_%{M2~J^cK2|DF_jpyps(ZXCYKAI0-%$#| zD-e9Z#X=i!m4Y8BR}m|v8t|_o|DToZBL$x;R~|bjPw2K>>?jTY*O zYrs%Y&Ywa2>4TqGp`H}?_VU=vsX-*KAy71uUMRus(GuLQS>)xhqg8|8}JV-K;uBSgiC_fpoXF_-7X^r{I(M zQ)8ubh*AUTPozsnXWAULZVq~C!2i#5{wB{~Lb5ToL35l@gFq9zw3b1eqg8OTCc(?& zP^SjLuWU8=Im&NyAaEACHPl~Ukily459Yv!HU~Hkt6_@Lr6UJcayX%}(#?6mbVl39 z8R=Dvb38SW{ufLKqjkdyypN**jX006E^i=h|Cj1(oxjnVILP=uPTw5&CBtZ&*l{r; zjcYboBJgg5Cws|9G)i+2yYaQLy`UKz&B@=V<)l}{VNeYcdtfe_a<0$@@Dyp90ezLL zm~#g;;E!eu+uY@vaGNdmcw>YpbYt?bEa-}u3bOgL+swDU_i#Is@Q%hLBldFmgocLt zx@vReNRz%LUR>4CP%>%hlIGea%}phZ!B3* zx1^-FxTL;rLCK=(YC|#yu#g!1ld*0|b^S7I6+9$!2B93h*PPR0GMrc1#b<)p zDmxy#y2IG-kq~;;WpWd?D7A@MCwBZD``lO%6`EWd1U)|%G>or7d^8`@({S9Ea?JWt zj;b~|G&Z%9b(+{Au@I`C+F=TgiE*l|kmJZN6)uSv5Nw7X zEp|HZ6XFxp+kfJ%0vkx9)DvE{)hesnnjz}eNIHugJwaCK$@hXNZV(n_)x&GUyj(6E z8i@)CIc^n>LMRR|Z@=2yQlRse-Z&=pl`R|mzxdvc{EJ@j-`({2CV%4xt6PTb z^!M4ec=f^p|KnFSJ!uYD48Iq#VNUnoMyTKUCvW<;9}k&-aq8kQ;2RH9W76_csrgqoZjlLtAFJ`V7@_FT=IzjDKyHz=o5eK z>TRoc_V|4}?*rA0@?Y8bB;wfH?{==dxbvN#ZvEqfogaAo!8{yjdb&Sh-*04AWuAg> zXvV)@(K5fKp@rYl@Y@z|2aiFW<4-I;x^Q9PsfB(^mzJE)2ldYGxc@=F^6&Jo@i!YC zKlSIY$=cP#!S8hXqSsqFpXD8b z<58>fPRV=D|AlW4#;x~v_WXUex8raAdmVQlG=BBrrQXNmkN0*=Kkn%MQ@^O#R$H>H z3$X@UwKiOvc3DV=w&~D-!h~-Ec==gmXH0eM@#4uD`m@!xzA;X3$G}o*a_Vkx$C0l^OQFj)Z`%i3TeiNM zw=2&-sOv#b_|#;eDIvEWgthc^{}SKp|H8^K(?2{$VSZ$9v)s(Nok1=|Yx)Lgb&~&H;qJn_{Ob%)$QjmSw{0-QBgKCx z{IW2u$Ng2P(O6GHyzdqM?(>$d{~Wycx;}#Z{6pcdK7VTKyMw>jW4J5Q4s8x=$L8W+ z`}dmqt%v907Ukn7{CKVC@sBRsdVJPpoBi&s^7S7TdOP^tnsLAOjX$v@F7yd5i-%I%)JmItT+cLELl)%G$N%MBNvMGU7O8iaXU7zpR z`tsl>&?o!=HPcGeaFn6(U$PbP1pTU(`hy#JaILnx*2~RIFU=3uS=v0$miN^3SHoLK z*NuC*h zh?dd3{A#>5olyxIs~_%{<3RPz@$C1k_N!8*svWOsS>B0T{FA~O6?bd6wzRk998JH5 z$?~EflE&R3Uo^KvdQ@)f-<8J8It(4HgI_gv=X2M16nm$LZ?-{Jb&MTv7kjHyynU(A zwo0Y-Z)saAtJagE^5gn_Rd*<7?@BnH(P8+1TUq{FOC{QihuTI{8}E&kul3SX-*{e! z^u3cFKjppkUa9RK_3Pz$TWO87rf4U=cZ&JuC34LKV}JeD)KmzsTdDwa{*ZLj{ty8rwI0LR9`DD^ClvrM|7vIS)ui8nskL1sUykPzGw{sAETE4Wb82HVtjKt@TF;WsYZ!r=d9L7j|7#Jh*37$(jtbS=# zv&ScIF4g1HDVKm0(fkINEjPS|A*NJpeL{%{jN$Dh4R0f9c>74h+eRARF4ELn%Y0Hn z$lF4iVr!Z0_y~FXN5k7b8om9B+q|u#;q4p^Z{uiq`$nVu*7bkKZ{@K^jj)fSMlY&u zp15q`!rI0u*utZ7dGmt$%7*5~%I4D=a4O@mN6iqvk#$RIYFEr$IHj69eFWsuP0JSG zH5okDLhz*xwT)HHOB?gYc_5ukzTu`)jBH>dtY0!^mWm7gh8w>rHFaKNEqBAKK`RV| z;$d1f5~Z zh^432HcqN)s-3djk=k$s)QdC6MMV-ns&+|@(Jd`tm|NbNHlMKvNpocb62ll{xpHEi zt6>n9+L-vH2+DA{=%mF}jT0Iht4^CzVGF{Fl1yA|1jVEyjrFxl7TFxs{I5O5>a4rw z21*S#J?fN-8P!2?jzw`UHRTxUE)mw(!D~AD*D$;-&N`!s)*US`rf7Cqq&&)5Q*1e? zZi|}J^3$zmZkCM3u8Af~nx~vtZ95=mQ(<=eP)y}UGS<;pQ@l7*xFc#$SyolwWK{`v z5N=+2bOUNRE00&rvcM+BVQq6`TU2XV-YXxN%|ai|o;?UCbd)F$ensvAm5!h%sm= zp;A&EBuj0nn{FkRM07=!%h3lGSD2}T77|*p=>ff<$MmFWf0g~JjZAy+a9A_zrLHWo zlMq&{ApPGuo?C<3N^Vn!u{9`xpl5ZPzBEoLDy{xYB$D*7XvLbZgT{GM5dt=mW;-dV zp~Fq9k2YI&PeJ2=mu)|xsv;*G0!*Wd6dvMKZR=KGK&e@}Y(af(pxhzjOm`otL()vA zFIs}V2u+`EI`ObxdUlE!B&V#*_}NA<a`)pe5APzkTH zx~ie7x~}=OF#SPM*}hTZ99_NiWQ+^7bCw2;UNYHafuqO>+cuH4K@dt8?h&@hS|JPA zKQ%9|YYOsiTq}y8KMArW&rf!X;UZ!}>0M=?q z>mAm@)|k@jNNY?|pOJ}s@I|ZHgD)EG9(>U(_uz|KvWcg13>Z zz`u=z1^(=s_Nd>U%O{Z2MivGBZDdm5-$pjY$F(VA}%L*U=Il>0wpSGPHH zZSNuBYHAlOTU438;s5{p_Fc0#diDlM?xDQ?aPlQryz*v@-fW2zFXLM?7?xsE$XDkG zRKG)Gd{=;nO9Euq$s4 z;T6ccH^z50`1A(d<@+fdZoEH1yoI1m z#)ajJAl_Gi-E{m7^7KX@UO~JsN*K~;o`t-%uqlsvX5eBz^P^x_-ax$WstX424&?17 zF9~AiEei9Q)kfu+eM;cge3=G$y70iDjEjcf9J}%wAdmV?#?>8{${Qck??lKegufb< z$4`}Ad1pBCxbUFzPKe370P^Zp7;KftkDFb2>mhF%{3qjL{Ze^n#N_=Q^7_GFjmqQa z(5}3v9Q_Uev&zFeGe!r^zZ42X7iK6^jG;Nc?-h zsDq~W0m##ZD_0&r|911Q)R9-{$Qv1x_hB399U7B2&5_6Yp!#voKv%zPyhGkv{_#9D zSHJm=ydp>5SuuIN+K{(0CU1o!k8jkgejEo}{mLPaV~861(OeRfx6zR|82&2n!I-=g zA#aqCV1HEJy)k(=IPzFuRo*Kxc~?W8E;Lf2%6l;;ZwKUYi-gJG;W(o5k{D1KM(fMH zsStx4$opqZ-p;zPzhZq=dHFGUJ0P#M^34Mu!>D-{^0a-hzN)+lA`j6U^Lz^5ObVu@ z=^Y!B_YUM0!hbR@mY>S|rpN=yn3j}h&JYx5pwpa~ye=oAAA%B;$MREoC&lE=?}Kk8 zIZ%pG{T9aLjY6lJjrD!X(>VtF|I=ggE*@g{2a3z%nW)r3&3JqY!|;^Hod;CEjWKyM zOYFW&8cy{)FDCB@$V3?d>e^t!=otS$|%+2`@4afgn)Ysd@ZPQn7&XJ`*$J~6>*QaCQ?XwcQ4qZ8W z)|9E0cq&slyP|TMf+Gx=G^xVa%_lchHr6gOcx7$l(!jB&!>xkFMQ1fHon{|>L{?NI z4;j+djh*F5T_Uxi)0XYR$n2^gmWwJYm%01TMi&j;1yPCCE_+9`Yh|h3F6lT%E~n*F{lZnYbd~v> zCR|OctgK#9RoSqtv9@w~U1Kwz-Ld|&eMI&H9)vjzCNZ3s_`pYDj)7ZW&zB)1pIC-V z_EqG03^w<+BIZ8u69kuHA~H?zk3m0D@IGj3#|l0M^eVyYpv+>yJbQ7e;Gcu%48fN} zpVfk=0-rB97d)2d=-VPq;;BPsY{jc(0EchhY zw+en6I@~3AEAoN&&E)TjG`=91`@?=Dcsum}Oz_`ev%e+(T+z z0%6w)egHgI3LXx9t`p1;upe^hzY^RZdHAM7e^>C^&^;S^FfPjJBzP0#6bn8Ayyb#- z0MB&jM+?q}Zp$3{>4JZYcrSA3mkH*X_V)`u0QSRze*`%%2)-Hm{88`&(DUzte+Zde zpry_)K_Be*XfWydf>(lfg5WDaXPqWL=ROM@`eMO9fc*7>mmur~g5L-KwSq?@t{(}W zj{M&txC%Uv2tEsPUKTtU_G^OAK-hl@Zh`y^gr`0?A|FzMXCQ1p!6RUg6`T+LiGm+R z{v0p388WK{-vJ%gIP~ubu7S)S3Lb~}ZxIZ3Z8E9=S75lQt(NT`G!M(NAPpd zGl@2@{QC$V2mihfeURW^A)lu@^dkj-1NziC^reCe5bt>oeUso(;Qz5hzg;lf-;)mg zIl*T$p9S9yoj(!05cE#CQKkN*cNbiQv=4IVWrCT$BOUr|!K{Nz9r`lCCm|m;IrNJK zb4~YlhklP>?k4w~Lw`x|$B^@hL;pf>2k`HYiKFV@TQK)BK2&fIq_ILUw}3lQ@c)6# zZwqb#-tN$Ebm$K`^k*IV+YbE;hu*D&)ra!?3BCvAGS;D25O;!NFmnW#0?&7N>WKRr z7LVsH6LZdgHgRW5KSwb8lg-3~u*QIRFC_;5e9*5HdLGjLBcX2x{T89$1NuEee+u-U z6LaDZ^DDu0J|p~;|FZCNz3(rAQ^0>E=7a_2Gr_!%%ghON>qN}7fd4?j^MQ*5ZwDSp zEVuRt3+DaaRN_H4jTOTG6VQ(r%<+7I@U%eBdZ8bM_HwS^df<)3kUtr^Z54bR@a=+M z1HMb}=fH0ecLRWVTW~MnzY&W*yNQvm??QeK=4HeW17k~K!$W-z6#Cyl?@Qd>i1Y>s z?gQs&;pv4sGg0se;2DD1-yThj=YKTc6kG^gCp?#dr%~`7z^4o57`u*`H(xN{5nK+Q6GJBNd4DB%BJk_PY=AIt3hoE|2{H1=L;K|t z4{=Z6eFTpO?nc}b{xCfR7XkMd9*&u%f>!~L7R-6+B;x%|P;aW>VmN0LOB#)RgnV+8sKieG-exl4Hz~51DGjJ|3;-$zwz9staA z!R5e56N~;Q5F@S^A?GB)+&Z~Ico^48q0a?gLkyXW>s-Mp;A@C^;{tQNVCL2B#1hy2 z#E9!m$az%ob->RMQ(>6j31-;Wi6!hi!v6sHKNHNcIva}kcy>XPBbZ@(5=+>8;{KQu zfxlRAKa6q1gokMyC-ljnPbE%42+Rz@BY=+)ewNXE;Xe-iCktK++$cPhd4|w$0(~8E zifbcikAm3`{D3$g0Op5++5i8PSoD8@xIdmfL(Z=Se-8YV@G!0yg?<3W%-4w_lX1N% zxDc4w<&BlO2WKUC;^mN8xE zJZt_ap*JC}c|y;_SbDtBw?UuNh@l(X!nXxa2Hq}s9`I#?8-agJ3_UsK-!AmKLBC5d z`}6yWkuIK3@QCnNga1k4c?vww2z~?j1L6Vzn2!acS$G-9NXB&@!gdsV4e)+~Id@1A zGm~Ko1+yLwCYHP%F8sd-|3txD3z#ZA%+GHKy*KE`5<@0+I6*LidJB+`KGH-}PmI2U zrdcqZ=6O0m3V5y1`vGqdTnc;nKLCo9}Il6(8+(NV9rB!5TkC<>=aCu zxS=@d)PdtPG1*=tMxN4e?UR^s@p&{cyV<`BW*?P+Fw!RioAW4u=K!1gbmRq19(jPd zx?L!kb*4n{TwwFu89c`UA1d_k0#6luIq*!ucLET%iAiL!`x+p+1*`7%uIl}Suo4@PQm2oy(oG50PiH0`M^_x$^U|2=Al_D z01vAo@3(ah@S$L4-QNXMegbulJVn6m1s4P73LXk<))v4s5x7w3vw#N+J{GuK@Cm?U z1#`Y}7_rO^juMP1ls8u}h7!9L0Up-R#iUa)mijKYDh5+6m|;&6 z%&^>MojeR{)>42Ob{*+5ww^DTVYdlp*zXHw*dGXH*jtEG05Eq6X5D*8a3A1D1+$EP zD|iI(OT_sAFs}<{*mngN0{>O;K;SO~j{Mo@KC|5 zw_^l10#6itCh*~c&jFq#cq{O+g0};kH6JAKYTy%vem8KV;GMuL1wRG6PVgJR=L=>( zvrVvvzUBLZIbL5+TmS}`TO53sU}T5)pkS8Qqr`>eMm`HB{UyOH$JYh39RDJi<;b>| zlyQV262SO3bBm;m5o}ipV0L9aap`$kEZ1CSILvrlJX4EJZz3@YbS~@)T)B)0W)7|- z=_&T^5wg&bSxX91EdQs>tu>XjQ^w(g2h)d-&9~qW;&h+zaF6zrG z7jzn?Ay@ivs^wxFp=0`!exN6TPk=ob7wPN+ONBlQ_93`5Uiztg6Bh-;FfWvc(JN*? zE2f^R1Ir>;`Z^O<^h`N)b}`D|$DtcJEL<2@j-jJmTBVDgE`KjDs%}P4(I@55nLny0 z+h^_+{J=1O5>V<0*yC}joVmbB(3ikwx#k{*AD9MQ`yP&UUBsx^jm;idfP<*`YHlb#BHssB)O^oL|wfT$H|;Sn6K0LtjBm1!30W zQvUVCGH==H(6`4cYX ze~Vbg@sAz)r^IYPFy?(;$!D&A>G<27So5El4Faanp%)R$xXF2;@{b};ff;79L!U;R zZ|QR!`aEK(Gc^uIQV11lw;~Z7XTQZSHX6~d`Jl<-P8f0o3fUEbu4*g z>@#I8_z0nMyqGVTa!gqZPlFTIlr`u_foGM_iPs9A1H4^uPvFZ09|p|zQ*Fz42%Zf3 z4hR22@KD$<2nLJyvS5}opYKu*vAK5xo(r6juyo>V!R&+0vsCa9_Y(SJz~;c|=*$j@izjEnqS^VN9GeU`}QSVTJc&3%^ebNyHQO!M3p z6_Pw$3s(9V!K{AfehNIqhYOwY9_Qfsf>WS#9a-g=dnkm(zqeNC#Onn!UUT0hef5>3 z1G5X|`m*-ncME3y=ZR9J6aQQ=io<)-!OsY0SIGmW$WQ!d!71RsI`}ieeLy$Qi$$ND z3>%jD-_yZ)f~hChvUN-`&ym5*bd47}@npdZfVtkS{Kq*wd@f5m`5Off1zzjm^@7KM zeu;xG7knb$Xoe#=90d%gd>sXl~ znDv3{>`KoQ%(5c7^( z>jhK)OB{T;;8M`J?yqw06wJIbhjd|XL;Q%)sSnrpb#BAcW{6Xu^EfZX?+G3VI-db3 zf0kh84^M9)ow%!DR)d2a%=a_MQw;hr2agm?{UjDRF7EGP_yg~W-{s%Gn`CLKgat{k8{Rsy@Etqon>_PeY?h!Hh`HVs5 zehCaX#H4p}aIRp=;d2P(AL#J#IfT-uIdnd6@KXo~GhZ&8|GZx>t+I-gbeDFB$C2xk4c-@!WsGhRNE@bk%yzDqFslRr85Ey1i0d~Tuqe0iRj z<=f7|9R!a6ozF9rzn9lA1jzi8&52A z#mRyh&op8wiz5Wr!>%Bfbq4A|+z<9VVkQFSI9$ZkaXzukJ!%BAZ&^$%WkB5C8E;A1sFZ8UhLw&OHfls^ z`LI-Kcxf5DM(`(9R$4luyd3t(QNu=<0AGI2`TBgUlOqo16H8+lYRA_+<(#L9+oQm< zB1eI{hlJxXf<5dz+a73+SK@dYdQaT7wYP!Fb9i)k2R$-9$DUc3FY0 z59scq2XuGU1G;nLXPkG-g9i4v_3o||p1;0tr~=!^eql0fx_X3!;GlJ!WqxQ*GMlLg zX!}R_rgw95;DEp{6a22vfvx&rC<^F|K|^3_INY2G-%F2k?;Y}`%=z-E@Mv`NQf;ym zwd4WpeZq6larzTa9;c5DRmc<6Z6Wmy&uQ;hi~=anu`K=Nko8Q;D&jH5=HPRTx}gf; zaqWY`$aqIEIC{AHka9UP9S5MhN2!Ab&q2{9RLhIE=;;yqB{*FJYPzCqbT< zVQ}onFnpbJ+fBga8Kscd>Jjd8;5$@B=kXMN?AKeF0}Ub3CBc^N--Dla^!2l!$8UCD z{CN9hq%MTTSNY-LTYt1e6Qo1pWl(dDzz<$M!{pa5IRK1i=<-Vm69wWb8wvkp!74kh z`9rPqY~SmA3_J#cwF`gF!b`;M%+S;=BSo!-CGoxte;~<@K^c9RdpKe~*&Y5~MgdR9 zP84K_v(P%T#M#$6v&GrZI@^o0zjfw_vxq546m$~j0PE~5&Vkn1MVy1Iv#U4k8fcY(8}MINOBdR-a35;=z<;Q-@Z6C^5U0AFqcM^{^3mQYfyDgq}Y3 zUGw}u8_zX?r+lI3e?U5OA7>pWhd_{jeHzbS9iB|X^XoL8e>yxFhUfi|heZ#`Qfm@j zvJ68P6r)YJcc5k9sAn|s41?qflZ+=e{7GaT$XdXB>PEm?pc!0Aaed(9drCSrAY~W9 zL(-gO#&}ILE0cd|z^FWCyceDf!^5REk-xvuGs}$q!joxuUJrTlEKds2GHI5_cD z$Q*?Hp2Pg689Ypw>F|#oz;{0ejK|NAIe|wpKZH>OA&W7LlrTb?QbwO;w-PUR8H5NNnm<${a~->g>hUc>lknMj87! zHOP%au}Z_4xM!*u!7%>#51Sqf$V@Z+Uz|WHjCKB!!Hh}TTca}eb!s5CE={ag#vV@% zST?l6!VXUjSZ-;Bh5elxuyDmGs{e5H9f6vX;cHdFY=qsQ8U*c#kv|&LD`Q8f1}tOK zSVlr&|<}a|6cHq#>^@(eqVn87nBE-<7c#66Y29Wv=+oLhOt15>7X-> z;s+bjh*#-hDc%#Pm?P5aN1~GxF-fW6}vpnu5GNVsyE+V zBQM)JDm2C&KQR>Bj@DM4Y!4De$8Rf~ugCe-2u05qOyZfQ<~|4c4D|VU{VS37#!wn1Low0vsAc- zQ{n$E`wUSU@pIj&%@Z(tZ|=Og^PQbHcP%X0a_E+sTQ1yk#g_NC?A|hd(<5HXCcM>v ztL&mH{E|~k8_F(T1=mJgMLkBh^FL^*_+b3_HKkiHsl&B<)BBsQ*mU8hnVSyXRI;h_ z#?B9P-q?F%&utgtlsoray;pGJ=Hqw$@vhJPRV^D^HmzB;rfJQBHAkz(`2Ij^1b+&RBE=el#wJ?H#&tIoS%-Rko$TzB?)@zWe{*?1vNi~CQ@zh(1-v2!}@ z`Hg=l9ACQny?O0cwJgT90{QWW!e_Q_YWZRDt%KiM_u0BXr2G$l>9=2x(=$7NuXp>c ziH}#Uncs3+%lWTwZ1Hwc*BE{EN*AdifQltu9-=dDGdOTIPK){_GxG+W8-synpEX-j3Hgf7E-`>Y@^A=7xF2zxkL4 zXAapkc2idq1EsefwxsgVnT&JDma$vX)=%mUhUv>9K5!L($<*YbItEM z^Sj#oW^d!f<;zVDSa!qso_|(hPRhK$si77C&|l4bfIYcCjQP)rx%E(Sz#RKE^A0@>5$!5IM)& zI8e3n%_9Wka-jOAAJ+7LbK3s(Ly*FJ+q=X1eD8P-qW0crdFw|H#lyEHSLwcaCZsN7 z@2!X|FIpRh;U30Ls9SKG#dQcAic(vRQ%x$%MtNKth)&MOjETBE-uXvKX)L@ekFzYO zT~xQkI}A6PaUU+{@lH2RuUcMJS6{{b@p+RQ_id=HJ;~#(ZQKL54mBQcXyYCW>z6ex z_ISS<_pv+Z^S0C8zIcxVHm=YkBbeXyxNNq`5(V7sD{1DN-_T~CNW;F6hJ7Fn`#zd! z)-umE3E9WdxP6HM( zNoCcNn#!75oR71t+PF)-1y##xDsi4eGfxCD2OzX^3c|7ljg9r5{f(9dH*P8`kJ5&l zg2Sl*(E~j~;XL=EsczAd+L{#4H{huixP7#-n=>(*nFgK&Q#OJ}>2N8}oE=aZrl+>D z!K-XsQC(Myg6D7K+y+~Do6tyVD@_!wB&rp~^#~4AS6fNh=%9)tbyUloNW}9FpnufU z7K1$u0iNX)i9idMF0GdXAezxyAOu;v*r~fg4Bl5c#3Nb&=59X750m~%u4^JiqH8%D z9n@J?-PBlReiwL)*$$UA)f!e-3x_`_QY$Py>r`N+m1wL5EFf)@;UOMr)x{Lnl#@ME z>aFCu=3OgZ!_XHY8MddWFsGyF0YW?iN+UO2jN5J6T(6vZ#LS5ku&>;7N72ekY|XY5 zr?{P_oy-5!Q4>zL9^Jt|o@0}SPN^s>a}roFck%3DmBIgFn%F4-X)?JILP+W@XUJ4#gpwy@zmy^R80><W`ILgDDC4oHiUJu-=A0L0thg*%xy9)$Y-cCmzpTX0Q`A6kk z{pLfS&b!2$miG@_uDlk=y8|@p$5+i%UjLZ9S>3|<8`y&U>jOSl-qx{pK1X@TqCno% zn7r#CPv?QIyv7*cPVl*P8s5g79E7}Pl@|qF}unu2A|u% zd8*f%bHGLY@^Ptt=G!H>RbG3% z$Jm-YK5=yAUF*mzaO82nUey6)FAwr|5QtG3+_OS4y*`CJe(Og4`r=akE{(~18S=Qd zq#BLuqL@5yJXW8jZ`7vQ^-9sT>UgRSzOjLF+D*)!h+Qr-|p-fJSSRAdyPvNy%@ zk21UBa>(ALz3~1s!m81DX**?-L7{sQbU+tj@+S1 znvvIaGgm^BG$Wtu=ExPAq#1c!H%lorNzd&e7(Y`VnxuI<)^)o*GWpq)h9-%gutJ>8 zuoI?dbp4r|p-Fo360nfc_pi?nnSqjCLCnn>6Pl!by(J(dJ+`#aBuz_k-K^=MNqQw- zZRj-bels0vSTEOzV*zaL7tfdaXx8HDiu8QXxIAw@Z0;Zbw7A(P=Hu^8anFa%{p3?p zIX*P*&K|$HRm1qEi+c`mE@G$_cOT>K=B*KTxp8+l`ym0Ev6%bCb6lli$>rkrIdQWd zW4C#4m$+FIx_T+^Q*py-(%;@`bJU05!m6Qrn7D2F3d~+bu77L|>|fs~y|w!oY@NNd zvU+L5X_Y)04y#CX*4I`wR^p~6vhEn(;IwjAzIXSh+$uZm2B4K{IN_&HeK(SRn=K$Pb zV|%lS{hYaa|Jdqgp6FYJ|qkB7ZlF!w$_NAQ7= zxk)g;VZT@~)AX>z^P1oR;D1l>-H`LS;A4ULk~npE40f^L)$lj>2cZ8LJadKqBy64& zL4Njc&4RB05Bn3+Ie*zK_#^OKCHQ*S=9_%*{0a02h0brZo)G+9*nbereazk!yc+y` zse$r|D+Keq_ql=}gxw^VdxozQ+yb8S1fK}}L%~ePEy@G?alylpSI-FMTydA+eL??J z@J8rh_6vhP%>Vu2N8Q-xntj25p9PQE|4VR{@Q{9*VCrV}9|zAy;O`5aI$R@oEaEhK zj)R9fn?1*Ye++ECEeEDvQGtb9*}_r7v>?$)O@!L`LBaNSLlmS z7J0;=cL2^8dWDC30ihoXda2MWfyW4)VJ8ZmZFdGS2XB~}g69C6J<3Ig8VS1+VV4jy z^I;kV_W?HFGYkK@!hbdRHxYL??A|uP<#1j_Ed1ts<`nkq1^*qS;~tIXZo%x2f8p>y zPMktsg5P}G4E|#9JTLr{f&U~t+)MC%VlFnod?c9jq20uyLw2^6`33mRcg{TwySE?d z!2RItLoEDe&-9dSv*U!$H^e6rQ#j0Ff}4ToI`m@&H-c`yc^3X=hkhS1;;M(tM~T@G zV4e^<`+=8;aX&_LCfxen<9cFgU+j;F8HHK10j8*{h0c8Yk>Ihww+WsIe6L`(yN3lY z0R9cJw7X{o<2KXow*q<};6DkS`Sy-rekSv=V8;8o;Gw`~9~OkgvZI%SGNAl1z}*E? zH?wah=)4~*6#7Kq!GaG1HhZ#whdQ&*rJVV|hY?F#Hftcj)Y*(XzX{t0lA;GMvwg8v9SO7Mrk6Nz&HV9cH`z);1TCG-^Vv4WXz3k1&sK2h)-V6zvD z^f_jn6?`svfO~=GLcx82zen610LJX0DflL#vwY3IFrb$L-!JrX;75q1|M{(he=nGM z{;FW|>s~P9fp-a={m*BDrvWE3AXECEP7dBrFui&SX8H1y8}>Og{A7Zd`F612Jm5)! z3xQ_{X1b0hrVyC<4mNWJ&^fm>`(^@@&Fq^AKsjrKpLuegVAiEAf|-^}1!=~P(GabxR@=u1%a!GWzV_zXK zztCe?*0Y3+xeT8KCO?YAOQNoV&BUDmrY_7|(z9XHX#OXF+e!Xs0cW6MV@NQ2ClJ%H zzGg|8QhqzZP|@(CnVGoAPkE*;0b>ei>k@DVFrqR181hY7lAm#o5IR@ZSiYoFPv!|R zZ!-@Q+#dE(f_XbROE7huCzv{(AQ(fBsRNY5d^l0)C^ECgOFGj*lgWaDp{|+8D;nyT zNd;l3Q|3qmP5xv`AIP{frN3g_naq3`#+S(o0Apm1ws?;+g~!MgY-9>HG6gdl{f3{a zbB;k%h)d~3#LNVka$HIuMNDBZlW{418Zk2mW)3c;GjDYqsd4Cwi6tMJ9r_AlDhRU{ zm&#dBEP1unp>HRayt>w*-#{#7afd^{o46-_#4znBN|VpTbAYK9aS?2aAf5-C_avGp zC4yOJ4|cG51}yWI*$)3Pf+5PQ7CaR8BEhVWrwfLKkAQ7ltheV0W_7wy@Lbr}2!`cd zFPL@YcEM~DcL_cgHt&&C|0e}!znnbo-luU2%VT8y^?1>Fz>sRpRXJeGk*>h%>0=onEAtdGv)u5VCK&v!OWkN1T%kl z|EBzAZ3Oa}KNkv}`NL~AR8%#(rHUals;v}H3l2Hp>J-VAnA>$Hvg7&M3xwOMn~HoiaUXsp?w_jNGW-AL~PI?Mil z>%Kft&GV5?8rC_*Ob`1X8usnP3t*#41#Ppt;Q6rGwP~Hq6HI!FSo-He!5_kA{nN4@ zESP0lN-Sl{@*rj&jv{7+Fl-OR^dC)T};C5JGw+S1?lQI(WU0fULe>qBoCR>E!@k4 z&hDYFZ0&IbYA*g^GSGk78caK45OIIb3%hU^a`m%rHS%&7fQhQab&8%P=zNb2+m zxs7~sLQ?MR**gbc`NH5F8y>EB4 zZDb!?pnghyps7!Mw)4N@Q|hG+%_S4;SIi}NdE9)&te-W!h^7wigNj$ZVuhjx0(d_2Tm(C4!OPVU>%W3O%Ohx0;MUCc@XVcX9 zG=^EG--C`e&u1_y#_W(|ocP4Q994J7c~~2qAqQ%OiC@FE9jks=9seX)L$oDf|5!Sd zA3E%hbhr6k>V}_Qc>0E)Wp7(w;D5Z~r>hotJ3#q1DVP0p%URn8T?RDxr=v=SH4Hv$ z=%BGb8}ieQE&Mu<*XoAiv(6rLJ~+;AscPx86?1Q~zbp3nE%SwLZKWfostrHGZw+Iz zV|82K&#}|XVcCh{T*h^y1CF@+#N3BSI9kqEKMqs|hZO%7=mPNA8pKC=>@!jKCe>Fp zHH{g=uWiaB6nw2Qv95V$V@++N$8IX*h0kO>xIc1fO|4nTj`&QfUs`?A z^tGQ$@YOfajJ}*sSdBOVvc`-LfXpHqW(`drYdNp?2;pm3HMPxE)r+SuX|8Q-T-JbZ zKaM?WlL!{nKWgw`37YaK0%53MARD9 z+*q}wX<=<6_+5GORzdW$7vtgpyWq6u+Sms&u`f29l$Q;UJ>Mgc5-vo0@%%s)?TcrxpqafS8QHP;Y;;^wF{Ols;pYDps{v&6ylGDN>&?%f#3&e4gip&v~?E@CsggxwZj zfB3Oe`7%B;wE~|S$}85$fEAkc9%X{P03Pg6)hHj=8p3W3`(I*~#aT1ob>R$`ejEcT zaA_Do?=~pPXJKm8Zw3giypM73wHTh1$BD4YGxtz%o2fT!?oFWenJ?uRv<7MZp@m>Z@T~%H-fOg&>2k|}-c_P0$NENj zs5*f>zCWX3DB^llrtt(~)NgW3-j$H2`HZ9o@{W(mdlT|TsW8|oZ(dB^TadRA{?w1t zJx#A!--Ejp7fo*z;%*fJHz6cOh`t_wCxvek?S* zb~Eooo&MeLG`lUF`Og{_nxx$!Gaw}GzL<`iIUSm$z0VB@>F+$V=7uI|R_k3iYffnR zj`NhzY07m@%v~*R_6>YTc)7Tl@44u~v~4k^xk%s5;?F*d?*u;~ZkACl()YT!X)$$w ziMc-&H_N0eIdtAwSvh;wl&O`Kcm;NLMddUF_;M7KNs}s!-F$LGWn=9kgICrzE)5)e ze5cl#<%O@I29tvL9Ar-usEA)H^A6WPm~7zI*4!=B)GexOuBtx<-@VqFn*el$(RV{p zeD9fj0UemnS=b-w`d~gWALhXn;UW*8BX9wSn9oW$?h>=DvVS6;3!a&RTVT%>%r*bx z1wReDMlhd8oFcd$n0113I3}GVcp&Io1YZsOJ;B$*W*s4aU)XmDJ_S7Y35Ml8B)C23 zI|UDi&HBW!tUE6Weh~I+f-eI9TY`TE{Fz|Zlyfm?`w3nOJb<_p0L&1< z*}w-AcQ$1EJO-F;V1|UPhJ7@#G{GujX})|%l9+9T>14bo0Iwrv#|-lw!E86%gy;9L zuONm@_P^#C3?qfPg>+!HtA~aE@35Z`yb1OU^xL{-&G%Au4ZW0D(m2Y&;{~(cO%u$t z&mxwz^JZ5u=XSZjr0G1J&6n5v#ZEcvk3!5aiq{#L;ZdpWV>$#o9CSulIhI|ZkJ z9}vuZ+es|>W1cw)enIGrt3@zXdP^{dR__DBDd0~97XfoHq|8!4JyUB0a2KJE1@0-B zWdZ2%98RW%@$r04W;OFDHy=MROoQU?4$dR)gC7` z=M|=}W55_@7)_%|FC&3T;nI3#(kuG2oFY22Kwyl{f>|yqlTi|rX5te0CZBp`Np!NKCBy4rc}(Dd1Uz5=F6VhV88wmd;83$CniSg5C_Yf z8suS?_%)x|M4!<__vhDVG--SDr#*COueXm7r+6`#VA5JRnq4cW*~0Ve1obooof89Wjw7Ghg!&4+Z)e2iyTG%f`eyXb#r$ zw*j=HkDtv7=Ivt{I4SUa$0M875S>zDF@D)(y~NK+O1_Gp9Bj7%H=&t4B%UsRYOC`q@_!mYnL=P zmCUMbTDq*U8V^?v#|AdxbD3srcY{r5mp7Iys9RD}TwH>uFeQtss|~5Vcv$gJ5`%v- z)-9>7Ush8~j_4LR;R77omj!*AJjP+*+S-Q3rQC=XS?501k>1Zd&A}RvZ9Bo!nuCRs zk+@Y*PqB5&)eJxRTRiKF2OJ*+OzsY(>E5Av;;GM63YFPUzWHrO7VAly4^Te)p0}g% zXH`G@^paIS&E7^=?|bPU|Fh0_e!BJVmwa|f=LgI-f;o`V)-w*y0qC#B)Le1IA4Mon zmS?vISGU^EQw$}=Sr3DU&0$V>dg8L#FAp@#W;{#dRP))J$9AQq3n?7fq@UBsg4(DLzC)ea7OYg0w$}VSf@xQoj9nG4#snbkiH=de{-9;!`Z7K<^f=)uk86Nx6vo%hdH=?yt41O3-#M36^8&_Zev`sEG?u81 zAN!dKTp9+@I|Yim-#@G04EVY7et{vK<1OW3X)=&k1{&LN7S&K zj!_3}x)+L@ZIJ8HXNsGB2luGhE^g*YSKiM=#@zql5^}2pXWOHt43u`=L$aB5gYSl> zjP4e{0c_TISy5o}aYfd6!O!=}d1u71H^CkwxEMC`ll0SI&lJpdFjw#d*k+9vJUf7E zgwEToI>8(dmJ4o%eTLvCU|%eFHtg>UE`UDw35I3gr-%G+0>3Nt1+aNLOWiud=JP#W zlVNybmNB2xk!LKhS*w&P#Xf@c^Fg0V3_9yTg@gsPH*XNae60~4_A|?g(cWoR3TD`I zh$XIz1ylZ2#E?G;*sMu{hyBrA5*Ei>pd z<_Xp18@@CKOI#cibnT0Ls(+zFFCrFxz<`H3k)QFpVY#kBI@8N?QXckyidhCKbE;sb zg=zaL@3MnEC<~*Hlndi#Tr|{=m`)a<#%t1!bW!(NhYc+cSM+e6X4-k1_S8*1Z?k;# zsPfVj&6mH2edWC9nqDQ|01wvm4(!y`pXDE&)deX}IB#NyFJEWq3S4GCD4ONIqTP;O z$Y{IXwvWE0-9EbXhm||$4QyTd!}1-Ea_pG)dNy9Oj+}0Zh8+=LU(fCmqoFCqkJiMp zo+D^z2OAJ8d-kNs?hy&oyD5X5$jPNfyfCiRJsixLY2GX66B2tcCs!#+)0Bosb1D_{ z7hQ6+Ym*1+njuz(!xckx*<{mGGJ8c&tL&|(Ri^Z`%6t_XzK`BV1#=yY1!`JAT2?17vk zLnlTGM*$>wG@P0iILGJ_um|fAuw#Wm-tQk5IuA8R%z6bFer*qtfzB~HwWT{S^3r}% zUg5Xz?q?(>gCSE^gOMZXQ&)0kBQN|%-|e^lt52oBea+ucG8P{JiiLHABCcH`BXJF! z=2S@Io%!tTaPQ-1?c--7x$Ni)=R)lcM)gELlDakPWM0RtZu}5}1FDy`uSr;e z$4`OTXIZB&&K}m85NA(L{t^XAaqe$@GsJm7z46V&qIGrC5--anC*i*cJxqjtj?o;x zwRnx6(Ixy(VxHZN(phFgZlwDKs09+7gq(Iz-KIu(r%;mRof73Wdu$?6GeeG3q8vG# zK}Dot1W;+)W4w$*MOKGw+;m73F);`rOpO`sIXqFI?{iT*m}l z(jv3I2>iYs_~nGf!udVwO;=<2+_gX=sV7~0i>&Vhp|7c*F3U*E@^X|#1x&HNe~upFaX24`cuWySI(s&-5+X?AQ+aCA>x^X9048>m^2-9`^uNSN|FBtX4ul!&nTn zZ=lzUq1E8o*Nt%YK0#>aTv2~vstIntCXM%Zd*IDyUGQIZB=6JCKEt+&p+^CK{8vrU zFZA+RKm4~yqk%5PX(7Bq7LL!?PpknK_g(5km&qi8zWnizOt2Cdlh4NQ?^GuO{V*+1 zKl=JRsSHhj?5XGZk3ECwKqdmilz2dZZw`v`xwsVu_Dt&yV1EOKS}NBaXhfMq}eKT!65)}Vt3X%N$Q)56;nA7tti znF`q={O}cM1S;Am9;qG2$C}#8K!b4f0|T*<&B&~wVEZpF@`aV=6_1j2(j6Q?xWo=;Pm_ag;{%y-fviVDzsf zD-8rKZ$r@4tqb~r3gYnVZ?R&GpaJ2*(-d&}cLDN)}>b%rTu=#FTuP9d>*KtPOYzg)HF zM9=5?mod=(L4=6qKVDn~k49KxSJ7|X1eXk#M^M#xx`M0=IPf3&N6>>7+ zAM0{moX(Zc>5l)Rqu{U8m~2MAs5$55b6W0Ccf!oY0#9pYFnW^1`W zM76cKKSV2-&HcdFCapx7qe&b3FofGwM3YvoN#ktV|Hv(Y#x8MvYzcf6RB3hBOA!p$ zqBs~qZGG<)44vKx`uczMZ^N>ooupXGC;nYA%4h!l0mUoiZG!)ttJyPYn%QyvIbS<% zJFY($Xgbow{eo$HHr78FzXV>t&F4Jce_wS+HCli*?r2GjGU0d813_$7f2dh)oqyGG zv2(GH-wLKtmhj0JF~X<0K_V^3KYGLn|ICXKcIO9#(PqY%@JGZbN&ny&CCeWlqqOs< z#3=3kV`G$#er1f($*+k~y83l7N_T%rjMBqj6Qdm9Z;w$9^1mNYyfMtyMr8EC{K@e4 zNwh2E-K*cntpazZ6;X74**`MBEac6t&)4;>K^cGYB?4wd1@LayuW|+4jN*(|5?cwY zd>wafC9H~7!fK}yHfklbjio8Z+HBm#k%{xk{$@n%OrE3Z-V{-L%ne8{=kq2n$9vgS z+7NlCV2A^}?4K)vg#UQ0xyFD8+PMuoF)p^OvBR+>+9q( zi~%w{taXMb$ICvPJdD9%B)4H?B{GbiyzC|=vK|>?u9v+Y*!r;ku#Yi5nO^qQ^a*~( zPAK)-4Z8+BoyJZm^V*dYb{;!nsMl^dVVAMktfJir!meYnSw*{%gt=p}Sw*{1gx$ts zvx;`33A>NQW)r1{H&v*-=6b!!QIZSt#dd@M3FtjY>y{6jeOCC$($`50E$tt#>0>JFJ-63zp} z?jpAClM!-0E|zhK=hOAf)L>NBH+NwvgZaqssK1X4mcbc=LMXRSRiA4;G>15 z!5pP83YeUTuE2*sHg7b9dUkDtj_6)Z#sfLgTp4(3-ay+eK@og~Xx zC-BbjD?{>a0Xd%O8U6xc>lU*i?bba6s}^?6+4TH8LpZd#L*Vpyxn%g$gbTh{v9=X3 z!#`Y*N`>!a_0=R~_%oG1k;EG%8ukc5nxOxuy>o%DsyfsDJ~@bp6fmMjMF>}eT#`Vf zfT&yo0&+=_B2r2&BuFqqgn($#CIkgrpxi}k6)mlocBplv(3Vl`I6^C;t&ZAOCF1>3 zg(*^Rt@Aummumg*L{&Orz;WVdpi|OvVy_vB^f^DQ!(C zStGSf+TQYEXH@eijSN$&8&`i@i^2SsU6b41j+>q4^h`e*zA5?VwHvkfMg11P zS4ej@IT*z26Yu`SYn#+EleV|4MEB9-=J%5FP?M2Fjl~-hlaTli6QRI-s^l7ZNjz^gD+Pd(!@LFy9Y<;^8oj0tC9qU)?ekJb)_xHnC zUETb?)b6~l-MY`#w%YXm`cU)94F@;88#^_(MeYav4)rT(sCN3NRcFoZ^e05BYphQnohSY#(cyiJk2+*~+XPbiS&6nYFM^ zJK84l*tkC1t!uSucDLB!(uPT|1ba#whPf6RZNcA&v2|zVw#p4^Zu~6R^KyqHEn$}T;Yn{)q=|bede7Uj zj9d$snzLZf%0yr3g1!ii@UDc`udeFwlTS*OAf?Bwmp9l^nj}=lcZ`@Ddb3m z44Hj8St@Ktjmz6o+G)of=oPmo+R{xMnk(&x*FU-b;QDBp_wvfTD)zH}Z!h~~S)2N^ z>igASSU+aPWh>Acj~KZn|OlieS&t zhGVDUhx+%_HOx6JcJ!UThZ`D-4i9@QR#LY;HnF&PQjdmU&+EBSX%|6ty2#hjZG8v7i?1tlcf0TW`!bI<{_1@3HmNWtd(~)cL`+ zPp*9!?Tx*YZLQsUwzgHSZuZ44f7y@crn+9>>d7Q`g#;$1ip?XL9_}9c*qYqKHp<#Q!hK7gw zHI9Av`t9pay7_s;zPeHDZB9AsxTa&TN|c4_ERnBhS=?0T=Zi-h=jBTD^#2Y!$x))Y zGO2F3yI*y~&i~djDSg@6#}~^>Sf6DW;r@|urC+%06yHA>uHqjG$2h}(*57zm3VZ8M z)L&P{IO8jI{hKp8Bbx>8OPpQUOX{4SKO5W^D{q+eK98ZNH{fQxBmZw`k#o3kgjf|T zX}H+gNrt#5b}>exedz7sxo4m2;~yqcw(QNdE<|W0I?je;MOEzEG0uj@In6T6AKC7i zlE-4ZVw2pA^;p7UyZ5h;wc7Y{$0Hpdi_K}cTdg1~7iZMNv7*|AvBPz_wKd0L_4PQ< z9DXcuzJJW6>{x7hJ#Cf9eo4a^k(EKHhnY~r58TZBJ~uN@j(k=V`00gMM!dywCuN@8 z;gnNbpV;P0ZQD&AT|9D3_W9X8^7=1n{iR^k#Nx@*h8Gr2P7w_sp7c(hR$O#xaZx@b zc}o;ZvOHe~V>}v6nN}=&c}oHap-X>p(SU?@inxD@*qKNK2_vO?syvK1m^9*&QIn^R zDxNa!d>$W?T{LO@CB-8rPC38FlI-D=M+y=Ak}1QdeDxA(!F=IlsvxO_5H87{Fnn6^ zWEYA#E1oz>M@N5k{CFS?z_r2ld4IzKkBvBehNELxZsuA7d_SLg^!-yJel)a{f6sn2{-_^I1S#PQBKDTXQ;#o*93LI=AAsLn^fyjg2J<$==5I(mS@1ul_eJAL z`&%pFZw;9#+s6_wCz~XQ=GqQ?4%Ma$DE*JZM5Agtd}``j1|=I}rRgf-^Tt!PA$Dcs z@Lm>UL8)cj_AP_*4Y7)3D3^+;zc_i^!o`BJB+fLned97H+Ypl|Gw| zqfD=twhW>*@xLT!^v8%=254WG`_%wHTAh`(?PKokbLWe>kJ8%#iPE&9XbFDa`Yt!q zUPLL;C!RD;QAj6ET1pr-(dTU(|62>p%E2pH6Mf#I`TN6&XE+v=m~GpCjy{)Eot*|v z^m!B8xE^P2h@~CH5>44Vzch{|+PcwgDwEwNP4?0hLey$gd1_9cQW2k{r8kvxubdNw zkc>NeR%mKO^v(h$7TWe{$|YI|ErayQ%hJg(97e*#q@D&(4pzlBWUPu~I}Y3NDFa*k z=)nw2lkFQPq^TVKy>j{-IsBp#HcnXj5=vw?;iSE z8UrTEQsW8dO@31`4K$JrG>#V7T4HOJGSxvNNj=!8kz}A5nJI6C^Tl>Jn(r?38d?oFr4jxU_j#mrbbi=H8y**mpy>Uer zHlMTc0|k7Qln;9j8N$B-UlR4ad=E7A;;TSKrPox=FD<{M^4f}kuY86FE>AtcH#@@v zl?#W@x4ECc)S{4Y^y1rU$(Gf!QzVInk50>-1HAEtSPNov@qbi zn4uUi-Se%=(6g{+e)aguh4>v)eE2QkJCstwd;5GNGW5A0c1)%t;A@ZJk(&9JUtU#F zJi7vs;CCGZzH%5IaX&X%9cH!|Z?s)rGbiAygW*x+w^%9TtA3$ycGaSVbC?IdwC9T! zED8AfU1P%mU#1Ji;g3%$Li`*`svIxnFqaDgzWo&jKwS)9R5Ksh3iw7=D8%o#s)+f% zRaC~er9xTt{HrS#6wbeTUch&w5(Y=kDV;a3qAK9KP@ynPRw8=7@Dv`HUtKZpQhaa# z(d7Emayn8z03RSsYMNoR;}`-E~v zJsH0SYEkpOn`DT9FS~?Z*SHGtQL@Uag~1}czYvPUFOKnrh|rIUTu_s2MSRgAJm50# zI#U6L)v&v?u+EkE5kq9qIa@$MZ*dOI&p9qu(Q%QAj*B&Prk@jf-G|YM?sXgNx#&d4 z#U{GwzSc>@x6O%#@^kTsj*B#O7N6}=JkD`Zh>nXvboLg8=~;N&bCHLRi#v2&)S=^I z4&CT*m-ujpy#Igq|8NOJyBdC$uF21vEW$`WumC@P8+Mz>DFGo1*sH?dyDeIb&+WOd z!KE8#oA`0v;#mvv#gm|#!%gas{+jXpg4Di@PvCjOWYR2uh&S^4xh`@2XH|@<#1Wj^ zqGtvdSU=$Fbn^$NNjf6*&Sl})#-IFkAqLqA0$T2eiS5AP#IQZ8Xiivs-hi0+R$z3< zbw7q&R#iIh3Z9?>J6XBpn`!+LaM8sqaKG}>e3a0_KQ1RNWa2H zu?Z&1oz0}ko7p776r>)&rw(UV&Yo}Q{KwaiiMp6ktBMlzDpGOHe@oo#7qDQ zvBc#MNHL8gvlcB_fZvJ_!WNAAj2}hvdqjy@MZ(g+==mMS!a1Kx$utMxreSl0lJNo*4O~@W~!ptTO}4JyFuGU`K{63ym$2v__-x9G0*Y)tS7LPD)bw9akGT zQ*5T5osr0BKV>7S#^*)#6ZwhMMB7S$e(Og0=6`Aazx3T#SFj`dk3!Q-e5o~*7-MtN})x^$Xp&Kl{|8jS+!lp>>cI+b+O zgLHb)_;`j&rUDvD%P!67^@NVurVYk>wFUBy4fYrq{0KT+FEwX27(Tf$ zgl{KfO=WzC@b}S?ba#x)J>#%h7+~ad)kpMx13g=&^6l~YB|m*L@G~;hWBRb7nMmI@u%EtX zVXq}*<}APe27#~l0`zS8jJl@x9}&G*p*MvJa;Enu`2Fd6f3f#Af!~cRJy>{N4w9 zwyX+q!rm(pdmBA_XL|q#BIJ5Uk#9k5XS^4$$?6t=Ef?=$^?1w#DPNz|`$6xni8JVkry)p2x{Q6?E^5b1Q z-(Gh_*4hGLZ}xgd?Cpj<8=rGMdlyIS^=rc3=!m_2o<06tx25ljh`m9uXSW-eOiSPF zh`ncE52r$x-vOSz)e(DRV9#z_`1V#t?7i!iAAe)b(zi2W?=IN0+aJEY2O{gKjJIjOpKUcMz1I z4>G@B!Djj08L{^&CNDG7%6 z&)(l;&*oH>_ndhey zuzAHAY?+Vix8os{GkfN@y)XYqzUjuXo#SEXj(m19-~#jFn{FM!=X?GhU^G#=BcE|@ zkaR~rOTAwG@BXHnet{t`);q;I^drC%SK4QWPGaprhd~?!J1K6vc5S+zT&;lJJ{YsQ=f& zJF%hLyY_HAIuy?%?2Bw4{4~WjF#9O|_}wn|twq!42f}aj^uG;1)7(M&{I1yz-f@1R z_$u%Cd+^Wmj<>^LQ3^kYY{tjyt{Gs9Z!-KGuPl8v@Q;Gu;@b>AYt8(-;O`1Q*Bof? z5Ac)OlIi~xemXlI`oh2Q;3D6PIC-W{y0|;j`u=G7PfQ*J#ZiC0iz9yD#od`KDT%ug z()aUBl63KuFD%kNOD5^!_?l@aZ%}0&_Ko<5%HI~p_)W%fDgM^MH2FDq;x`%>%Fp(e ziCS7CKTi>vSoykN{wdDiF5u7Lnq&OW%D)kuiSg!r`8l#>qE`4>26NPJ%@aFZ82>3j zclmQ5%fxsyKz`~_e^SIhU;epH|McKH@^eii6XVg3JRPs8GBF<6FFf;{mWlDG3o8yTw0~YOSpG2n z^Mk1={tE+sn1qhk1erK1td+k2vP_IGcgfE=c_w<|FXiW)Iure8zx=epG(QvZzZLNx zr*r;j7k+GzEx(2DFF)tRndpC`BgfsN9QsQ4f8mRv)9tB-G`AE-4dCxs7bjFAcN4?I z$r}mbodEe8-8o3=>5g=G!y#%taVsQ!xV4t@oYvoeq`^WpeH-62^3!y*@X3nV#YKsf zHG0U%f`_;3%PTLhtSPO!6c0taN3g=D0vbI^Wr30pJSCqjY5cfG^4Ph2=%Z@>tSbsD z7gv^7Eb#N*#KTe+ReqX#`H5pq+`vy9PrYkzV*gfsMCR_#n=H*u`NW}gcj6NV8{M+@ zo`^}k=Wan#Z?vb8CLeH1xutHJk^ATgaif&Do9BtcY3`UOjzw=RVc;> zXoGQL&Mn>jN!YA_%~_IPfUx`oALHuQ3cp(;=C#;b@g0cw4skB*Y!`nMb{-X%AL0x{6u%9F5mlH1({}u94 zV!l5$L;O|PDHCr6Un{-~IkZ%!xjP%?tE<>7sDrOx$DQ4UFck##2c|&|R^H@$5%xwg*G9i5 zhKgH9XS@w4pFNV_3_E+pe3$Gc@mq-ZHSvewPsLpMh{G=JJPG~N#ZSX#wzxC&`-}S_ z++Z>9-CQhYz9x%jBF$Hd--gW^@gRh~QCtI^)neYm*e>o2o4l4{dS=7s>>G&9JWDX{9hr}Ge-yGQlWg^V<0K#6UUc6LF21zF3B^Tk|4 z;J9FRmP`L}$ZwLo4eD-#Uz3pru6Z7i{9M$_ z^OCnkoxVkeP3ruad>R1mFJfLxACLUdP8aC67jv!bEOM4p54wn17k$Y}!$9emLjOYP zAX~vmG3|eye0pf7RGb6vm1NmjOhy`Rf}L+j&USc%BN=uWZ?>567LYpu;0B3lzmP2Z6UeYX5q2g^PWv+?Ukt7w!w&6NifR8k zvX<%V#I(PfEc=_uuzwTmd{=VX|AFK@A8seZ4(&f6rv1mr*#NlTh-<;ml4buO8TKEA zo%h7=g8wS!8esf&+plivwfZ;`D(~>CBF-8ez4?R6C6QC zyJg-lC1;0o+pmeoKwd58I94Mr2d^Y|0l=*ibG>j2S$VvZj5L3Mxb7F9g|s~^{wnxU z>9cGPNWKvArzK||UzD8Z!$XoYkMBzU5cr7X%;P7L{}u8U7^s*>=H+y83Ai5_>7NSj zFD?U*CU*tEjT0Awr;?SgVlwi@^XEMAAn-!za6G?Ga;`-#lbm^6B{|QTo5+ZZef$=2 zckp(P9}xG0d@s2h0Pb;d2k>)brLBRCw5@>s*Cgljgoh>PocXV0Mg+&}8}c;pm&men zDj9bE1^Q=7-Ur-M^4Gw%`Q*$L^Za>2TZBcq^#EsyCxFi)qik7LXG?wsG7t7lY@KRbN+=Ct_X)n@c5UJ6s|841Qk)CkBOTW1ZzP`p`O{?BbnQ^gq5EBr4~rS~ahUhTdWUH^LCiWmg{-`EB2Ns0 z64>ueW`V%virH4r^Yja)zXbY4E5cdBj`O_`XuF#Mv zzZ&wkWazVgPnZ00$UBi?zZ~|viVuN%OFsu~b)fY7V2m6p`H#>h$C58*%5YAVoX<~w zjXW-tmqd^32L^x4K96?6XZ z2eNKkza-|pnzyAx`+p|G{(Z3Xq2$k^U$;C7a@AdXGIV|iol_+L1bOK!`D>8pk;jMk zmxqgaZ+Wu#LGUc`HgJvj4)6`)+rT%ASAy>q-vIul_$u%d;(6eM;!^Mt@eFW0GfWTf zik>1K1wLDx4<0P;3cgs3rW1T!3=FO#k0xO)MZ5sKnmjqY?%Pa8`S9Fz2O0W2H+^4> z(?swP`62+^uf#NafIK<0^Bfs=egHcS(&-1CSH*+CpOMD^;NrMuA|C*^BTo+PWRYR# z3E0UI^G8YsiO<5gd%pD9X2y!S<}^*r@phKGQ`6!3bou3_E!nD{Qqv!HXIcs=+5@kTJ8xwdOnu9uOq3-*hd zwr9jFlb6Ue2*-6CGIqgHF?Bu@v)oQVJFsiglf=|HUCb)zB0d$|OPmeP7w3R^J!99T ze9oCneO{lCd5)MW?h2kIW}kA;J45Fj@Dj=UgKrRXEL|_=`TADzF!0^t5#XPQ(WHaj z;&EWE`7#aYO77VOGV91acMRq@^SX5Af)A5*-TH}`?ZG`)3_05;*CnZw3Cr>YCTrunAYH&)9^H1PU| zVHxjV#mw*VI6v8SZaXpUb3SUFiP;8tofgXu-*MO{?gRP1i~E5O ziid#T6%PX+6^{mgDjoxF*)p^_9^6)31U^+f1>8+M9qiUNvrz`%0g{)4&lk@Dj}k8c zPY~CFFC}*Yz`3~W zgC7wy?@x%C_vggS`$6$k@H^sZ;6ICJfxo@>o;;W_!RN=;LcPAt^WFw^IG*1pW;=K5s7wId!<2*DgZGM=7q0J9&U$=K%pknpqdX7%hL~;jeKG6K zt*JtXb@Z9!EPvilvg_Xt9(NRD7jzdh&HczM9Js+A4;Nz>j1^P=67t#9M%jupz_Y}x zFJ5y}hxuJZ&H=z(CuaG$wN}VkcdI4O2HzrPJ>DsXT5zwJb@$I=*4@KmmepP{$M)Zf zS*I_G+5X=U4+Fn19tZx5nEf|~YcsnRZX;$~9mFjEj$(%GE}jAIBVG(1NInMucY&B? zI9kkpJyFaunI>l2;B_g(vV7)<*-qTrEaWU7UW-yE2V5(LH(aL$6oA)D&NSaf?hSyu z+hey*3pu)Luv7BZ;77O3RP0>2<;yXB7#QlDvfSIl^iiYJ3V6|=3jL?m_{ z&1+yX%i&Zp^WI6!yyu9s!FggfUAKOVuq=mRlC!?Xh*=IrVz!OxV)m&rG1EC$JO;c# z%ycd#(;(amG3|UuOglG=*#_Q5bmg${q9pS%d-WpIjO_4a%;R`mdUA<4*fS|L7WV2XLLZC-^4uQ1Ci&3HTN< z?_J(0=JN^nithpcRLplmelFe*enk8(_}AjD7|4Dr&H?{kJOuo*nD5-YC9VM<5%XH) zBk_;Hu{L4Z?g5`5eieL@nD>0$S~1GABj&$dB+mo)63+nVi@6qY9(gnh4bH70gIj_p zO3r$BYsiqF0WOuCeX~l;W?Um?{Vo-=ewT~c7u*^$>=c4GN?8_I4X=k#SZGO7A1Y9OA1J4!D1}_lL179m<-j|7&g1;rc z5xhyvzOz+)EBGGqHt_x8UEp2f1K<|eGEnKQuxEX@93S4#Wj>D=vo15ghCgsDQ|cVU zp8C`wb1cii^)kz0oVXBs)+^=Hu)iLg*mrb2o7K@Zk#C3TH=8c~+2Q<<<8=m@<>bl-JRZz? zii3w?&+?)U%V#P!%FD5zDV~l!+bDHde)F(dT#e%G;>8PQyhE_X!HIam!yt3Px37IEuBnv z91Wa@TwcJ8hk2o#X<$8Nfcs$2c5Y!=&Y6(YIl0)i|6XAkvrH}hF7GNsbdMm82w9&l z4Pd5$@tOS$G7|;Y3Y&2Uvhu=mGdV>WNF>uso0;febp5cIj!Uz`j_~9Y$&&l!!(xs1 z4f~oaA24n7mz=g)UKxlCyVWa#I_H!h!#eN>P z6E4OdxN2;z#n*$WzY+W8*i7d}Plv~>UT&w(IQ)UzimkQyKCtEY7i6TL?jdX^jCC07 z!LGIVH(~@&Y#MkQ=bk z4(FU~ALLiD=SF@Vd(N%RP9~V?r!MEF)Zuff+-Uzc$g{w-Pwon~uzY9P($D!T!}4A^ zH}Wv-M`NR%l?XcqOgrQmV6#&$of{yZEgf>Tr&CK-d*eKq_HV?V8+j%6Yp^k{s}PpY zxH2yCHn7FT=U=JAzOr39;q;R$OFL^c05_>=bWBl z`OYLa@)Yc+W22o>2s;BzJ7nIQFgwf1vcqcthW!@y+{ml2=e-Hq;eE7?VA>(y12#Ln z2Vr*hfEo5z*mEQEzQk{^(N0%{-4CW6as$}x@E(NO;WY-sevCagGT-COh=+D~tEdH- zcF0*^v(tyHeDRuuVf$mxja-2J5Nu2j-B2*?&=rBrP8;|$zv0Agj55xqoC6HpIoND$ z$M6?qE~QlG{p&*Jcx7|TVdSzJM zcXAH?z`3-^P67Ou=Aq|(!V-xTPE(@F8XJp<@&LXh; ziOjpn2a$PFf-A&ka+nV6{@@f(K8?(Y9vt&%`g6!9Q;KU+Y$j)Z?Oxt;Prj0jdvkOf zv6=oBau%iGIl$!iz;E~69waNjyU1tY58R{JOumn-xy>`4{CTp<{8dl>CRy{$qn`Xj zvdX*##wN4himbN8^N7i_$k_-4*WHunkh_HP0#81OtoByu$;Xhph5A!G`80C(P+soI z=a74Z@)}RRgxoWfFZbjt$?7{BJ^2=LPN=`llix$ee3kA&Pri%Hjs^FqC*Mcz9m=2a zeT`WqQ)iIJ9CIkAei2!HaEiDk_9Y&d zi`!tognSbIz%9i_X1dmr)i*baDc|bxHZjw)%aiXGGv3F^>dQ}xssD;6e?we={V}rU zr5}rFr!DJFa?ZQREDt^}ZhgOpnCZ;-_*^mLM{yD#WyNmB)+3JU{Rm zaT`PU?gyFWd5gz2O{|I}Gcd-vG}6 z^O<$Z+1)C|dEiB2b~pF#5_ATFZ;+gPlXwQ0&$!bL<#&4W`^g%U9~86wbB;tgnR6pD z+tu^rjsUn9#4P_eJ$_frz7@nn{Tv|YLJT_u%xA)EY(Gs*JKa1!Tg>+9p3T6yfKj{m zq`)i__iTpF1(T@{rk$A{yLYA_ryV|1P955D?@xhghtG)Hxx@X|9L^)ObBp9;zWYQS z+HvnvfoW%lEuNmjg*GJG=&M z`aUOV)$JdA#K+b2@W7(L`g869#@D*KGL;D$kh43G)6| zLt>ESaYu1a$h&#$o*_Y4wxt1*v;DYdNFZmM87VpWBJmKgdxiu$78HZ6W8o0Ve-9G4=O*{FJBfo)uvy zf_sH>a7VCvRz&<~$)`c?-oKOniLF9?<|WhPQ^o9$e8<4%_PxZkf3C;wxsjZ3PRjQX zO#dsM&NPo_h}n1Cvm!a+7(QQemM7mauxp29V)mCc9=mt+aD7n&Io~}n{rkjdM!`NY z$DjRTwt?3?eoMR5Ga*0C<1@u<8+>oU^aqGpk3&4RXFph;e4l~(wd|}4~nVJcM?qBJ>vm= zwuh%BCqF070rR~D(|=n`o5wuYwiMSz^{Z-+7>Y#%0fVu;1}r z1-qsjDQ5Xk^7s<*-ykpcc$S!Jx_r06?0iE!5BkeIzENBY`FfAt`+8_d%OU@s0v<hA*LGNudr+CdQXS%TA2L1o}BMa z*tPa{G5g~$Jbp;L0CP;fM?rnY^{jXhnD0?g&N0NE$9M|-f#hU+9)tNk4xNVjWP2Wi z?c@~6$@V-(2b4n($;tLS2G<_Wm7F|S%=PC{9*-4gW?;VL@nvGJD^z$~DdybYz0)@u ziGi!59E|D@R(rfoTn+gT$QMx?=M-^U@D7iEE-r+e?>X2Sg?k2LOgOK2QF5Hx!e=lb z-w*!4)Bi}^3G(B<6xt!5AZD`pzJ%HDBF=_9$76d2<1xtHdwhs%BKSh-YzMn{`XDEd zll&Lpme^uQ2c6ID;g2!na2&I2EhU@GgK#<6Yz-ujoJpzHShRLA41QaGK=g?9EHt>4*zB5aEjb&1;I?4ne3EV}HkhQ*laHG6gh`dTq9$%b7ceh9Q=WM6Pulb4wL&(iu*6v zY|Q6aX5&>WGCL+*doubLT^4zC$k}AI&m8iYP|m+Bx31W?5HlIA#5BulrcMF&S>j>X zvsfvosci8$>{%R?vn}P3v%_m5h9~p*AZ)Sha85W>+z)%&Vwoejd%l58{UXU(6dY?P zr+$g}0_@Ah%<>#D?Ny1#W50x)6NX(X=Ga~=w7eeh*oFq9xX5S&L=UoH@LfP4_SJGGH+@ig!-GOj7;3dJnXG31_39E=xJUPL}S zlur@2hj$v81q;Xf%jD_c5^^sm4$8%g;hjT1CzMx-+2*Usy+e78nDH(l_X*`o#Y}%K zxo;?6E~b1XnH>slm6-F8wPcMg8^s0C*+SMhbDNmYMs6ju1Hx?+F96>|*7);dG1cxT NkD<2iO@I%8{}ZTDOPT-x diff --git a/RF24/examples/scanner/output/scanner.cpp b/RF24/examples/scanner/output/scanner.cpp deleted file mode 100644 index 5eb1627..0000000 --- a/RF24/examples/scanner/output/scanner.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#line 1 "scanner.pde" - -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Channel scanner - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// -// Channel info -// - -const short num_channels = 128; -short values[num_channels]; - -// -// Setup -// - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/scanner/\n\r"); - - // - // Setup and configure rf radio - // - - radio.begin(); - radio.setAutoAck(false); - - // Get into standby mode - radio.startListening(); - radio.stopListening(); - - // Print out header, high then low digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",i>>4); - ++i; - } - printf("\n\r"); - i = 0; - while ( i < num_channels ) - { - printf("%x",i&0xf); - ++i; - } - printf("\n\r"); -} - -// -// Loop -// - -const short num_reps = 100; - -void loop(void) -{ - // Clear measurement values - memset(values,0,num_channels); - - // Scan all channels num_reps times - int rep_counter = num_reps; - while (rep_counter--) - { - int i = num_channels; - while (i--) - { - // Select this channel - radio.setChannel(i); - - // Listen for a little - radio.startListening(); - delayMicroseconds(128); - radio.stopListening(); - - // Did we get a carrier? - if ( radio.testCarrier() ) - ++values[i]; - } - } - - // Print out channel measurements, clamped to a single hex digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",min(0xf,values[i]&0xf)); - ++i; - } - printf("\n\r"); -} - - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24/examples/scanner/output/scanner.elf b/RF24/examples/scanner/output/scanner.elf deleted file mode 100755 index 5e0f4b153bcd3684fc9008dac0bf38d4a5fbfd97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100436 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 ZC5_yqj3YXVcR0Mu?yd|2_9q0v{|C_~feiov diff --git a/RF24/examples/scanner/output/scanner.hex b/RF24/examples/scanner/output/scanner.hex deleted file mode 100644 index 00a3175..0000000 --- a/RF24/examples/scanner/output/scanner.hex +++ /dev/null @@ -1,325 +0,0 @@ -:100000000C9463000C948B000C948B000C948B006C -:100010000C948B000C948B000C948B000C948B0034 -:100020000C948B000C948B000C948B000C948B0024 -:100030000C948B000C948B000C948B000C948B0014 -:100040000C948F050C948B000C9423060C948B005D -:100050000C948B000C948B000C948B000C948B00F4 -:100060000C948B000C948B000000000024002700EF -:100070002A0000000000250028002B0000000000DE -:1000800023002600290004040404040404040202DA -:100090000202020203030303030301020408102007 -:1000A0004080010204081020010204081020000012 -:1000B0000007000201000003040600000000000029 -:1000C000000090043B0711241FBECFEFD8E0DEBF35 -:1000D000CDBF11E0A0E0B1E0E6EFF3E102C0059092 -:1000E0000D92AA33B107D9F712E0AAE3B1E001C03B -:1000F0001D92A13FB107E1F710E0C6ECD0E004C0CB -:100100002297FE010E94F509C23CD107C9F70E945F -:100110001C060C94F9090C9400000F931F93CF93C5 -:10012000DF938C01EB01009731F46115710519F42F -:1001300020E030E038C081E090E06EE070E00E94A6 -:10014000CB02FC019C01009771F180E8838320972A -:1001500071F0D387C28781E883838091E702909111 -:10016000E802892B21F4F093E802E093E7020115FD -:100170001105C9F011870087838182608383809194 -:10018000E9029091EA02892B71F4F093EA02E0937C -:10019000E9028091EB029091EC02892B21F4F0931B -:1001A000EC02E093EB02C901DF91CF911F910F9117 -:1001B0000895A0E0B0E0EFEDF0E00C94CC09FE0172 -:1001C0003596619171918091E9029091EA02AF01B7 -:1001D0000E94EE002096E2E00C94E809ABE0B0E06B -:1001E000E4EFF0E00C94BC093C012B015A01FC0146 -:1001F00017821682838181FD03C06FEF7FEFC6C136 -:100200009AE0892E1E010894211C311CF3012381E0 -:10021000F20123FD859123FF81912F01882309F4A9 -:10022000B2C1853239F423FD859123FF81912F01DD -:10023000853229F490E0B3010E940604E7CF982F9D -:10024000FF24EE249924FFE1FF15D0F09B3269F0E2 -:100250009C3228F4903259F0933291F40EC09D32C2 -:1002600049F0903369F441E024C052E0F52A84E07B -:10027000F82A28C098E0F92A25C0E0E1FE2A22C029 -:10028000F7FC29C0892F80538A3070F4F6FE05C030 -:10029000989C902C1124980E15C0E89CE02C1124F9 -:1002A000E80EF0E2FF2A0EC09E3229F4F6FC6BC184 -:1002B00040E4F42A07C09C3619F450E8F52A02C03D -:1002C000983649F4F20123FD959123FF91912F0176 -:1002D000992309F0B8CF892F8554833018F08052C4 -:1002E000833038F444E050E0A40EB51E5FE3598338 -:1002F0000FC0933631F0933779F0933509F056C03B -:1003000020C0F5018081898342E050E0A40EB51E33 -:10031000610101E010E012C0F501C080D180F6FC5F -:1003200003C06FEF7FEF02C0692D70E042E050E044 -:10033000A40EB51EC6010E94FB038C015FE7F522E7 -:1003400014C0F501C080D180F6FC03C06FEF7FEFD1 -:1003500002C0692D70E042E050E0A40EB51EC60157 -:100360000E94E9038C0150E8F52AF3FE07C01AC089 -:1003700080E290E0B3010E940604EA948E2D90E0A2 -:1003800008171907A8F30EC0F601F7FC8591F7FED0 -:1003900081916F0190E0B3010E940604E110EA949C -:1003A000015010400115110579F7EAC0943611F09B -:1003B000993669F5F7FE08C0F50120813181428147 -:1003C000538184E090E00AC0F501808191819C0115 -:1003D000442737FD4095542F82E090E0A80EB91EC7 -:1003E0009FE6F92257FF09C0509540953095219519 -:1003F0003F4F4F4F5F4FE0E8FE2ACA01B901A1010C -:100400002AE030E00E943204D82ED21840C095373E -:1004100029F41F2D1F7E2AE030E01DC01F2D197FFB -:100420009F3661F0903720F4983509F0ACC00FC0CA -:10043000903739F0983709F0A6C004C028E030E0C2 -:100440000AC0106114FD146020E130E004C014FD06 -:10045000166020E132E017FF08C0F501608171816C -:100460008281938144E050E008C0F5018081918150 -:10047000BC0180E090E042E050E0A40EB51EA10176 -:100480000E943204D82ED2188FE7F82EF122F6FE01 -:100490000BC05EEFF522D91438F4F4FE07C0F2FC6D -:1004A00005C08FEEF82202C01D2D01C0192DF4FEEB -:1004B0000DC0FE01ED0DF11D8081803319F499EE20 -:1004C000F92208C01F5FF2FE05C003C08F2D867899 -:1004D00009F01F5F0F2DF3FC14C0F0FE0FC01E15B6 -:1004E00010F09D2C0BC09D2C9E0C911A1E2D06C049 -:1004F00080E290E0B3010E9406041F5F1E15C0F366 -:1005000004C01E1510F4E11A01C0EE2404FF0FC050 -:1005100080E390E0B3010E94060402FF1DC001FDCC -:1005200003C088E790E00EC088E590E00BC0802F04 -:10053000867891F001FF02C08BE201C080E2F7FCF7 -:100540008DE290E0B3010E94060406C080E390E0D3 -:10055000B3010E9406049A94D914C0F3DA94F1010D -:10056000ED0DF11D808190E0B3010E940604DD20B5 -:10057000A9F706C080E290E0B3010E940604EA9465 -:10058000EE20C1F743CEF30166817781CB012B9634 -:10059000E2E10C94D8090F931F93CF93DF93689FE8 -:1005A0008001699F100D789F100D1124C8010E94D1 -:1005B000E702EC01009729F060E070E0A8010E94DA -:1005C000F403CE01DF91CF911F910F910895CF9346 -:1005D000DF93BC018230910510F462E070E0A091DD -:1005E000EF02B091F002ED01E0E0F0E040E050E019 -:1005F00021C0888199818617970769F48A819B8138 -:10060000309719F09383828304C09093F002809313 -:10061000EF02FE0134C06817790738F4411551051F -:1006200019F08417950708F4AC01FE018A819B81BB -:100630009C01E9012097E9F641155105A9F1CA018C -:10064000861B970B049708F4BA01E0E0F0E02AC09B -:100650008D919C91119784179507F9F4641775078C -:1006600081F412968D919C911397309719F0938392 -:10067000828304C09093F0028093EF02FD013296D2 -:100680004CC0CA01861B970BFD01E80FF91F61934F -:10069000719302978D939C9340C0FD018281938159 -:1006A0009C01D9011097A1F68091ED029091EE0284 -:1006B000892B41F480912301909124019093EE02C3 -:1006C0008093ED024091250150912601411551057D -:1006D00041F44DB75EB78091210190912201481BF2 -:1006E000590B2091ED023091EE02CA01821B930B4F -:1006F0008617970780F0AB014E5F5F4F8417950711 -:1007000050F0420F531F5093EE024093ED02F90157 -:100710006193719302C0E0E0F0E0CF01DF91CF91EF -:100720000895CF93DF93009709F450C0EC0122970E -:100730001B821A82A091EF02B091F002109709F18A -:1007400040E050E0AC17BD0708F1BB83AA83FE016F -:1007500021913191E20FF31FAE17BF0779F48D910C -:100760009C911197280F391F2E5F3F4F39832883A3 -:1007700012968D919C9113979B838A834115510505 -:1007800071F4D093F002C093EF0220C012968D91C5 -:100790009C911397AD01009711F0DC01D3CFFA01C2 -:1007A000D383C28321913191E20FF31FCE17DF076C -:1007B00069F488819981280F391F2E5F3F4FFA0114 -:1007C000318320838A819B8193838283DF91CF91C0 -:1007D0000895FC010590615070400110D8F7809594 -:1007E00090958E0F9F1F0895DC0101C06D934150BD -:1007F0005040E0F70895FC016150704001900110F5 -:10080000D8F7809590958E0F9F1F08950F931F9393 -:10081000CF93DF938C01EB018B8181FF1BC082FFA3 -:100820000DC02E813F818C819D812817390764F48A -:10083000E881F9810193F983E88306C0E885F985A9 -:10084000802F0995892B31F48E819F8101969F839A -:100850008E8302C00FEF1FEFC801DF91CF911F9170 -:100860000F910895FA01AA27283051F1203181F122 -:10087000E8946F936E7F6E5F7F4F8F4F9F4FAF4FA8 -:10088000B1E03ED0B4E03CD0670F781F891F9A1FBB -:10089000A11D680F791F8A1F911DA11D6A0F711D6F -:1008A000811D911DA11D20D009F468943F912AE07B -:1008B000269F11243019305D3193DEF6CF01089563 -:1008C000462F4770405D4193B3E00FD0C9F7F6CF94 -:1008D000462F4F70405D4A3318F0495D31FD40525C -:1008E000419302D0A9F7EACFB4E0A69597958795F2 -:1008F00077956795BA95C9F70097610571050895D1 -:100900009B01AC010A2E069457954795379527957C -:10091000BA95C9F7620F731F841F951FA01D089514 -:100920008AE391E068E049E00E9475070895FF922C -:100930000F931F93CF93DF9380E8E7E4F1E0DF01AB -:100940001D928A95E9F704E610E021C08AE391E060 -:100950006F2D0E94F2078AE391E00E94840880E8EC -:1009600090E00E94D7058AE391E00E947C078AE329 -:1009700091E00E944608882329F088819981019698 -:10098000998388832297FA94BFEFFB16F9F60150FA -:100990001040EFEF0F3F1E0729F0C5E4D2E08FE7CC -:1009A000F82ED4CFC7E4D1E000E011E000D000D0B1 -:1009B000ADB7BEB712961C930E931197899199917A -:1009C0008F70907014969C938E9313970E94D90009 -:1009D0000F900F900F900F90B2E0C734DB0731F704 -:1009E00000D083E091E0EDB7FEB7928381830E944F -:1009F000D9000F900F90DF91CF911F910F91FF9031 -:100A0000089580E895E060E070E00E948D00089510 -:100A10000F931F93CF93DF9384ED92E040E051EE6C -:100A200060E070E00E9454060E94010500D086E05C -:100A300091E0EDB7FEB7928381830E94D9000F90B9 -:100A40000F908AE391E00E944B088AE391E060E016 -:100A50000E94E8078AE391E00E9484088AE391E01B -:100A60000E947C07C0E0D0E000E011E000D000D0A0 -:100A7000EDB7FEB712830183CE0124E095958795EB -:100A80002A95E1F7948383830E94D90021960F90E1 -:100A90000F900F900F90C038D10541F700D083E040 -:100AA00091E0EDB7FEB7928381830E94D900C0E048 -:100AB000D0E00F900F9000D000D0EDB7FEB70183CB -:100AC0001283CE018F709070948383830E94D9002B -:100AD00021960F900F900F900F90C038D10559F7C5 -:100AE00000D083E091E0EDB7FEB7928381830E944E -:100AF000D9000F900F90DF91CF911F910F91089522 -:100B00000F931F93082F84ED92E0602F0E94280717 -:100B1000112707FD1095C8011F910F9108951F928D -:100B20000F920FB60F9211242F933F938F939F93A1 -:100B3000AF93BF9380914B0290914C02A0914D02D4 -:100B4000B0914E0230914F020196A11DB11D232F8D -:100B50002D5F2D3720F02D570196A11DB11D20933B -:100B60004F0280934B0290934C02A0934D02B0939E -:100B70004E028091470290914802A0914902B091A3 -:100B80004A020196A11DB11D80934702909348022D -:100B9000A0934902B0934A02BF91AF919F918F9168 -:100BA0003F912F910F900FBE0F901F9018950197B6 -:100BB00039F0880F991F880F991F02970197F1F755 -:100BC0000895789484B5826084BD84B5816084BDC5 -:100BD00085B5826085BD85B5816085BDEEE6F0E0B6 -:100BE000808181608083E1E8F0E010828081826012 -:100BF0008083808181608083E0E8F0E08081816093 -:100C00008083E1EBF0E0808184608083E0EBF0E0C2 -:100C1000808181608083EAE7F0E080818460808366 -:100C20008081826080838081816080838081806810 -:100C300080831092C10008950E94E1050E9408057A -:100C40000E949704FDCF1F920F920FB60F921124AE -:100C50002F933F934F938F939F93EF93FF934091E5 -:100C6000C600E091D002F091D10231969F012F771A -:100C7000307031978091D2029091D30228173907B2 -:100C800039F0E05BFD4F40833093D1022093D002D6 -:100C9000FF91EF919F918F914F913F912F910F90E5 -:100CA0000FBE0F901F901895AF92BF92DF92EF92F8 -:100CB000FF920F931F93CF93DF93EC017A018B0187 -:100CC000DD24403081EE580780E0680780E0780737 -:100CD00011F0DD24D39491E0A92EB12CE885F9859B -:100CE000DD2069F0C5010A8802C0880F991F0A94A7 -:100CF000E2F7808360E079E08DE390E005C0108248 -:100D000060E874E88EE190E0A80197010E949A09DA -:100D10002150304040405040569547953795279593 -:100D200080E12030380720F0DD2011F0DD24D6CF1F -:100D3000EC81FD813083EE81FF812083EA85FB8594 -:100D4000208141E050E0CA010E8402C0880F991F43 -:100D50000A94E2F7282B2083EA85FB852081CA01CB -:100D60000F8402C0880F991F0A94E2F7282B208372 -:100D7000EA85FB858081088802C0440F551F0A94CC -:100D8000E2F7842B8083DF91CF911F910F91FF9029 -:100D9000EF90DF90BF90AF900895DC011296ED9137 -:100DA000FC911397E058FF4F2191319180819181FF -:100DB000281B390B2F773070C9010895DC0112967A -:100DC000ED91FC911397EE57FF4F20813181929165 -:100DD0008291E058F0408217930719F42FEF3FEF0C -:100DE00005C0E20FF31F8081282F30E0C90108956C -:100DF000DC011296ED91FC911397DF01AE57BF4FC6 -:100E00002D913C911197E058FF4F80819181E058DE -:100E1000F0408217930719F42FEF3FEF0BC0E20F5A -:100E2000F31F80812F5F3F4F2F7730702D933C93BE -:100E3000282F30E0C9010895DC011296ED91FC9154 -:100E40001397EE57FF4F808191819293829308957B -:100E5000FC01A085B18521898C9190E0022E02C011 -:100E6000959587950A94E2F780FFF6CF0484F5857F -:100E7000E02D608308958BE291E09093D5028093FA -:100E8000D40280E592E09093D7028093D60285EC5D -:100E900090E09093D9028093D80284EC90E09093F4 -:100EA000DB028093DA0280EC90E09093DD02809385 -:100EB000DC0281EC90E09093DF028093DE0286EC0E -:100EC00090E09093E1028093E00284E08093E2025C -:100ED00083E08093E30287E08093E40285E08093DF -:100EE000E50281E08093E6020895FC01608341837E -:100EF00080E2828313820895FC01808160E00E9479 -:100F0000CD080895FF920F931F938C01F62E80E079 -:100F10000E94650985E00E946A09F80181816F2DB0 -:100F20000E94CD081F910F91FF9008951F93CF93BA -:100F3000DF93EC0160E070E00E94820781EE8EBDDD -:100F40000DB407FEFDCF1EB5CE0161E070E00E943A -:100F50008207812FDF91CF911F9108951F93CF9327 -:100F6000DF93EC0160E070E00E94820782EE8EBDAC -:100F70000DB407FEFDCF1EB5CE0161E070E00E940A -:100F80008207812FDF91CF911F9108950F931F93B7 -:100F9000CF93DF93EC01162F042F60E070E00E94E6 -:100FA00082071F7110621EBD0DB407FEFDCF1EB576 -:100FB0000EBD0DB407FEFDCF8EB5CE0161E070E031 -:100FC0000E948207812FDF91CF911F910F91089589 -:100FD000662319F061E04FE302C061E040E00E9447 -:100FE000C6070895462F603808F04FE765E00E9475 -:100FF000C6070895EF92FF920F931F93CF93DF934D -:101000007C01162FEA01022F60E070E00E94820747 -:101010001F7110621EBD0DB407FEFDCF1EB508C0C6 -:1010200088818EBD0DB407FEFDCF21968EB501508F -:101030000023B1F7C70161E070E00E948207812FB1 -:10104000DF91CF911F910F91FF90EF9008951F9323 -:10105000CF93DF93EC01162F60E070E00E948207CF -:101060001F711EBD0DB407FEFDCF8EB58FEF8EBD77 -:101070000DB407FEFDCF1EB5CE0161E070E00E9409 -:101080008207812FDF91CF911F91089569E00E941F -:101090002708817008950F931F938C01FC018081B4 -:1010A00061E00E94A708F801818161E00E94A70821 -:1010B000F801808160E00E94CD08C80161E070E025 -:1010C0000E9482070E947B0981E00E945C0980E007 -:1010D0000E94650985E00E946A09C80164E04FEF3B -:1010E0000E94C607C80167E040E70E94C607C80122 -:1010F0000E94AE07C8010E949607C80161E00E94E5 -:10110000F2071F910F9108950F931F938C0160E0D8 -:101110004BE00E94C607C80167E040E70E94C6078F -:10112000A8014B5F5F4FC8016AE025E00E94FA0703 -:10113000C8010E94AE07F801808161E00E94CD08DD -:1011400082E890E00E94D7051F910F910895482FE3 -:1011500050E0CA0186569F4FFC0124914A575F4FC9 -:10116000FA0184918823C1F0E82FF0E0EE0FFF1F11 -:10117000E859FF4FA591B491662341F49FB7F894C5 -:101180008C91209582238C939FBF08959FB7F894EC -:101190008C91822B8C939FBF0895482F50E0CA01F9 -:1011A00082559F4FFC012491CA0186569F4FFC0136 -:1011B00034914A575F4FFA019491992309F444C03E -:1011C000222351F1233071F0243028F42130A1F092 -:1011D000223011F514C02630B1F02730C1F0243090 -:1011E000D9F404C0809180008F7703C08091800083 -:1011F0008F7D8093800010C084B58F7702C084B546 -:101200008F7D84BD09C08091B0008F7703C080912D -:10121000B0008F7D8093B000E92FF0E0EE0FFF1F4C -:10122000EE58FF4FA591B491662341F49FB7F8940F -:101230008C91309583238C939FBF08959FB7F8942A -:101240008C91832B8C939FBF08950F931F93CF9303 -:10125000DF938C01EB0109C02196D801ED91FC913F -:101260000190F081E02DC801099568816623A1F7FE -:10127000DF91CF911F910F910895EF92FF920F93FD -:101280001F93CF93DF938C017B01EA010CC0D70140 -:101290006D917D01D801ED91FC910190F081E02DDF -:1012A000C80109952197209791F7DF91CF911F9160 -:1012B0000F91FF90EF900895882319F48CB5806208 -:1012C00002C08CB58F7D8CBD08959CB5937F982B03 -:1012D0009CBD08952CB5382F33702C7F322B3CBD2C -:1012E0002DB590E0959587959595879581702E7F82 -:1012F000822B8DBD08958DE061E00E94A7088BE0F0 -:1013000061E00E94A7088AE061E00E94A7088DE0E2 -:1013100060E00E94CD088BE060E00E94CD088AE08A -:1013200061E00E94CD088CB580618CBD8CB5806475 -:101330008CBD0895A1E21A2EAA1BBB1BFD010DC096 -:10134000AA1FBB1FEE1FFF1FA217B307E407F50775 -:1013500020F0A21BB30BE40BF50B661F771F881F51 -:10136000991F1A9469F760957095809590959B01E7 -:10137000AC01BD01CF0108952F923F924F925F9231 -:101380006F927F928F929F92AF92BF92CF92DF9295 -:10139000EF92FF920F931F93CF93DF93CDB7DEB7FA -:1013A000CA1BDB0B0FB6F894DEBF0FBECDBF09948E -:1013B0002A88398848885F846E847D848C849B84E5 -:1013C000AA84B984C884DF80EE80FD800C811B81F3 -:1013D000AA81B981CE0FD11D0FB6F894DEBF0FBE22 -:1013E000CDBFED010895EE0FFF1F0590F491E02DA4 -:0613F0000994F894FFCF00 -:1013F6002578000A0D000A0D524632342F657861B1 -:101406006D706C65732F7363616E6E65722F0A0D56 -:10141600002000F102000000000000280725093D19 -:0A14260009CD06F806DE061C0700DB -:00000001FF diff --git a/RF24/examples/scanner/printf.h b/RF24/examples/scanner/printf.h deleted file mode 100644 index 66f6438..0000000 --- a/RF24/examples/scanner/printf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/RF24/examples/scanner/scanner.ino b/RF24/examples/scanner/scanner.ino deleted file mode 100644 index e4aca24..0000000 --- a/RF24/examples/scanner/scanner.ino +++ /dev/null @@ -1,126 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Channel scanner - * - * Example to detect interference on the various channels available. - * This is a good diagnostic tool to check whether you're picking a - * good channel for your application. - * - * Inspired by cpixip. - * See http://arduino.cc/forum/index.php/topic,54795.0.html - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// -// Channel info -// - -const uint8_t num_channels = 128; -uint8_t values[num_channels]; - -// -// Setup -// - -void setup(void) -{ - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/scanner/\n\r"); - - // - // Setup and configure rf radio - // - - radio.begin(); - radio.setAutoAck(false); - - // Get into standby mode - radio.startListening(); - radio.stopListening(); - - // Print out header, high then low digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",i>>4); - ++i; - } - printf("\n\r"); - i = 0; - while ( i < num_channels ) - { - printf("%x",i&0xf); - ++i; - } - printf("\n\r"); -} - -// -// Loop -// - -const int num_reps = 100; - -void loop(void) -{ - // Clear measurement values - memset(values,0,sizeof(values)); - - // Scan all channels num_reps times - int rep_counter = num_reps; - while (rep_counter--) - { - int i = num_channels; - while (i--) - { - // Select this channel - radio.setChannel(i); - - // Listen for a little - radio.startListening(); - delayMicroseconds(225); - - - // Did we get a carrier? - if ( radio.testCarrier() ){ - ++values[i]; - } - radio.stopListening(); - } - } - - // Print out channel measurements, clamped to a single hex digit - int i = 0; - while ( i < num_channels ) - { - printf("%x",min(0xf,values[i]&0xf)); - ++i; - } - printf("\n\r"); -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/RF24/examples/starping/Jamfile b/RF24/examples/starping/Jamfile deleted file mode 100644 index de9b1f6..0000000 --- a/RF24/examples/starping/Jamfile +++ /dev/null @@ -1,206 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = EEPROM SPI RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - diff --git a/RF24/examples/starping/printf.h b/RF24/examples/starping/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/examples/starping/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/examples/starping/starping.pde b/RF24/examples/starping/starping.pde deleted file mode 100644 index 4813a77..0000000 --- a/RF24/examples/starping/starping.pde +++ /dev/null @@ -1,293 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * Example RF Radio Ping Star Group - * - * This sketch is a more complex example of using the RF24 library for Arduino. - * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the - * role_pin low, and the others will be 'ping transmit' units. The ping units - * unit will send out the value of millis() once a second. The pong unit will - * respond back with a copy of the value. Each ping unit can get that response - * back, and determine how long the whole cycle took. - * - * This example requires a bit more complexity to determine which unit is which. - * The pong receiver is identified by having its role_pin tied to ground. - * The ping senders are further differentiated by a byte in eeprom. - */ - -#include -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 - -RF24 radio(9,10); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'pong' receiver. -const int role_pin = 7; - -// -// Topology -// - -// Radio pipe addresses for the nodes to communicate. Only ping nodes need -// dedicated pipes in this topology. Each ping node has a talking pipe -// that it will ping into, and a listening pipe that it will listen for -// the pong. The pong node listens on all the ping node talking pipes -// and sends the pong back on the sending node's specific listening pipe. - -const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; -const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_invalid = 0, role_ping_out, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Address management -// - -// Where in EEPROM is the address stored? -const uint8_t address_at_eeprom_location = 0; - -// What is our address (SRAM cache of the address from EEPROM) -// Note that zero is an INVALID address. The pong back unit takes address -// 1, and the rest are 2-6 -uint8_t node_address; - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Address - // - - if ( role == role_pong_back ) - node_address = 1; - else - { - // Read the address from EEPROM - uint8_t reading = EEPROM.read(address_at_eeprom_location); - - // If it is in a valid range for node addresses, it is our - // address. - if ( reading >= 2 && reading <= 6 ) - node_address = reading; - - // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' - else - { - node_address = 0; - role = role_invalid; - } - } - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/starping/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - printf("ADDRESS: %i\n\r",node_address); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // The pong node listens on all the ping node talking pipes - // and sends the pong back on the sending node's specific listening pipe. - if ( role == role_pong_back ) - { - radio.openReadingPipe(1,talking_pipes[0]); - radio.openReadingPipe(2,talking_pipes[1]); - radio.openReadingPipe(3,talking_pipes[2]); - radio.openReadingPipe(4,talking_pipes[3]); - radio.openReadingPipe(5,talking_pipes[4]); - } - - // Each ping node has a talking pipe that it will ping into, and a listening - // pipe that it will listen for the pong. - if ( role == role_ping_out ) - { - // Write on our talking pipe - radio.openWritingPipe(talking_pipes[node_address-2]); - // Listen on our listening pipe - radio.openReadingPipe(1,listening_pipes[node_address-2]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Prompt the user to assign a node address if we don't have one - // - - if ( role == role_invalid ) - { - printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); - } -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 250 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - } - - // Try again 1s later - delay(1000); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - uint8_t pipe_num; - if ( radio.available(&pipe_num) ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (!done) - { - // Fetch the payload, and see if this was the last one. - done = radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got payload %lu from node %i...",got_time,pipe_num+1); - } - - // First, stop listening so we can talk - radio.stopListening(); - - // Open the correct pipe for writing - radio.openWritingPipe(listening_pipes[pipe_num-1]); - - // Retain the low 2 bytes to identify the pipe for the spew - uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - printf("Sent response to %04x.\n\r",pipe_id); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - } - } - - // - // Listen for serial input, which is how we set the address - // - if (Serial.available()) - { - // If the character on serial input is in a valid range... - char c = Serial.read(); - if ( c >= '1' && c <= '6' ) - { - // It is our address - EEPROM.write(address_at_eeprom_location,c-'0'); - - // And we are done right now (no easy way to soft reset) - printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); - while(1) ; - } - } -} -// vim:ai:ci sts=2 sw=2 ft=cpp diff --git a/RF24/keywords.txt b/RF24/keywords.txt deleted file mode 100644 index d829ee9..0000000 --- a/RF24/keywords.txt +++ /dev/null @@ -1,16 +0,0 @@ - RF24 KEYWORD1 - begin KEYWORD2 - setChannel KEYWORD2 - setPayloadSize KEYWORD2 - getPayloadSize KEYWORD2 - print_details KEYWORD2 - startListening KEYWORD2 - stopListening KEYWORD2 - write KEYWORD2 - writeFast KEYWORD2 - writeBlocking KEYWORD2 - txStandBy KEYWORD2 - available KEYWORD2 - read KEYWORD2 - openWritingPipe KEYWORD2 - openReadingPipe KEYWORD2 \ No newline at end of file diff --git a/RF24/nRF24L01.h b/RF24/nRF24L01.h deleted file mode 100644 index 132de94..0000000 --- a/RF24/nRF24L01.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright (c) 2007 Stefan Engelke - Portions Copyright (C) 2011 Greg Copeland - - 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. -*/ - -/* Memory Map */ -#define CONFIG 0x00 -#define EN_AA 0x01 -#define EN_RXADDR 0x02 -#define SETUP_AW 0x03 -#define SETUP_RETR 0x04 -#define RF_CH 0x05 -#define RF_SETUP 0x06 -#define STATUS 0x07 -#define OBSERVE_TX 0x08 -#define CD 0x09 -#define RX_ADDR_P0 0x0A -#define RX_ADDR_P1 0x0B -#define RX_ADDR_P2 0x0C -#define RX_ADDR_P3 0x0D -#define RX_ADDR_P4 0x0E -#define RX_ADDR_P5 0x0F -#define TX_ADDR 0x10 -#define RX_PW_P0 0x11 -#define RX_PW_P1 0x12 -#define RX_PW_P2 0x13 -#define RX_PW_P3 0x14 -#define RX_PW_P4 0x15 -#define RX_PW_P5 0x16 -#define FIFO_STATUS 0x17 -#define DYNPD 0x1C -#define FEATURE 0x1D - -/* Bit Mnemonics */ -#define MASK_RX_DR 6 -#define MASK_TX_DS 5 -#define MASK_MAX_RT 4 -#define EN_CRC 3 -#define CRCO 2 -#define PWR_UP 1 -#define PRIM_RX 0 -#define ENAA_P5 5 -#define ENAA_P4 4 -#define ENAA_P3 3 -#define ENAA_P2 2 -#define ENAA_P1 1 -#define ENAA_P0 0 -#define ERX_P5 5 -#define ERX_P4 4 -#define ERX_P3 3 -#define ERX_P2 2 -#define ERX_P1 1 -#define ERX_P0 0 -#define AW 0 -#define ARD 4 -#define ARC 0 -#define PLL_LOCK 4 -#define RF_DR 3 -#define RF_PWR 6 -#define RX_DR 6 -#define TX_DS 5 -#define MAX_RT 4 -#define RX_P_NO 1 -#define TX_FULL 0 -#define PLOS_CNT 4 -#define ARC_CNT 0 -#define TX_REUSE 6 -#define FIFO_FULL 5 -#define TX_EMPTY 4 -#define RX_FULL 1 -#define RX_EMPTY 0 -#define DPL_P5 5 -#define DPL_P4 4 -#define DPL_P3 3 -#define DPL_P2 2 -#define DPL_P1 1 -#define DPL_P0 0 -#define EN_DPL 2 -#define EN_ACK_PAY 1 -#define EN_DYN_ACK 0 - -/* Instruction Mnemonics */ -#define R_REGISTER 0x00 -#define W_REGISTER 0x20 -#define REGISTER_MASK 0x1F -#define ACTIVATE 0x50 -#define R_RX_PL_WID 0x60 -#define R_RX_PAYLOAD 0x61 -#define W_TX_PAYLOAD 0xA0 -#define W_ACK_PAYLOAD 0xA8 -#define FLUSH_TX 0xE1 -#define FLUSH_RX 0xE2 -#define REUSE_TX_PL 0xE3 -#define NOP 0xFF - -/* Non-P omissions */ -#define LNA_HCURR 0 - -/* P model memory Map */ -#define RPD 0x09 -#define W_TX_PAYLOAD_NO_ACK 0xB0 - -/* P model bit Mnemonics */ -#define RF_DR_LOW 5 -#define RF_DR_HIGH 3 -#define RF_PWR_LOW 1 -#define RF_PWR_HIGH 2 diff --git a/RF24/tests/README b/RF24/tests/README deleted file mode 100644 index 43ceaf5..0000000 --- a/RF24/tests/README +++ /dev/null @@ -1,7 +0,0 @@ -The sketches in this directory are intended to be checkin tests. -No code should be pushed to github without these tests passing. - -See "runtests.sh" script inside each sketch dir. This script is fully compatible with -git bisest. - -Note that this requires python and py-serial diff --git a/RF24/tests/native/Jamfile b/RF24/tests/native/Jamfile deleted file mode 100644 index 10d0336..0000000 --- a/RF24/tests/native/Jamfile +++ /dev/null @@ -1,300 +0,0 @@ -PROJECT_NAME = $(PWD:B) ; -PROJECT_DIR = . ; -PROJECT_LIBS = RF24 ; - -OUT_DIR = ojam ; -F_CPU = 16000000 ; -MCU = atmega328p ; -PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; - -UPLOAD_RATE = 57600 ; -AVRDUDE_PROTOCOL = stk500v1 ; -COM = 33 ; - -# Host-specific overrides for locations -if $(OS) = MACOSX -{ -ARDUINO_VERSION = 22 ; -OLD_DIR = /opt/arduino-0021 ; -AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; -AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; -ARDUINO_DIR = /opt/Arduino ; -ARDUINO_AVR = /usr/lib/avr/include ; -} - -# Where is everything? -ARDUINO_VERSION ?= 22 ; -SKETCH_DIR = $(HOME)/Source/Arduino ; -AVR_TOOLS_PATH ?= /usr/bin ; -ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; -ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; -AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(SKETCH_DIR)/libraries ; -AVR_AS = $(AVR_TOOLS_PATH)/avr-as ; -AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; -AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; -AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; -AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; - -DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H HAL=1 ; -CTUNING = -ffunction-sections -fdata-sections ; -CXXTUNING = -fno-exceptions -fno-strict-aliasing ; -ASFLAGS = -mmcu=$(MCU) ; -CFLAGS = -Os -Wall -Wextra $(ASFLAGS) $(CTUNING) ; -CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; -LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; - -# Search everywhere for headers -HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; -HDRS += [ GLOB $(HDRS) : utility ] ; - -# Grab everything from the core directory -CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# In addition to explicitly-specified program modules, pick up anything from the current -# dir. -PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; - -# Shortcut for the out files -OUT = $(OUT_DIR)/$(PROJECT_NAME) ; - -# AvrDude setup -AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -# GitVersion version.h ; - -rule AvrAsm -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrAsm -{ - $(AVR_AS) $(ASFLAGS) -o $(<) $(>) -} - -rule AvrCc -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrCc -{ - $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) -} - -rule AvrC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrC++ -{ - $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule AvrAsmFromC++ -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; - - CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; - CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; -} - -actions AvrAsmFromC++ -{ - $(AVR_CXX) -S -fverbose-asm -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) -} - -rule Pde -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Clean clean : $(<) ; -} - -actions Pde -{ - echo "#include " > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule AvrPde -{ - local _CPP = $(OUT_DIR)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - AvrC++ $(<) : $(_CPP) ; -} - -rule AvrObject -{ - switch $(>:S) - { - case .S : AvrAsm $(<) : $(>) ; - case .c : AvrCc $(<) : $(>) ; - case .cpp : AvrC++ $(<) : $(>) ; - case .pde : AvrPde $(<) : $(>) ; - } -} - -rule AvrObjects -{ - for _I in $(<) - { - AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; - } -} - -rule AvrMainFromObjects -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - MkDir $(<:D) ; - Depends all : $(<) ; - Clean clean : $(<) ; -} - -actions AvrMainFromObjects -{ - $(AVR_LD) $(LDFLAGS) -o $(<) $(>) -} - -rule AvrMain -{ - AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; - AvrObjects $(>) ; -} - -rule AvrHex -{ - Depends $(<) : $(>) ; - Depends $(<) : $(<:D) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions AvrHex -{ - $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule AvrUpload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - AvrUploadAction $(2) : $(3) ; -} - -actions AvrUploadAction -{ - $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) -AvrHex $(OUT).hex : $(OUT).elf ; - -AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; -AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; -AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; - -# -# Native -# - -OUT_DIR_NATIVE = out_native ; -OUT_NATIVE = $(OUT_DIR_NATIVE)/$(PROJECT_NAME) ; -NATIVE_CORE = $(SKETCH_DIR)/hardware/native ; -HDRS = $(NATIVE_CORE) $(HDRS) ; -NATIVE_CORE_MODULES = [ GLOB $(NATIVE_CORE) : *.c *.cpp ] ; -NATIVE_MODULES = ; -DEFINES += NATIVE ; - -rule NativePde -{ - local _CPP = $(OUT_DIR_NATIVE)/$(_I:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>) - { - case *.pde : NativePde $(<) : $(>) ; - } -} - -rule Objects -{ - for _I in $(<) - { - local _O = $(OUT_DIR_NATIVE)/$(_I:B).o ; - Object $(_O) : $(_I) ; - } -} - -rule Main -{ - MainFromObjects $(<) : $(OUT_DIR_NATIVE)/$(>:B).o ; - Objects $(>) ; -} - -actions C++ -{ - c++ -c -o $(<) $(CCHDRS) $(CCDEFS) $(>) -} - -actions Link -{ - c++ -o $(<) $(>) -} - - - -MkDir $(OUT_DIR_NATIVE) ; -Depends $(OUT_NATIVE) : $(OUT_DIR_NATIVE) ; -Main $(OUT_NATIVE) : $(NATIVE_CORE_MODULES) $(NATIVE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; - -Depends native : $(OUT_NATIVE) ; - diff --git a/RF24/tests/native/pingpair_irq.pde b/RF24/tests/native/pingpair_irq.pde deleted file mode 100644 index 99c2cdf..0000000 --- a/RF24/tests/native/pingpair_irq.pde +++ /dev/null @@ -1,223 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Interrupt-driven test for native target - * - * This example is the friendliest for the native target because it doesn't do - * any polling. Made a slight change to call done() at the end of setup. - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(8,9); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 7; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -// Interrupt handler, check the radio because we got an IRQ -void check_radio(void); - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/examples/pingpair_irq/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipe for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(1,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Attach interrupt handler to interrupt #0 (using pin 2) - // on BOTH the sender and receiver - // - - attachInterrupt(0, check_radio, FALLING); - - // - // On the native target, this is as far as we get - // -#if NATIVE - done(); -#endif -} - -static uint32_t message_count = 0; - -void loop(void) -{ - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender) - { - // Take the time, and send it. - unsigned long time = millis(); - printf("Now sending %lu\n\r",time); - radio.startWrite( &time, sizeof(unsigned long) ); - - // Try again soon - delay(2000); - } - - // - // Receiver role: Does nothing! All the work is in IRQ - // - -} - -void check_radio(void) -{ - // What happened? - bool tx,fail,rx; - radio.whatHappened(tx,fail,rx); - - // Have we successfully transmitted? - if ( tx ) - { - if ( role == role_sender ) - printf("Send:OK\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Sent\n\r"); - } - - // Have we failed to transmit? - if ( fail ) - { - if ( role == role_sender ) - printf("Send:Failed\n\r"); - - if ( role == role_receiver ) - printf("Ack Payload:Failed\n\r"); - } - - // Transmitter can power down for now, because - // the transmission is done. - if ( ( tx || fail ) && ( role == role_sender ) ) - radio.powerDown(); - - // Did we receive a message? - if ( rx ) - { - // If we're the sender, we've received an ack payload - if ( role == role_sender ) - { - radio.read(&message_count,sizeof(message_count)); - printf("Ack:%lu\n\r",(unsigned long)message_count); - } - - // If we're the receiver, we've received a time message - if ( role == role_receiver ) - { - // Get this payload and dump it - static unsigned long got_time; - radio.read( &got_time, sizeof(got_time) ); - printf("Got payload %lu\n\r",got_time); - - // Add an ack packet for the next time around. This is a simple - // packet counter - radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); - ++message_count; - } - } -} - -// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24/tests/native/printf.h b/RF24/tests/native/printf.h deleted file mode 100644 index df6c46a..0000000 --- a/RF24/tests/native/printf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#include "WProgram.h" - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#endif // __PRINTF_H__ diff --git a/RF24/tests/pingpair_blocking/Jamfile b/RF24/tests/pingpair_blocking/Jamfile deleted file mode 100644 index 18244ec..0000000 --- a/RF24/tests/pingpair_blocking/Jamfile +++ /dev/null @@ -1,219 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = SPI RF24 ; -PROJECT_DIRS = $(PWD) ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 115200 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN ?= /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN ?= /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -AR = $(AVR_BIN)/avr-ar rcs ; -RANLIB = ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE ?= $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Library -{ - LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -rule Arduino -{ - LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; - Main $(<) : $(>) ; - LinkLibraries $(<) : core libs ; - Hex $(<:B).hex : $(<) ; - for _p in $(PORTS) - { - Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; - } -} - -# -# Targets -# - -# Grab everything from the core directory -Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Main output executable -Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/RF24/tests/pingpair_blocking/pingpair_blocking.pde b/RF24/tests/pingpair_blocking/pingpair_blocking.pde deleted file mode 100644 index 004b421..0000000 --- a/RF24/tests/pingpair_blocking/pingpair_blocking.pde +++ /dev/null @@ -1,273 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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 -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Test version of RF24, exposes some protected interface -// - -class RF24Test: public RF24 -{ -public: RF24Test(int a, int b): RF24(a,b) {} -}; - - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24Test radio(48,49); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const int role_pin = 5; - -// -// Topology -// - -// Radio pipe addresses for the 2 nodes to communicate. -const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes -// in this system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_ping_out = 1, role_pong_back } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; - -// The role of the current running sketch -role_e role; - -// -// Test state -// - -bool done; //*< Are we done with the test? */ -bool passed; //*< Have we passed the test? */ -bool notified; //*< Have we notified the user we're done? */ -const int num_needed = 10; //*< How many success/failures until we're done? */ -int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ -int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ -const int interval = 100; //*< ms to wait between sends */ - -char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ - -void one_ok(void) -{ - // Have we received enough yet? - if ( ! --receives_remaining ) - { - done = true; - passed = true; - } -} - -void one_failed(void) -{ - // Have we failed enough yet? - if ( ! --failures_remaining ) - { - done = true; - passed = false; - } -} - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_ping_out; - else - role = role_pong_back; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/tests/pingpair_blocking/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // get test config - // - - printf("+READY press any key to start\n\r\n\r"); - - while (! Serial.available() ) {} - configuration = Serial.read(); - printf("Configuration\t = %c\n\r",configuration); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens two pipes for these two nodes to communicate - // back and forth. - // Open 'our' pipe for writing - // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) - - if ( role == role_ping_out ) - { - radio.openWritingPipe(pipes[0]); - radio.openReadingPipe(1,pipes[1]); - } - else - { - radio.openWritingPipe(pipes[1]); - radio.openReadingPipe(1,pipes[0]); - } - - // - // Start listening - // - - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - if ( role == role_pong_back ) - printf("\n\r+OK "); -} - -void loop(void) -{ - // - // Ping out role. Repeatedly send the current time - // - - if (role == role_ping_out) - { - // First, stop listening so we can talk. - radio.stopListening(); - - // Take the time, and send it. This will block until complete - unsigned long time = millis(); - printf("Now sending %lu...",time); - radio.write( &time, sizeof(unsigned long) ); - - // Now, continue listening - radio.startListening(); - - // Wait here until we get a response, or timeout (250ms) - unsigned long started_waiting_at = millis(); - bool timeout = false; - while ( ! radio.available() && ! timeout ) - if (millis() - started_waiting_at > 200 ) - timeout = true; - - // Describe the results - if ( timeout ) - { - printf("Failed, response timed out.\n\r"); - one_failed(); - } - else - { - // Grab the response, compare, and send to debugging spew - unsigned long got_time; - radio.read( &got_time, sizeof(unsigned long) ); - - // Spew it - printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); - one_ok(); - } - - // Try again later - delay(250); - } - - // - // Pong back role. Receive each packet, dump it out, and send it back - // - - if ( role == role_pong_back ) - { - // if there is data ready - if ( radio.available() ) - { - // Dump the payloads until we've gotten everything - unsigned long got_time; - bool done = false; - while (radio.available()) - { - // Fetch the payload, and see if this was the last one. - radio.read( &got_time, sizeof(unsigned long) ); - } - // Delay just a little bit to let the other unit - // make the transition to receiver - //delay(20); - //} - - // First, stop listening so we can talk - radio.stopListening(); - - // Spew it - printf("Got payload %lu...",got_time); - - // Send the final one back. - radio.write( &got_time, sizeof(unsigned long) ); - - // Now, resume listening so we catch the next packets. - radio.startListening(); - printf("Sent response.\n\r"); - - } - } - - // - // Stop the test if we're done and report results - // - if ( done && ! notified ) - { - notified = true; - - printf("\n\r+OK "); - if ( passed ) - printf("PASS\n\r\n\r"); - else - printf("FAIL\n\r\n\r"); - } -} -// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/RF24/tests/pingpair_blocking/printf.h b/RF24/tests/pingpair_blocking/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/tests/pingpair_blocking/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/tests/pingpair_blocking/runtest.py b/RF24/tests/pingpair_blocking/runtest.py deleted file mode 100755 index 0772f95..0000000 --- a/RF24/tests/pingpair_blocking/runtest.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/python - -import sys,serial - -def read_until(token): - while 1: - line = ser.readline(None) - sys.stdout.write(line) - - if (line.startswith(token)): - break - return line - - -ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) - -read_until("+READY") -ser.write(sys.argv[2]) - -line = read_until("+OK") -ser.close() -if (line.find("PASS") != -1): - sys.exit(0) -else: - sys.exit(1) diff --git a/RF24/tests/pingpair_blocking/runtests.sh b/RF24/tests/pingpair_blocking/runtests.sh deleted file mode 100755 index e106448..0000000 --- a/RF24/tests/pingpair_blocking/runtests.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -# Connect u0 to receiver, u1 to sender - -jam u0 u1 && expect test.ex diff --git a/RF24/tests/pingpair_blocking/test.ex b/RF24/tests/pingpair_blocking/test.ex deleted file mode 100755 index ea992ad..0000000 --- a/RF24/tests/pingpair_blocking/test.ex +++ /dev/null @@ -1,11 +0,0 @@ -#/usr/bin/expect - -set timeout 100 -spawn picocom -b 57600 /dev/ttyUSB0 -expect "+READY" -send "1" -expect "+OK" -spawn picocom -b 57600 /dev/ttyUSB1 -expect "+READY" -send "1" -expect "+OK" diff --git a/RF24/tests/pingpair_test/Jamfile b/RF24/tests/pingpair_test/Jamfile deleted file mode 100644 index 18244ec..0000000 --- a/RF24/tests/pingpair_test/Jamfile +++ /dev/null @@ -1,219 +0,0 @@ -# (1) Project Information - -PROJECT_LIBS = SPI RF24 ; -PROJECT_DIRS = $(PWD) ; - -# (2) Board Information - -UPLOAD_PROTOCOL ?= arduino ; -UPLOAD_SPEED ?= 115200 ; -MCU ?= atmega328p ; -F_CPU ?= 16000000 ; -CORE ?= arduino ; -VARIANT ?= standard ; -ARDUINO_VERSION ?= 100 ; - -# (3) USB Ports - -PORTS = p4 p6 p9 u0 u1 u2 ; -PORT_p6 = /dev/tty.usbserial-A600eHIs ; -PORT_p4 = /dev/tty.usbserial-A40081RP ; -PORT_p9 = /dev/tty.usbserial-A9007LmI ; -PORT_u0 = /dev/ttyUSB0 ; -PORT_u1 = /dev/ttyUSB1 ; -PORT_u2 = /dev/ttyUSB2 ; - -# (4) Location of AVR tools -# -# This configuration assumes using avr-tools that were obtained separate from the Arduino -# distribution. - -if $(OS) = MACOSX -{ - AVR_BIN ?= /usr/local/avrtools/bin ; - AVR_ETC = /usr/local/avrtools/etc ; - AVR_INCLUDE = /usr/local/avrtools/include ; -} -else -{ - AVR_BIN ?= /usr/bin ; - AVR_INCLUDE = /usr/lib/avr/include ; - AVR_ETC = /etc ; -} - -# (5) Directories where Arduino core and libraries are located - -ARDUINO_DIR ?= /opt/Arduino ; -ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; -ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; -SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; - -# -# -------------------------------------------------- -# Below this line usually never needs to be modified -# - -# Tool locations - -CC = $(AVR_BIN)/avr-gcc ; -C++ = $(AVR_BIN)/avr-g++ ; -LINK = $(AVR_BIN)/avr-gcc ; -AR = $(AVR_BIN)/avr-ar rcs ; -RANLIB = ; -OBJCOPY = $(AVR_BIN)/avr-objcopy ; -AVRDUDE ?= $(AVR_BIN)/avrdude ; - -# Flags - -DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; -OPTIM = -Os ; -CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; -C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; -LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; -AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; - -# Search everywhere for headers - -HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; - -# Output locations - -LOCATE_TARGET = $(F_CPU) ; -LOCATE_SOURCE = $(F_CPU) ; - -# -# Custom rules -# - -rule GitVersion -{ - Always $(<) ; - Depends all : $(<) ; -} - -actions GitVersion -{ - echo "const char program_version[] = \"\\" > $(<) - git log -1 --pretty=format:%h >> $(<) - echo "\";" >> $(<) -} - -GitVersion version.h ; - -rule Pde -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_SOURCE) ; - Clean clean : $(<) ; -} - -if ( $(ARDUINO_VERSION) < 100 ) -{ - ARDUINO_H = WProgram.h ; -} -else -{ - ARDUINO_H = Arduino.h ; -} - -actions Pde -{ - echo "#include <$(ARDUINO_H)>" > $(<) - echo "#line 1 \"$(>)\"" >> $(<) - cat $(>) >> $(<) -} - -rule C++Pde -{ - local _CPP = $(>:B).cpp ; - Pde $(_CPP) : $(>) ; - C++ $(<) : $(_CPP) ; -} - -rule UserObject -{ - switch $(>:S) - { - case .ino : C++Pde $(<) : $(>) ; - case .pde : C++Pde $(<) : $(>) ; - } -} - -rule Objects -{ - local _i ; - - for _i in [ FGristFiles $(<) ] - { - local _b = $(_i:B)$(SUFOBJ) ; - local _o = $(_b:G=$(SOURCE_GRIST:E)) ; - Object $(_o) : $(_i) ; - Depends obj : $(_o) ; - } -} - -rule Library -{ - LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Main -{ - MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; - Objects $(>) ; -} - -rule Hex -{ - Depends $(<) : $(>) ; - MakeLocate $(<) : $(LOCATE_TARGET) ; - Depends hex : $(<) ; - Clean clean : $(<) ; -} - -actions Hex -{ - $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) -} - -rule Upload -{ - Depends $(1) : $(2) ; - Depends $(2) : $(3) ; - NotFile $(1) ; - Always $(1) ; - Always $(2) ; - UploadAction $(2) : $(3) ; -} - -actions UploadAction -{ - $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i -} - -rule Arduino -{ - LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; - Main $(<) : $(>) ; - LinkLibraries $(<) : core libs ; - Hex $(<:B).hex : $(<) ; - for _p in $(PORTS) - { - Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; - } -} - -# -# Targets -# - -# Grab everything from the core directory -Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; - -# Grab everything from libraries. To avoid this "grab everything" behaviour, you -# can specify specific modules to pick up in PROJECT_MODULES -Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; - -# Main output executable -Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/RF24/tests/pingpair_test/pingpair_test.pde b/RF24/tests/pingpair_test/pingpair_test.pde deleted file mode 100644 index 7685ca8..0000000 --- a/RF24/tests/pingpair_test/pingpair_test.pde +++ /dev/null @@ -1,435 +0,0 @@ -/* - Copyright (C) 2011 James Coliz, Jr. - - 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. - */ - -/** - * Full test on single RF pair - * - * This sketches uses as many RF24 methods as possible in a single test. - * - * To operate: - * Upload this sketch on two nodes, each with IRQ -> pin 2 - * One node needs pin 7 -> GND, the other NC. That's the receiving node - * Monitor the sending node's serial output - * Look for "+OK PASS" or "+OK FAIL" - */ - -#include -#include "nRF24L01.h" -#include "RF24.h" -#include "printf.h" - -// -// Hardware configuration -// - -// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 - -RF24 radio(7,8); - -// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver -// Leave open to be the 'ping' transmitter -const short role_pin = 5; - -// -// Topology -// - -// Single radio pipe address for the 2 nodes to communicate. -const uint64_t pipe = 0xE8E8F0F0E1LL; - -// -// Role management -// -// Set up role. This sketch uses the same software for all the nodes in this -// system. Doing so greatly simplifies testing. The hardware itself specifies -// which node it is. -// -// This is done through the role_pin -// - -// The various roles supported by this sketch -typedef enum { role_sender = 1, role_receiver } role_e; - -// The debug-friendly names of those roles -const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; - -// The role of the current running sketch -role_e role; - -// Interrupt handler, check the radio because we got an IRQ -void check_radio(void); - -// -// Payload -// - -const int min_payload_size = 4; -const int max_payload_size = 32; -int payload_size_increments_by = 2; -int next_payload_size = min_payload_size; - -char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char - -// -// Test state -// - -bool done; //*< Are we done with the test? */ -bool passed; //*< Have we passed the test? */ -bool notified; //*< Have we notified the user we're done? */ -const int num_needed = 10; //*< How many success/failures until we're done? */ -int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ -int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ -const int interval = 100; //*< ms to wait between sends */ - -char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ - -uint8_t pipe_number = 1; // Which pipe to send on. - -void one_ok(void) -{ - // Have we received enough yet? - if ( ! --receives_remaining ) - { - done = true; - passed = true; - } -} - -void one_failed(void) -{ - // Have we failed enough yet? - if ( ! --failures_remaining ) - { - done = true; - passed = false; - } -} - -// -// Setup -// - -void setup(void) -{ - // - // Role - // - - // set up the role pin - pinMode(role_pin, INPUT); - digitalWrite(role_pin,HIGH); - delay(20); // Just to get a solid reading on the role pin - - // read the address pin, establish our role - if ( digitalRead(role_pin) ) - role = role_sender; - else - role = role_receiver; - - // - // Print preamble - // - - Serial.begin(57600); - printf_begin(); - printf("\n\rRF24/tests/pingpair_test/\n\r"); - printf("ROLE: %s\n\r",role_friendly_name[role]); - - // - // Read configuration from serial - // - // It would be a much better test if this program could accept configuration - // from the serial port. Then it would be possible to run the same test under - // lots of different circumstances. - // - // The idea is that we will print "+READY" at this point. The python script - // will wait for it, and then send down a configuration script that we - // execute here and then run with. - // - // The test controller will need to configure the receiver first, then go run - // the test on the sender. - // - - printf("+READY press any key to start\n\r\n\r"); - - while (! Serial.available() ) {} - configuration = Serial.read(); - printf("Configuration\t = %c\n\r",configuration); - - // - // Setup and configure rf radio - // - - radio.begin(); - - // We will be using the Ack Payload feature, so please enable it - radio.enableAckPayload(); - - // Config 2 is special radio config - if (configuration=='2') - { - radio.setCRCLength(RF24_CRC_8); - radio.setDataRate(RF24_250KBPS); - radio.setChannel(10); - } - else - { - //Otherwise, default radio config - - // Optional: Increase CRC length for improved reliability - radio.setCRCLength(RF24_CRC_16); - - // Optional: Decrease data rate for improved reliability - radio.setDataRate(RF24_1MBPS); - - // Optional: Pick a high channel - radio.setChannel(90); - } - - // Config 3 is static payloads only - if (configuration == '3') - { - next_payload_size = 16; - payload_size_increments_by = 0; - radio.setPayloadSize(next_payload_size); - } - else - { - // enable dynamic payloads - radio.enableDynamicPayloads(); - } - - // Config 4 tests out a higher pipe ## - if (configuration == '4' && role == role_sender) - { - // Set top 4 bytes of the address in pipe 1 - radio.openReadingPipe(1,pipe & 0xFFFFFFFF00ULL); - - // indicate the pipe to use - pipe_number = 5; - } - else if ( role == role_sender ) - { - radio.openReadingPipe(5,0); - } - - // - // Open pipes to other nodes for communication - // - - // This simple sketch opens a single pipe for these two nodes to communicate - // back and forth. One listens on it, the other talks to it. - - if ( role == role_sender ) - { - radio.openWritingPipe(pipe); - } - else - { - radio.openReadingPipe(pipe_number,pipe); - } - - // - // Start listening - // - - if ( role == role_receiver ) - radio.startListening(); - - // - // Dump the configuration of the rf unit for debugging - // - - radio.printDetails(); - - // - // Attach interrupt handler to interrupt #0 (using pin 2) - // on BOTH the sender and receiver - // - - attachInterrupt(0, check_radio, FALLING); - delay(50); - if ( role == role_receiver ) - printf("\n\r+OK "); -} - -// -// Print buffer -// -// Printing from the interrupt handler is a bad idea, so we print from there -// to this intermediate buffer -// - -char prbuf[1000]; -char *prbuf_end = prbuf + sizeof(prbuf); -char *prbuf_in = prbuf; -char *prbuf_out = prbuf; - -// -// Loop -// - -static uint32_t message_count = 0; -static uint32_t last_message_count = 0; - -void loop(void) -{ - // - // Sender role. Repeatedly send the current time - // - - if (role == role_sender && !done) - { - // The payload will always be the same, what will change is how much of it we send. - static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; - - // First, stop listening so we can talk. - radio.stopListening(); - - // Send it. This will block until complete - printf("\n\rNow sending length %i...",next_payload_size); - radio.startWrite( send_payload, next_payload_size,0 ); - - // Update size for next time. - next_payload_size += payload_size_increments_by; - if ( next_payload_size > max_payload_size ) - next_payload_size = min_payload_size; - - // Try again soon - delay(interval); - - // Timeout if we have not received anything back ever - if ( ! last_message_count && millis() > interval * 100 ) - { - printf("No responses received. Are interrupts connected??\n\r"); - done = true; - } - } - - // - // Receiver role: Does nothing! All the work is in IRQ - // - - // - // Spew print buffer - // - - size_t write_length = prbuf_in - prbuf_out; - if ( write_length ) - { - Serial.write(reinterpret_cast(prbuf_out),write_length); - prbuf_out += write_length; - } - - // - // Stop the test if we're done and report results - // - if ( done && ! notified ) - { - notified = true; - - printf("\n\r+OK "); - if ( passed ) - printf("PASS\n\r\n\r"); - else - printf("FAIL\n\r\n\r"); - } - -} - -void check_radio(void) -{ - // What happened? - bool tx,fail,rx; - radio.whatHappened(tx,fail,rx); - - // Have we successfully transmitted? - if ( tx ) - { - if ( role == role_sender ) - prbuf_in += sprintf(prbuf_in,"Send:OK "); - - if ( role == role_receiver ) - prbuf_in += sprintf(prbuf_in,"Ack Payload:Sent\n\r"); - } - - // Have we failed to transmit? - if ( fail ) - { - if ( role == role_sender ) - { - prbuf_in += sprintf(prbuf_in,"Send:Failed "); - - // log status of this line - one_failed(); - } - - if ( role == role_receiver ) - prbuf_in += sprintf(prbuf_in,"Ack Payload:Failed\n\r"); - } - - // Not powering down since radio is in standby mode - //if ( ( tx || fail ) && ( role == role_sender ) ) - //radio.powerDown(); - - // Did we receive a message? - if ( rx ) - { - // If we're the sender, we've received an ack payload - if ( role == role_sender ) - { - radio.read(&message_count,sizeof(message_count)); - prbuf_in += sprintf(prbuf_in,"Ack:%lu ",message_count); - - // is this ack what we were expecting? to account - // for failures, we simply want to make sure we get a - // DIFFERENT ack every time. - if ( ( message_count != last_message_count ) || ( configuration=='3' && message_count == 16 ) ) - { - prbuf_in += sprintf(prbuf_in,"OK "); - one_ok(); - } - else - { - prbuf_in += sprintf(prbuf_in,"FAILED "); - one_failed(); - } - last_message_count = message_count; - } - - // If we're the receiver, we've received a time message - if ( role == role_receiver ) - { - // Get this payload and dump it - size_t len = max_payload_size; - memset(receive_payload,0,max_payload_size); - - if ( configuration == '3' ){ - len = next_payload_size; - }else{ - len = radio.getDynamicPayloadSize(); - } - - radio.read( receive_payload, len ); - - // Put a zero at the end for easy printing - receive_payload[len] = 0; - - // Spew it - prbuf_in += sprintf(prbuf_in,"Recv size=%i val=%s len=%u\n\r",len,receive_payload,strlen(receive_payload)); - - // Add an ack packet for the next time around. - // Here we will report back how many bytes we got this time. - radio.writeAckPayload( pipe_number, &len, sizeof(len) ); - - ++message_count; - } - - } -} diff --git a/RF24/tests/pingpair_test/printf.h b/RF24/tests/pingpair_test/printf.h deleted file mode 100644 index b2efd56..0000000 --- a/RF24/tests/pingpair_test/printf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2011 J. Coliz - - 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. - */ - -/** - * @file printf.h - * - * Setup necessary to direct stdout to the Arduino Serial library, which - * enables 'printf' - */ - -#ifndef __PRINTF_H__ -#define __PRINTF_H__ - -#ifdef ARDUINO - -int serial_putc( char c, FILE * ) -{ - Serial.write( c ); - - return c; -} - -void printf_begin(void) -{ - fdevopen( &serial_putc, 0 ); -} - -#else -#error This example is only for use on Arduino. -#endif // ARDUINO - -#endif // __PRINTF_H__ diff --git a/RF24/tests/pingpair_test/runtest.py b/RF24/tests/pingpair_test/runtest.py deleted file mode 100755 index 45fb65c..0000000 --- a/RF24/tests/pingpair_test/runtest.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/opt/local/bin/python - -import sys,serial - -def read_until(token): - while 1: - line = ser.readline(None,"\r") - sys.stdout.write(line) - - if (line.startswith(token)): - break - return line - - -ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) - -read_until("+READY") -ser.write(sys.argv[2]) - -line = read_until("+OK") -ser.close() -if (line.find("PASS") != -1): - sys.exit(0) -else: - sys.exit(1) diff --git a/RF24/tests/pingpair_test/runtests.sh b/RF24/tests/pingpair_test/runtests.sh deleted file mode 100755 index 4d02310..0000000 --- a/RF24/tests/pingpair_test/runtests.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -# Connect u0 to receiver, u0 to sender -# WARNING: Test config 2 only works with PLUS units. - -jam u0 u1 && expect test.ex 1 -sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB0 -sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB1 -expect test.ex 2 -sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB0 -sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB1 -expect test.ex 3 -sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB0 -sleep 1 -stty 57600 raw ignbrk hup < /dev/ttyUSB1 -expect test.ex 4 diff --git a/RF24/tests/pingpair_test/test.ex b/RF24/tests/pingpair_test/test.ex deleted file mode 100755 index a14ffef..0000000 --- a/RF24/tests/pingpair_test/test.ex +++ /dev/null @@ -1,11 +0,0 @@ -#/usr/bin/expect - -set timeout 100 -spawn picocom -b 57600 /dev/ttyUSB0 -expect "+READY" -send [lindex $argv 0] -expect "+OK" -spawn picocom -b 57600 /dev/ttyUSB1 -expect "+READY" -send [lindex $argv 0] -expect "+OK" diff --git a/RF24/wikidoc.xslt b/RF24/wikidoc.xslt deleted file mode 100644 index b94d3ef..0000000 --- a/RF24/wikidoc.xslt +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - === === - - - - - '''' - -Parameters: - - - - * '''': - - - - - -Returns: - -* - - -Warning: - - - - <pre> </pre> - - - - - - - From ab3bad4b1c40e28c5ca8442d571ef9b85f04a1bf Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 1 May 2014 00:16:07 +0200 Subject: [PATCH 234/326] subree of RF24 merged --- libraries/RF24/.gitignore | 14 + libraries/RF24/Doxyfile | 1551 +++++++++++++++++ libraries/RF24/FAQ | 53 + libraries/RF24/README.md | 41 + libraries/RF24/RF24.cpp | 1220 +++++++++++++ libraries/RF24/RF24.h | 1124 ++++++++++++ libraries/RF24/RF24_config.h | 129 ++ libraries/RF24/RPi/RF24/Makefile | 55 + libraries/RF24/RPi/RF24/RF24.cpp | 1145 ++++++++++++ libraries/RF24/RPi/RF24/RF24.h | 1041 +++++++++++ libraries/RF24/RPi/RF24/RF24_config.h | 34 + libraries/RF24/RPi/RF24/bcm2835.c | 1399 +++++++++++++++ libraries/RF24/RPi/RF24/bcm2835.h | 1353 ++++++++++++++ libraries/RF24/RPi/RF24/examples/Makefile | 40 + .../RF24/RPi/RF24/examples/extra/Makefile | 40 + .../RF24/RPi/RF24/examples/extra/rpi-hub.cpp | 134 ++ .../RF24/RPi/RF24/examples/extra/scanner.cpp | 147 ++ .../RF24/RPi/RF24/examples/gettingstarted.cpp | 187 ++ .../examples/gettingstarted_call_response.cpp | 137 ++ .../RF24/RPi/RF24/examples/pingpair_dyn.cpp | 181 ++ libraries/RF24/RPi/RF24/examples/transfer.cpp | 148 ++ libraries/RF24/RPi/RF24/nRF24L01.h | 127 ++ libraries/RF24/RPi/RF24/readme.md | 117 ++ libraries/RF24/RPi/readme.md | 117 ++ libraries/RF24/doxygen-custom.css | 835 +++++++++ .../GettingStarted/GettingStarted.ino | 136 ++ .../RF24/examples/GettingStarted/printf.h | 37 + .../GettingStarted_CallResponse.ino | 124 ++ .../GettingStarted_CallResponse/printf.h | 37 + libraries/RF24/examples/Transfer/Transfer.ino | 144 ++ libraries/RF24/examples/Transfer/printf.h | 37 + .../TransferTimeouts/TransferTimeouts.ino | 189 ++ .../RF24/examples/TransferTimeouts/printf.h | 37 + .../RF24/examples/Usage/led_remote/Jamfile | 206 +++ .../examples/Usage/led_remote/led_remote.pde | 255 +++ .../RF24/examples/Usage/led_remote/printf.h | 37 + .../RF24/examples/Usage/nordic_fob/Jamfile | 219 +++ .../examples/Usage/nordic_fob/nordic_fob.pde | 142 ++ .../RF24/examples/Usage/nordic_fob/printf.h | 37 + .../examples/Usage/pingpair_maple/Jamfile | 182 ++ .../examples/Usage/pingpair_maple/main.cpp | 87 + .../Usage/pingpair_maple/pingpair_maple.pde | 242 +++ .../examples/pingpair_ack/pingpair_ack.ino | 123 ++ libraries/RF24/examples/pingpair_ack/printf.h | 37 + libraries/RF24/examples/pingpair_dyn/Jamfile | 206 +++ .../examples/pingpair_dyn/pingpair_dyn.ino | 232 +++ libraries/RF24/examples/pingpair_dyn/printf.h | 37 + .../examples/pingpair_irq/pingpair_irq.ino | 137 ++ libraries/RF24/examples/pingpair_irq/printf.h | 37 + .../pingpair_sleepy/pingpair_sleepy.ino | 225 +++ .../RF24/examples/pingpair_sleepy/printf.h | 37 + libraries/RF24/examples/scanner/Jamfile | 210 +++ libraries/RF24/examples/scanner/output/core.a | Bin 0 -> 187630 bytes .../RF24/examples/scanner/output/scanner.cpp | 128 ++ .../RF24/examples/scanner/output/scanner.elf | Bin 0 -> 100436 bytes .../RF24/examples/scanner/output/scanner.hex | 325 ++++ libraries/RF24/examples/scanner/printf.h | 31 + libraries/RF24/examples/scanner/scanner.ino | 126 ++ libraries/RF24/examples/starping/Jamfile | 206 +++ libraries/RF24/examples/starping/printf.h | 37 + libraries/RF24/examples/starping/starping.pde | 293 ++++ libraries/RF24/keywords.txt | 16 + libraries/RF24/nRF24L01.h | 127 ++ libraries/RF24/tests/README | 7 + libraries/RF24/tests/native/Jamfile | 300 ++++ libraries/RF24/tests/native/pingpair_irq.pde | 223 +++ libraries/RF24/tests/native/printf.h | 33 + .../RF24/tests/pingpair_blocking/Jamfile | 219 +++ .../pingpair_blocking/pingpair_blocking.pde | 273 +++ .../RF24/tests/pingpair_blocking/printf.h | 37 + .../RF24/tests/pingpair_blocking/runtest.py | 25 + .../RF24/tests/pingpair_blocking/runtests.sh | 5 + .../RF24/tests/pingpair_blocking/test.ex | 11 + libraries/RF24/tests/pingpair_test/Jamfile | 219 +++ .../tests/pingpair_test/pingpair_test.pde | 435 +++++ libraries/RF24/tests/pingpair_test/printf.h | 37 + libraries/RF24/tests/pingpair_test/runtest.py | 25 + .../RF24/tests/pingpair_test/runtests.sh | 21 + libraries/RF24/tests/pingpair_test/test.ex | 11 + libraries/RF24/wikidoc.xslt | 41 + 80 files changed, 18009 insertions(+) create mode 100644 libraries/RF24/.gitignore create mode 100644 libraries/RF24/Doxyfile create mode 100644 libraries/RF24/FAQ create mode 100644 libraries/RF24/README.md create mode 100644 libraries/RF24/RF24.cpp create mode 100644 libraries/RF24/RF24.h create mode 100644 libraries/RF24/RF24_config.h create mode 100644 libraries/RF24/RPi/RF24/Makefile create mode 100644 libraries/RF24/RPi/RF24/RF24.cpp create mode 100644 libraries/RF24/RPi/RF24/RF24.h create mode 100644 libraries/RF24/RPi/RF24/RF24_config.h create mode 100644 libraries/RF24/RPi/RF24/bcm2835.c create mode 100644 libraries/RF24/RPi/RF24/bcm2835.h create mode 100644 libraries/RF24/RPi/RF24/examples/Makefile create mode 100644 libraries/RF24/RPi/RF24/examples/extra/Makefile create mode 100644 libraries/RF24/RPi/RF24/examples/extra/rpi-hub.cpp create mode 100644 libraries/RF24/RPi/RF24/examples/extra/scanner.cpp create mode 100644 libraries/RF24/RPi/RF24/examples/gettingstarted.cpp create mode 100644 libraries/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp create mode 100644 libraries/RF24/RPi/RF24/examples/pingpair_dyn.cpp create mode 100644 libraries/RF24/RPi/RF24/examples/transfer.cpp create mode 100644 libraries/RF24/RPi/RF24/nRF24L01.h create mode 100644 libraries/RF24/RPi/RF24/readme.md create mode 100644 libraries/RF24/RPi/readme.md create mode 100644 libraries/RF24/doxygen-custom.css create mode 100644 libraries/RF24/examples/GettingStarted/GettingStarted.ino create mode 100644 libraries/RF24/examples/GettingStarted/printf.h create mode 100644 libraries/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino create mode 100644 libraries/RF24/examples/GettingStarted_CallResponse/printf.h create mode 100644 libraries/RF24/examples/Transfer/Transfer.ino create mode 100644 libraries/RF24/examples/Transfer/printf.h create mode 100644 libraries/RF24/examples/TransferTimeouts/TransferTimeouts.ino create mode 100644 libraries/RF24/examples/TransferTimeouts/printf.h create mode 100644 libraries/RF24/examples/Usage/led_remote/Jamfile create mode 100644 libraries/RF24/examples/Usage/led_remote/led_remote.pde create mode 100644 libraries/RF24/examples/Usage/led_remote/printf.h create mode 100644 libraries/RF24/examples/Usage/nordic_fob/Jamfile create mode 100644 libraries/RF24/examples/Usage/nordic_fob/nordic_fob.pde create mode 100644 libraries/RF24/examples/Usage/nordic_fob/printf.h create mode 100644 libraries/RF24/examples/Usage/pingpair_maple/Jamfile create mode 100644 libraries/RF24/examples/Usage/pingpair_maple/main.cpp create mode 100644 libraries/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde create mode 100644 libraries/RF24/examples/pingpair_ack/pingpair_ack.ino create mode 100644 libraries/RF24/examples/pingpair_ack/printf.h create mode 100644 libraries/RF24/examples/pingpair_dyn/Jamfile create mode 100644 libraries/RF24/examples/pingpair_dyn/pingpair_dyn.ino create mode 100644 libraries/RF24/examples/pingpair_dyn/printf.h create mode 100644 libraries/RF24/examples/pingpair_irq/pingpair_irq.ino create mode 100644 libraries/RF24/examples/pingpair_irq/printf.h create mode 100644 libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino create mode 100644 libraries/RF24/examples/pingpair_sleepy/printf.h create mode 100644 libraries/RF24/examples/scanner/Jamfile create mode 100644 libraries/RF24/examples/scanner/output/core.a create mode 100644 libraries/RF24/examples/scanner/output/scanner.cpp create mode 100755 libraries/RF24/examples/scanner/output/scanner.elf create mode 100644 libraries/RF24/examples/scanner/output/scanner.hex create mode 100644 libraries/RF24/examples/scanner/printf.h create mode 100644 libraries/RF24/examples/scanner/scanner.ino create mode 100644 libraries/RF24/examples/starping/Jamfile create mode 100644 libraries/RF24/examples/starping/printf.h create mode 100644 libraries/RF24/examples/starping/starping.pde create mode 100644 libraries/RF24/keywords.txt create mode 100644 libraries/RF24/nRF24L01.h create mode 100644 libraries/RF24/tests/README create mode 100644 libraries/RF24/tests/native/Jamfile create mode 100644 libraries/RF24/tests/native/pingpair_irq.pde create mode 100644 libraries/RF24/tests/native/printf.h create mode 100644 libraries/RF24/tests/pingpair_blocking/Jamfile create mode 100644 libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde create mode 100644 libraries/RF24/tests/pingpair_blocking/printf.h create mode 100755 libraries/RF24/tests/pingpair_blocking/runtest.py create mode 100755 libraries/RF24/tests/pingpair_blocking/runtests.sh create mode 100755 libraries/RF24/tests/pingpair_blocking/test.ex create mode 100644 libraries/RF24/tests/pingpair_test/Jamfile create mode 100644 libraries/RF24/tests/pingpair_test/pingpair_test.pde create mode 100644 libraries/RF24/tests/pingpair_test/printf.h create mode 100755 libraries/RF24/tests/pingpair_test/runtest.py create mode 100755 libraries/RF24/tests/pingpair_test/runtests.sh create mode 100755 libraries/RF24/tests/pingpair_test/test.ex create mode 100644 libraries/RF24/wikidoc.xslt diff --git a/libraries/RF24/.gitignore b/libraries/RF24/.gitignore new file mode 100644 index 0000000..ac32040 --- /dev/null +++ b/libraries/RF24/.gitignore @@ -0,0 +1,14 @@ +*.bak +*.o +.*.swp +*.orig +.swp +docs/ +output/ +ojam/ +out/ +16000000/ +8000000/ +out_native/ +version.h +Session.vim diff --git a/libraries/RF24/Doxyfile b/libraries/RF24/Doxyfile new file mode 100644 index 0000000..4604afc --- /dev/null +++ b/libraries/RF24/Doxyfile @@ -0,0 +1,1551 @@ +# Doxyfile 1.6.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = RF24 + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = v1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then 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. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +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. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. 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 that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip 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. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +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 only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +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 (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +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 (the default) these declarations will be included in the +# documentation. + +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 (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = 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. + +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. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +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. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED 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. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be 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. + +INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. 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. + +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 pattern (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 *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.h FAQ + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +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. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = NO + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = doxygen-custom.css + +# 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. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = 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. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# 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, 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. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, 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). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +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 +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +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. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# 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. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = 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. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# 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 very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = 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. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +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. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# 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 them the man command +# would be unable to find the correct page. The default is NO. + +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 captures the structure of +# the code including all documentation. + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# 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. + +XML_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 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. + +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 file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# 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. + +PERLMOD_LATEX = NO + +# 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 size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +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 (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all 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. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that 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. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +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 listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# 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. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need 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 setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# 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. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. 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. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. 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. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# 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 by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# 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 root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES 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. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/libraries/RF24/FAQ b/libraries/RF24/FAQ new file mode 100644 index 0000000..eccd03a --- /dev/null +++ b/libraries/RF24/FAQ @@ -0,0 +1,53 @@ +/** + * @page FAQ Frequently Asked Questions + * + * @ref starting + * + * @ref hardware + * + * @ref range + * + * @ref issues + * + * @ref ram + * + * @ref tests + * + * @section starting Where do I start? + * + * See my blog post: + * Getting Started with nRF24L01+ on Arduino + * + * @section hardware Where can I buy some hardware? + * + * @li iTeadStudio sells the basic 2.4G Wireless nRF24L01+ Module for $4. Such a deal! + * @li MDfly.com sells the same unit, 2.4Ghz Wireless nRF24L01+ Transceiver Module for $6.95, but it ships from the US so it gets there a lot faster. Great place to get a few units and get started quickly. + * @li MDfly.com also has the nRF24L01 2.4GHz Transceiver Module w/ Power Amplifier for $13.95, which increases range dramatically and uses a chip antenna + * @li MDfly.com also has the 2.4GHz Transceiver Module w/ Power Amplifier with an external antenna for $19.95 + * + * @section range What is the range of these units? + * + * Here are some results from measurements I have taken, using the basic $4 iTeadStudio units. + * I recommend that everyone take their own measurements in their particular circumstances. + * + * @li non-plus unit, 2MBps (worst case), 41+ ft line of sight indoors, immediate dropoff with any deviation from LOS. (41 ft is as far as I can go in my house without turning a corner) + * @li Plus unit, 250kbps (best case), 46 ft around two corners indoors, 49 ft around one corner. More importantly, at 250k, packet loss is almost negligible through almost all of that range. + * @li Both units at 1MBps, plus unit gets about 10% range improvement over non-plus in almost all situations. + * + * @section issues What should I do if I find a problem? + * + * Please open an issue on github if you find any problems using it with any version of Arduino or Maple. + * + * @section ram What is the RAM footprint of this library? + * + * 16 bytes. A single radio object consumes 16 bytes of RAM, and the library + * does not use any other RAM statically. + * + * @section tests Why are the examples in the 'tests' directory failing? + * + * The sketches in the 'tests' directory are not for general use. + * Please use the examples in the 'examples' directory instead. + * + * The 'tests' directory is only for people making changes to the library + * to ensure that their changes do not break anything. + */ diff --git a/libraries/RF24/README.md b/libraries/RF24/README.md new file mode 100644 index 0000000..d0f2c0e --- /dev/null +++ b/libraries/RF24/README.md @@ -0,0 +1,41 @@ +# Optimized High Speed Arduino driver for nRF24L01 2.4GHz Wireless Transceiver + +Design Goals: This library is designed to be... + +* More compliant with the manufacturer specified operation of the chip, while allowing advanced users +* to work outside the reccommended operation. +* Utilize the capabilities of the radio to their full potential via Arduino +* More reliable and feature rich +* Easy for beginners to use, with well documented examples and features +* Consumed with a public interface that's similiar to other Arduino standard libraries +* Built against the standard SPI library. + +April 2014: Official Release: Still some work to do, but most benefits have been realized + +* The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds +* Changes to read() and available () functionality have increased reliability and response +* Extended timeout periods have been added to aid in noisy or otherwise unreliable environments +* Delays have been removed where possible to ensure maximum efficiency +* Full Due support with extended SPI functions +* ATTiny 24/44/84 25/45/85 now supported. +* More! See the links below and class documentation for more info. + +Please refer to: + +* [Documentation Main Page](http://tmrh20.github.io/) +* [Class Reference]( http://tmrh20.github.io/RF24/classRF24.html) +* [Source Code](https://github.com/tmrh20/RF24) +* [Downloads](https://github.com/TMRh20/RF24/archive/master.zip) +* [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf) + +This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or +the SPI hardware will go into 'slave' mode. + +Supported Boards: + +* Uno, Nano, etc (328 based boards) +* Mega Types (2560, 1280, etc) +* ARM (Arduino Due) via extended SPI methods +* ATTiny 24/44/84 25/45/85 +* Raspberry Pi +* See the [documentation](http://tmrh20.github.io/) for more info \ No newline at end of file diff --git a/libraries/RF24/RF24.cpp b/libraries/RF24/RF24.cpp new file mode 100644 index 0000000..ebfbf81 --- /dev/null +++ b/libraries/RF24/RF24.cpp @@ -0,0 +1,1220 @@ +/* + Copyright (C) 2011 J. Coliz + + 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 "nRF24L01.h" +#include "RF24_config.h" +#include "RF24.h" + +/****************************************************************************/ + +void RF24::csn(bool mode) +{ + // Minimum ideal SPI bus speed is 2x data rate + // If we assume 2Mbs data rate and 16Mhz clock, a + // divider of 4 is the minimum we want. + // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz +#ifdef ARDUINO + #if !defined( __AVR_ATtiny85__ ) && !defined( __AVR_ATtiny84__) && !defined (__arm__) + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV2); + #endif +#endif + +#ifndef __arm__ + digitalWrite(csn_pin,mode); +#endif + +} + +/****************************************************************************/ + +void RF24::ce(bool level) +{ + digitalWrite(ce_pin,level); +} + +/****************************************************************************/ + +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +{ + uint8_t status; + +#if defined (__arm__) + status = SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); + while ( len-- > 1 ){ + *buf++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); + } + *buf++ = SPI.transfer(csn_pin,0xff); + +#else + csn(LOW); + status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ){ + *buf++ = SPI.transfer(0xff); + } + csn(HIGH); + +#endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::read_register(uint8_t reg) +{ + + #if defined (__arm__) + SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ) , SPI_CONTINUE); + uint8_t result = SPI.transfer(csn_pin,0xff); + #else + csn(LOW); + SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + uint8_t result = SPI.transfer(0xff); + + csn(HIGH); + #endif + + return result; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + uint8_t status; + + #if defined (__arm__) + status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); + while ( len-- > 1){ + SPI.transfer(csn_pin,*buf++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*buf++); + #else + + csn(LOW); + status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + SPI.transfer(*buf++); + + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, uint8_t value) +{ + uint8_t status; + + IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); + + #if defined (__arm__) + status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE); + SPI.transfer(csn_pin,value); + #else + + csn(LOW); + status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + SPI.transfer(value); + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::write_payload(const void* buf, uint8_t data_len, const uint8_t writeType) +{ + uint8_t status; + const uint8_t* current = reinterpret_cast(buf); + + if(data_len > payload_size) data_len = payload_size; + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + //printf("[Writing %u bytes %u blanks]",data_len,blank_len); + + #if defined (__arm__) + + status = SPI.transfer(csn_pin, writeType , SPI_CONTINUE); + + if(blank_len){ + while ( data_len--){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + while ( --blank_len ){ + SPI.transfer(csn_pin,0, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0); + }else{ + while( --data_len ){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*current); + } + + #else + + csn(LOW); + status = SPI.transfer( writeType ); + while ( data_len-- ) { + SPI.transfer(*current++); + } + while ( blank_len-- ) { + SPI.transfer(0); + } + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::read_payload(void* buf, uint8_t data_len) +{ + uint8_t status; + uint8_t* current = reinterpret_cast(buf); + + if(data_len > payload_size) data_len = payload_size; + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + //printf("[Reading %u bytes %u blanks]",data_len,blank_len); + + + #if defined (__arm__) + + status = SPI.transfer(csn_pin, R_RX_PAYLOAD, SPI_CONTINUE ); + + if( blank_len ){ + + while ( data_len-- ){ + *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + while ( --blank_len ){ + SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0xFF); + }else{ + while ( --data_len ){ + *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + *current = SPI.transfer(csn_pin,0xFF); + } + + #else + + csn(LOW); + status = SPI.transfer( R_RX_PAYLOAD ); + while ( data_len-- ) { + *current++ = SPI.transfer(0xFF); + } + while ( blank_len-- ) { + SPI.transfer(0xff); + } + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_rx(void) +{ + return spiTrans( FLUSH_RX ); +} + +/****************************************************************************/ + +uint8_t RF24::flush_tx(void) +{ + return spiTrans( FLUSH_TX ); +} + +/****************************************************************************/ + +uint8_t RF24::spiTrans(uint8_t cmd){ + + uint8_t status; + #if defined (__arm__) + status = SPI.transfer(csn_pin, cmd ); + #else + + csn(LOW); + status = SPI.transfer( cmd ); + csn(HIGH); + #endif + return status; + +} + +/****************************************************************************/ + +uint8_t RF24::get_status(void) +{ + return spiTrans(NOP); +} + +/****************************************************************************/ + +void RF24::print_status(uint8_t status) +{ + printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), + status, + (status & _BV(RX_DR))?1:0, + (status & _BV(TX_DS))?1:0, + (status & _BV(MAX_RT))?1:0, + ((status >> RX_P_NO) & B111), + (status & _BV(TX_FULL))?1:0 + ); +} + +/****************************************************************************/ + +void RF24::print_observe_tx(uint8_t value) +{ + printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), + value, + (value >> PLOS_CNT) & B1111, + (value >> ARC_CNT) & B1111 + ); +} + +/****************************************************************************/ + +void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen_P(name) < 8 ? '\t' : 0; + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); + while (qty--) + printf_P(PSTR(" 0x%02x"),read_register(reg++)); + printf_P(PSTR("\r\n")); +} + +/****************************************************************************/ + +void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen_P(name) < 8 ? '\t' : 0; + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); + + while (qty--) + { + uint8_t buffer[5]; + read_register(reg++,buffer,sizeof buffer); + + printf_P(PSTR(" 0x")); + uint8_t* bufptr = buffer + sizeof buffer; + while( --bufptr >= buffer ) + printf_P(PSTR("%02x"),*bufptr); + } + + printf_P(PSTR("\r\n")); +} + +/****************************************************************************/ + +RF24::RF24(uint8_t _cepin, uint8_t _cspin): + ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false), + payload_size(32), dynamic_payloads_enabled(false), + pipe0_reading_address(0) +{ +} + +/****************************************************************************/ + +void RF24::setChannel(uint8_t channel) +{ + // TODO: This method could take advantage of the 'wide_band' calculation + // done in setChannel() to require certain channel spacing. + + const uint8_t max_channel = 127; + write_register(RF_CH,min(channel,max_channel)); +} + +/****************************************************************************/ + +void RF24::setPayloadSize(uint8_t size) +{ + payload_size = min(size,32); +} + +/****************************************************************************/ + +uint8_t RF24::getPayloadSize(void) +{ + return payload_size; +} + +/****************************************************************************/ + +#if !defined (MINIMAL) + +static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; +static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; +static const char rf24_datarate_e_str_2[] PROGMEM = "250KBPS"; +static const char * const rf24_datarate_e_str_P[] PROGMEM = { + rf24_datarate_e_str_0, + rf24_datarate_e_str_1, + rf24_datarate_e_str_2, +}; +static const char rf24_model_e_str_0[] PROGMEM = "nRF24L01"; +static const char rf24_model_e_str_1[] PROGMEM = "nRF24L01+"; +static const char * const rf24_model_e_str_P[] PROGMEM = { + rf24_model_e_str_0, + rf24_model_e_str_1, +}; +static const char rf24_crclength_e_str_0[] PROGMEM = "Disabled"; +static const char rf24_crclength_e_str_1[] PROGMEM = "8 bits"; +static const char rf24_crclength_e_str_2[] PROGMEM = "16 bits" ; +static const char * const rf24_crclength_e_str_P[] PROGMEM = { + rf24_crclength_e_str_0, + rf24_crclength_e_str_1, + rf24_crclength_e_str_2, +}; +static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] PROGMEM = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_MAX"; +static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { + rf24_pa_dbm_e_str_0, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + +void RF24::printDetails(void) +{ + print_status(get_status()); + + print_address_register(PSTR("RX_ADDR_P0-1"),RX_ADDR_P0,2); + print_byte_register(PSTR("RX_ADDR_P2-5"),RX_ADDR_P2,4); + print_address_register(PSTR("TX_ADDR"),TX_ADDR); + + print_byte_register(PSTR("RX_PW_P0-6"),RX_PW_P0,6); + print_byte_register(PSTR("EN_AA"),EN_AA); + print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); + print_byte_register(PSTR("RF_CH"),RF_CH); + print_byte_register(PSTR("RF_SETUP"),RF_SETUP); + print_byte_register(PSTR("CONFIG"),CONFIG); + print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); + +#if defined(__arm__) + printf_P(PSTR("Data Rate\t = %s\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); + printf_P(PSTR("Model\t\t = %s\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); + printf_P(PSTR("CRC Length\t = %s\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); + printf_P(PSTR("PA Power\t = %s\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); +#else + printf_P(PSTR("Data Rate\t = %S\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); + printf_P(PSTR("Model\t\t = %S\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); + printf_P(PSTR("CRC Length\t = %S\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); + printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); +#endif +} + +#endif +/****************************************************************************/ + +void RF24::begin(void) +{ + // Initialize pins + pinMode(ce_pin,OUTPUT); + + #if defined(__arm__) + SPI.begin(csn_pin); // Using the extended SPI features of the DUE + SPI.setClockDivider(csn_pin, 9); // Set the bus speed to 8.4mhz on Due + SPI.setBitOrder(csn_pin,MSBFIRST); // Set the bit order and mode specific to this device + SPI.setDataMode(csn_pin,SPI_MODE0); + ce(LOW); + //csn(HIGH); + #else + pinMode(csn_pin,OUTPUT); + SPI.begin(); + ce(LOW); + csn(HIGH); + #endif + + // Must allow the radio time to settle else configuration bits will not necessarily stick. + // This is actually only required following power up but some settling time also appears to + // be required after resets too. For full coverage, we'll always assume the worst. + // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped. + // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure. + // WARNING: Delay is based on P-variant whereby non-P *may* require different timing. + delay( 5 ) ; + + // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier + // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet + // sizes must never be used. See documentation for a more complete explanation. + setRetries(5,15); + + // Reset value is MAX + //setPALevel( RF24_PA_MAX ) ; + + // Determine if this is a p or non-p RF24 module and then + // reset our data rate back to default value. This works + // because a non-P variant won't allow the data rate to + // be set to 250Kbps. + if( setDataRate( RF24_250KBPS ) ) + { + p_variant = true ; + } + + // Then set the data rate to the slowest (and most reliable) speed supported by all + // hardware. + setDataRate( RF24_1MBPS ) ; + + // Initialize CRC and request 2-byte (16bit) CRC + setCRCLength( RF24_CRC_16 ) ; + + // Disable dynamic payloads, to match dynamic_payloads_enabled setting - Reset value is 0 + //write_register(DYNPD,0); + + // Reset current status + // Notice reset and flush is the last thing we do + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Set up default configuration. Callers can always change it later. + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); + + // Flush buffers + flush_rx(); + flush_tx(); + + powerUp(); //Power up by default when begin() is called + + // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + // PTX should use only 22uA of power + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + + +} + +/****************************************************************************/ + +void RF24::startListening(void) +{ + powerUp(); + write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address){ + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + } + + // Flush buffers + flush_rx(); + flush_tx(); + + // Go! + ce(HIGH); + +} + +/****************************************************************************/ + +void RF24::stopListening(void) +{ + ce(LOW); + flush_tx(); + flush_rx(); + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + +} + +/****************************************************************************/ + +void RF24::powerDown(void) +{ + ce(LOW); // Guarantee CE is low on powerDown + write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); +} + +/****************************************************************************/ + +//Power up now. Radio will not power down unless instructed by MCU for config changes etc. +void RF24::powerUp(void) +{ + uint8_t cfg = read_register(CONFIG); + + // if not powered up then power up and wait for the radio to initialize + if (!(cfg & _BV(PWR_UP))){ + write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); + + // For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode. + // There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode before + // the CEis set high. - Tpd2stby can be up to 5ms per the 1.0 datasheet + delay(5); + + } +} + +/******************************************************************/ + +//Similar to the previous write, clears the interrupt flags +bool RF24::write( const void* buf, uint8_t len, const bool multicast ) +{ + //Start Writing + startWrite(buf,len,multicast); + + //Wait until complete or failed + //ACK payloads that are handled improperly will cause this to hang + //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + //Max retries exceeded + if( status & _BV(MAX_RT)){ + flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush + return 0; + } + //TX OK 1 or 0 + return 1; +} + +bool RF24::write( const void* buf, uint8_t len ){ + return write(buf,len,0); +} +/****************************************************************************/ + +//For general use, the interrupt flags are not important to clear +bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //This way the FIFO will fill up and allow blocking until packets go through + //The radio will auto-clear everything in the FIFO as long as CE remains high + + uint32_t timer = millis(); //Get the time that the payload transmission started + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout + + if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached + reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag + if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 + } + + } + + //Start Writing + startFastWrite(buf,len,0); //Write the payload if a buffer is clear + + return 1; //Return 1 to indicate successful transmission +} + +/****************************************************************************/ + +void RF24::reUseTX(){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + spiTrans( REUSE_TX_PL ); + ce(LOW); //Re-Transfer packet + ce(HIGH); +} + +/****************************************************************************/ + +bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //Return 0 so the user can control the retrys and set a timer or failure counter if required + //The radio will auto-clear everything in the FIFO as long as CE remains high + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail + + if( get_status() & _BV(MAX_RT)){ + //reUseTX(); //Set re-transmit + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + return 0; //Return 0. The previous payload has been retransmitted + //From the user perspective, if you get a 0, just keep trying to send the same payload + } + + } + //Start Writing + startFastWrite(buf,len,multicast); + + return 1; +} + +bool RF24::writeFast( const void* buf, uint8_t len ){ + return writeFast(buf,len,0); +} + +/****************************************************************************/ + +//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high +//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) +//Otherwise we enter Standby-II mode, which is still faster than standby mode +//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data + +void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 + + //write_payload( buf,len); + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + ce(HIGH); + +} + + +//Added the original startWrite back in so users can still use interrupts, ack payloads, etc +//Allows the library to pass all tests +void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){ + + // Send the payload + + //write_payload( buf, len ); + write_payload( buf, len,multicast? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + + ce(HIGH); + ce(LOW); + + +} + +bool RF24::txStandBy(){ + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + ce(LOW); + flush_tx(); //Non blocking, flush the data + return 0; + } + } + + ce(LOW); //Set STANDBY-I mode + return 1; +} + +bool RF24::txStandBy(uint32_t timeout){ + + uint32_t start = millis(); + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + ce(LOW); //Set re-transmit + ce(HIGH); + if(millis() - start >= timeout){ + ce(LOW); flush_tx(); return 0; + } + } + } + ce(LOW); //Set STANDBY-I mode + return 1; + +} +/****************************************************************************/ + +void RF24::maskIRQ(bool tx, bool fail, bool rx){ + + write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); +} + +/****************************************************************************/ + +uint8_t RF24::getDynamicPayloadSize(void) +{ + uint8_t result = 0; + + #if defined (__arm__) + SPI.transfer(csn_pin, R_RX_PL_WID, SPI_CONTINUE ); + result = SPI.transfer(csn_pin,0xff); + #else + csn(LOW); + SPI.transfer( R_RX_PL_WID ); + result = SPI.transfer(0xff); + csn(HIGH); + + #endif + + if(result > 32) { flush_rx(); return 0; } + return result; +} + +/****************************************************************************/ + +bool RF24::available(void) +{ + return available(NULL); +} + +/****************************************************************************/ + +bool RF24::available(uint8_t* pipe_num) +{ + + //Check the FIFO buffer to see if data is waitng to be read + + if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ + + // If the caller wants the pipe number, include that + if ( pipe_num ){ + uint8_t status = get_status(); + *pipe_num = ( status >> RX_P_NO ) & B111; + } + return 1; + } + + return 0; + + + +} + +/****************************************************************************/ + +void RF24::read( void* buf, uint8_t len ){ + + // Fetch the payload + read_payload( buf, len ); + + //Clear the two possible interrupt flags with one command + write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); + +} + +/****************************************************************************/ + +void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) +{ + // Read the status & reset the status in one easy call + // Or is that such a good idea? + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Report to the user what happened + tx_ok = status & _BV(TX_DS); + tx_fail = status & _BV(MAX_RT); + rx_ready = status & _BV(RX_DR); +} + +/****************************************************************************/ + +void RF24::openWritingPipe(uint64_t value) +{ + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) + // expects it LSB first too, so we're good. + + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); + write_register(TX_ADDR, reinterpret_cast(&value), 5); + + //const uint8_t max_payload_size = 32; + //write_register(RX_PW_P0,min(payload_size,max_payload_size)); + write_register(RX_PW_P0,payload_size); +} + +/****************************************************************************/ + +static const uint8_t child_pipe[] PROGMEM = +{ + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 +}; +static const uint8_t child_payload_size[] PROGMEM = +{ + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 +}; +static const uint8_t child_pipe_enable[] PROGMEM = +{ + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 +}; + +void RF24::openReadingPipe(uint8_t child, uint64_t address) +{ + // If this is pipe 0, cache the address. This is needed because + // openWritingPipe() will overwrite the pipe 0 address, so + // startListening() will have to restore it. + if (child == 0) + pipe0_reading_address = address; + + if (child <= 6) + { + // For pipes 2-5, only write the LSB + if ( child < 2 ) + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); + else + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); + + write_register(pgm_read_byte(&child_payload_size[child]),payload_size); + + // Note it would be more efficient to set all of the bits for all open + // pipes at once. However, I thought it would make the calling code + // more simple to do it this way. + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); + } +} + +/****************************************************************************/ + +void RF24::toggle_features(void) +{ + + #if defined (__arm__) + SPI.transfer(csn_pin, ACTIVATE, SPI_CONTINUE ); + SPI.transfer(csn_pin, 0x73 ); + #else + csn(LOW); + SPI.transfer( ACTIVATE ); + SPI.transfer( 0x73 ); + csn(HIGH); + #endif +} + +/****************************************************************************/ + +void RF24::enableDynamicPayloads(void) +{ + // Enable dynamic payload throughout the system + + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); + + + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + + // Enable dynamic payload on all pipes + // + // Not sure the use case of only having dynamic payload on certain + // pipes, so the library does not support it. + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + + dynamic_payloads_enabled = true; +} + +/****************************************************************************/ + +void RF24::enableAckPayload(void) +{ + // + // enable ack payload and dynamic payload features + // + + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + + // + // Enable dynamic payload on pipes 0 & 1 + // + + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/****************************************************************************/ + +void RF24::enableDynamicAck(void){ + // + // enable dynamic ack features + // + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); + + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + + +} + +/****************************************************************************/ + +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,32); + + #if defined (__arm__) + SPI.transfer(csn_pin, W_ACK_PAYLOAD | ( pipe & B111 ), SPI_CONTINUE); + while ( data_len-- > 1 ){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*current++); + + #else + csn(LOW); + SPI.transfer(W_ACK_PAYLOAD | ( pipe & B111 ) ); + + while ( data_len-- ) + SPI.transfer(*current++); + + csn(HIGH); + + #endif +} + +/****************************************************************************/ + +bool RF24::isAckPayloadAvailable(void) +{ + return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); +} + +/****************************************************************************/ + +bool RF24::isPVariant(void) +{ + return p_variant ; +} + +/****************************************************************************/ + +void RF24::setAutoAck(bool enable) +{ + if ( enable ) + write_register(EN_AA, B111111); + else + write_register(EN_AA, 0); +} + +/****************************************************************************/ + +void RF24::setAutoAck( uint8_t pipe, bool enable ) +{ + if ( pipe <= 6 ) + { + uint8_t en_aa = read_register( EN_AA ) ; + if( enable ) + { + en_aa |= _BV(pipe) ; + } + else + { + en_aa &= ~_BV(pipe) ; + } + write_register( EN_AA, en_aa ) ; + } +} + +/****************************************************************************/ + +bool RF24::testCarrier(void) +{ + return ( read_register(CD) & 1 ); +} + +/****************************************************************************/ + +bool RF24::testRPD(void) +{ + return ( read_register(RPD) & 1 ) ; +} + +/****************************************************************************/ + +void RF24::setPALevel(uint8_t level) +{ + + uint8_t setup = read_register(RF_SETUP) & 0b11111000; + + if(level > 3){ // If invalid level, go to max PA + level = RF24_PA_MAX << 1 + 1; // +1 to support the SI24R1 chip extra bit + }else{ + level = (level << 1) + 1; // Else set level as requested + } + + + write_register( RF_SETUP, setup |= level ) ; // Write it to the chip +} + +/****************************************************************************/ + +uint8_t RF24::getPALevel(void) +{ + + return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; +} + +/****************************************************************************/ + +bool RF24::setDataRate(rf24_datarate_e speed) +{ + bool result = false; + uint8_t setup = read_register(RF_SETUP) ; + + // HIGH and LOW '00' is 1Mbs - our default + wide_band = false ; + setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; + if( speed == RF24_250KBPS ) + { + // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 + // Making it '10'. + wide_band = false ; + setup |= _BV( RF_DR_LOW ) ; + } + else + { + // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 + // Making it '01' + if ( speed == RF24_2MBPS ) + { + wide_band = true ; + setup |= _BV(RF_DR_HIGH); + } + else + { + // 1Mbs + wide_band = false ; + } + } + write_register(RF_SETUP,setup); + + // Verify our result + if ( read_register(RF_SETUP) == setup ) + { + result = true; + } + else + { + wide_band = false; + } + + return result; +} + +/****************************************************************************/ + +rf24_datarate_e RF24::getDataRate( void ) +{ + rf24_datarate_e result ; + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + // switch uses RAM (evil!) + // Order matters in our case below + if ( dr == _BV(RF_DR_LOW) ) + { + // '10' = 250KBPS + result = RF24_250KBPS ; + } + else if ( dr == _BV(RF_DR_HIGH) ) + { + // '01' = 2MBPS + result = RF24_2MBPS ; + } + else + { + // '00' = 1MBPS + result = RF24_1MBPS ; + } + return result ; +} + +/****************************************************************************/ + +void RF24::setCRCLength(rf24_crclength_e length) +{ + uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; + + // switch uses RAM (evil!) + if ( length == RF24_CRC_DISABLED ) + { + // Do nothing, we turned it off above. + } + else if ( length == RF24_CRC_8 ) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV( CRCO ); + } + write_register( CONFIG, config ) ; +} + +/****************************************************************************/ + +rf24_crclength_e RF24::getCRCLength(void) +{ + rf24_crclength_e result = RF24_CRC_DISABLED; + uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; + + if ( config & _BV(EN_CRC ) ) + { + if ( config & _BV(CRCO) ) + result = RF24_CRC_16; + else + result = RF24_CRC_8; + } + + return result; +} + +/****************************************************************************/ + +void RF24::disableCRC( void ) +{ + uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; + write_register( CONFIG, disable ) ; +} + +/****************************************************************************/ +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR,(delay&0xf)< + + 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. + */ + +/** + * @file RF24.h + * + * Class declaration for RF24 and helper enums + */ + +#ifndef __RF24_H__ +#define __RF24_H__ + +#include + +/** + * Power Amplifier level. + * + * For use with setPALevel() + */ +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; + +/** + * Data rate. How fast data moves through the air. + * + * For use with setDataRate() + */ +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +/** + * CRC Length. How big (if any) of a CRC is included. + * + * For use with setCRCLength() + */ +typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; + +/** + * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + */ + +class RF24 +{ +private: + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + bool wide_band; /* 2Mbs data rate in use? */ + bool p_variant; /* False for RF24L01 and true for RF24L01P */ + uint8_t payload_size; /**< Fixed size of payloads */ + bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + +public: + + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ + + /** + * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(uint8_t _cepin, uint8_t _cspin); + + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + * + * @note Optimization: The radio is partially configured in PTX mode + * when begin is called to make the transition to PTX mode simpler. + */ + void begin(void); + + /** + * Start listening on the pipes opened for reading. + * + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. + */ + void startListening(void); + + /** + * Stop listening for incoming messages + * + * Do this before calling write(). + * + * @note Optimization: The radio will be taken out of PRX mode as soon + * as listening is stopped. Enables quicker and simpler engaging of + * primary transmitter (PTX) mode. + */ + void stopListening(void); + + /** + * Test whether there are bytes available to be read + * + * @note Optimization: The available functino now checks the FIFO + * buffers directly for data instead of relying of interrupt flags. + * + * @note Interrupt flags will not be cleared until a payload is + * actually read from the FIFO + * + * @see txStandBy() + * @see startWrite() + * @see write() + * @see writeFast() + * + * @return True if there is a payload available, false if none is + */ + bool available(void); + + /** + * Read the payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @note Optimization: No longer boolean. Use available to + * determine if packets are available. Interrupt flags are now cleared + * during reads instead of when calling available(). + * + * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer + * @return No return value. Use available(). + */ + void read( void* buf, uint8_t len ); + + /** + * @note Optimization: Improved performance slightly + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * TX/RX/RT interrupt flags will be cleared every time write is called + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool write( const void* buf, uint8_t len ); + + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param address The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); + + /** + * Open a pipe for reading + * + * Up to 6 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning Pipes 1-5 should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * @code + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); + * @endcode + * + * @warning Pipe 0 is also used by the writing pipe. So if you open + * pipe 0 for reading, and then startListening(), it will overwrite the + * writing pipe. Ergo, do an openWritingPipe() again before write(). + * + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits + * + * @param number Which pipe# to open, 0-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); + + /**@}*/ + /** + * @name Advanced Operation + * + * Methods you can use to drive the chip in more advanced ways + */ + /**@{*/ + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); + + /** + * Test whether there are bytes available to be read in the + * FIFO buffers. This optimized version does not rely on interrupt + * flags, but checks the actual FIFO buffers. + * + * @note Optimization: Interrupt flags are no longer cleared when available is called, + * but will be reset only when the data is read from the FIFO buffers. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + bool available(uint8_t* pipe_num); + + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening, or powerUp(). + * + * @note Optimization: The radio will never enter power down unless instructed + * by the MCU via this command. + */ + void powerDown(void); + + /** + * Leave low-power mode - making radio more responsive + * + * To return to low power mode, call powerDown(). + */ + void powerUp(void) ; + + /** + * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * + * Can be used with enableAckPayload() to request a response + * @see enableDynamicAck() + * @see setAutoAck() + * @see write() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0), NOACK (1) + */ + bool write( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command * + * This will not block until the 3 FIFO buffers are filled with data. + * Once the FIFOs are full, writeFast will simply wait for success or + * timeout, and return 1 or 0 respectively. From a user perspective, just + * keep trying to send the same data. The library will keep auto retrying + * the current payload using the built in functionality. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeFast is called + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(1000); // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0. + * @endcode + * + * @see txStandBy() + * @see write() + * @see writeBlocking() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool writeFast( const void* buf, uint8_t len ); + + /** + * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + */ + bool writeFast( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command + * This function extends the auto-retry mechanism to any specified duration. + * It will not block until the 3 FIFO buffers are filled with data. + * If so the library will auto retry until a new payload is written + * or the user specified timeout period is reached. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeBlocking is called + * @code + * Example (Full blocking): + * + * radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers + * txStandBy(1000); //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed. + * //Blocks only until user timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @see txStandBy() + * @see write() + * @see writeFast() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param timeout User defined timeout in milliseconds. + * @return True if the payload was loaded into the buffer successfully false if not + */ + bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); + + /** + * @note Optimization: New Command + * This function should be called as soon as transmission is finished to + * drop the radio back to STANDBY-I mode. If not issued, the radio will + * remain in STANDBY-II mode which, per the data sheet, is not a recommended + * operating mode. + * + * @note When transmitting data in rapid succession, it is still recommended by + * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is + * time enough between sends for the FIFOs to empty. + * + * Relies on built-in auto retry functionality. + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. + * //Blocks only until MAX_RT timeout or success. Data flushed on fail. + * @endcode + * @see txStandBy(unsigned long timeout) + * @return True if transmission is successful + * + */ + bool txStandBy(); + + /** + * @note Optimization: New Command + * + * This function allows extended blocking and auto-retries per a user defined timeout + * @code + * Fully Blocking Example: + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. + * //Blocks only until user defined timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @param timeout Number of milliseconds to retry failed payloads + * @return True if transmission is successful + * + */ + bool txStandBy(uint32_t timeout); + + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time as there are only 3 FIFO buffers. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + /** + * Enable dynamic ACKs (single write multicasting) for chosen messages + * + * @note To enable full multicasting or per-pipe multicast, use setAutoAck() + * + * @warning This MUST be called prior to attempting single write NOACK calls + * @code + * radio.enableDynamicAck(); + * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested + * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK + * @endcode + */ + void enableDynamicAck(); + + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @note Optimization: Calling this function NO LONGER clears the interrupt + * flag. The new functionality checks the RX FIFO buffer for an ACK payload + * instead of relying on interrupt flags.Reading the payload will clear the flags. + * + * @return True if an ack payload is available. + */ + bool isAckPayloadAvailable(void); + + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + + /** + * Non-blocking write to the open writing pipe used for buffered writes + * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * @see write() + * @see writeFast() + * @see startWrite() + * @see writeBlocking() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * @return True if the payload was delivered successfully false if not + */ + void startFastWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @note Optimization: This function again behaves as it did previously for backwards-compatibility. + * with user code. The library uses startFastWrite() internally. + * This is mainly used for single-payload transactions. + * + * @see write() + * @see writeFast() + * @see startFastWrite() + * @see whatHappened() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * + */ + void startWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Optimization: New Command + * + * This function is mainly used internally to take advantage of the auto payload + * re-use functionality of the chip, but can be beneficial to users as well. + * + * The function will instruct the radio to re-use the data in the FIFO buffers, + * and instructs the radio to re-send once the timeout limit has been reached. + * Used by writeFast and writeBlocking to initiate retries when a TX failure + * occurs. Retries are automatically initiated except with the standard write(). + * This way, data is not flushed from the buffer until switching between modes. + * + * @note This is to be used AFTER auto-retry fails if wanting to resend + * using the built-in payload reuse features. + * After issuing reUseTX(), it will keep reending the same payload forever or until + * a payload is written to the FIFO, or a flush_tx command is given. + */ + void reUseTX(); + + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); + + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + bool testCarrier(void); + + /** + * Test whether a signal (carrier or otherwise) greater than + * or equal to -64dBm is present on the channel. Valid only + * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * + * Useful to check for interference on the current channel and + * channel hopping strategies. + * + * @return true if signal => -64dBm, false if not + */ + bool testRPD(void) ; + + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + + /** + * The radio will generate interrupt signals when a transmission is complete, + * a transmission fails, or a payload is received. This allows users to mask + * those interrupts to prevent them from generating a signal on the interrupt + * pin. + * + * @code + * Mask all interrupts except the receive interrupt: + * + * radio.maskIRQ(1,1,0); + * @endcode + * + * @param tx_ok Mask transmission complete interrupts + * @param tx_fail Mask transmit failure interrupts + * @param rx_ready Mask payload received interrupts + */ + void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); + + /**@}*/ + + /**@}*/ + /** + * @name Optional Configurators + * + * Methods you can use to get or set the configuration of the chip. + * None are required. Calling begin() sets up a reasonable set of + * defaults. + */ + /**@{*/ + + /** + * Set the number and delay of retries upon failed submit + * + * @param delay How long to wait between each retry, in multiples of 250us, + * max is 15. 0 means 250us, 15 means 4000us. + * @param count How many retries before giving up, max 15 + */ + void setRetries(uint8_t delay, uint8_t count); + + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); + + /** + * Set Static Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); + + /** + * Get Static Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void); + + /** + * Get Dynamic Payload Size + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @note Optimization: Corrupt packets are now detected and flushed per the + * manufacturer. + * + * @return Payload length of last-received dynamic payload + */ + uint8_t getDynamicPayloadSize(void); + + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde + */ + void enableAckPayload(void); + + /** + * Enable dynamically-sized payloads + * + * This way you don't always have to send large packets just to send them + * once in a while. This enables dynamic payloads on ALL pipes. + * + * @see examples/pingpair_pl/pingpair_dyn.pde + */ + void enableDynamicPayloads(void); + + /** + * Determine whether the hardware is an nRF24L01+ or not. + * + * @return true if the hardware is nRF24L01+ (or compatible) and false + * if its not. + */ + bool isPVariant(void) ; + + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); + + /** + * Enable or disable auto-acknowlede packets on a per pipeline basis. + * + * AA is enabled by default, so it's only needed if you want to turn + * it off/on for some reason on a per pipeline basis. + * + * @param pipe Which pipeline to modify + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck( uint8_t pipe, bool enable ) ; + + /** + * Set Power Amplifier (PA) level to one of four levels: + * RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX + * + * The power levels correspond to the following output levels respectively: + * NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm + * + * SI24R1: -6dBm, 0dBm, 3dBM, and 7dBm. + * + * @param level Desired PA level. + */ + void setPALevel ( uint8_t level ); + + /** + * Fetches the current PA level. + * + * NRF24L01: -18dBm, -12dBm, -6dBm and 0dBm + * SI24R1: -6dBm, 0dBm, 3dBm, 7dBm + * + * @return Returns values 0 to 3 representing the PA Level. + */ + uint8_t getPALevel( void ); + + /** + * Set the transmission data rate + * + * @warning setting RF24_250KBPS will fail for non-plus units + * + * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps + * @return true if the change was successful + */ + bool setDataRate(rf24_datarate_e speed); + + /** + * Fetches the transmission data rate + * + * @return Returns the hardware's currently configured datarate. The value + * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the + * rf24_datarate_e enum. + */ + rf24_datarate_e getDataRate( void ) ; + + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); + + /** + * Get the CRC length + * + * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + rf24_crclength_e getCRCLength(void); + + /** + * Disable CRC validation + * + */ + void disableCRC( void ) ; + +private: + + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. + */ + /**@{*/ + + /** + * Set chip select pin + * + * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data + * and best of all, we make use of the radio's FIFO buffers. A lower speed + * means we're less likely to effectively leverage our FIFOs and pay a higher + * AVR runtime cost as toll. + * + * @param mode HIGH to take this unit off the SPI bus, LOW to put it on + */ + void csn(bool mode); + + /** + * Set chip enable + * + * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet + * for a much more detailed description of this pin. + */ + void ce(bool level); + + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); + + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); + + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); + + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @param len Number of bytes to be sent + * @return Current value of status register + */ + uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); + + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register + */ + uint8_t read_payload(void* buf, uint8_t len); + + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); + + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void); + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status); + + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value); + + /** + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); + + /** + * Built in spi transfer function to simplify repeating code repeating code + */ + + uint8_t spiTrans(uint8_t cmd); + /**@}*/ + +}; + +/** + * @example GettingStarted.ino + * Updated: TMRh20 2014
+ * + * This is an example of how to use the RF24 class to communicate on a basic level. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting with the serial monitor and
+ * sending a 'T'. The ping node sends the current time to the pong node, which responds by sending the value + * back. The ping node can then see how long the whole cycle took.
+ * @note For a more efficient call-response scenario see the GettingStarted_CallResponse.ino example. + * @note When switching between sketches, the radio may need to be powered down to clear settings that are not "un-set" otherwise + */ + +/** + * @example GettingStarted_CallResponse.ino + * New: TMRh20 2014
+ * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to
+ * primary transmitter if wanting to initiate communication instead of respond to a commmunication. + */ + +/** + * @example Transfer.ino + * New: TMRh20
+ * This example demonstrates half-rate transfer using the FIFO buffers
+ * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting
+ * with the serial monitor and sending a 'T'. The data transfer will begin, + * with the receiver displaying the payload count. (32Byte Payloads)
+ */ + +/** + * @example TransferTimeouts.ino + * New: TMRh20
+ * This example demonstrates the use of and extended timeout period and + * auto-retries/auto-reUse to increase reliability in noisy or low signal scenarios.
+ * + * Write this sketch to two different nodes. Put one of the nodes into 'transmit' + * mode by connecting with the serial monitor and sending a 'T'. The data
+ * transfer will begin, with the receiver displaying the payload count and the + * data transfer rate. + */ + +/** + * @example starping.pde + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +/** + * @example pingpair_ack.ino + * Update: TMRh20
+ * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to if wanting
+ * to initiate communication instead of respond to a commmunication. + */ + +/** + * @example pingpair_irq.ino + * Update: TMRh20
+ * This is an example of how to user interrupts to interact with the radio, and a demonstration + * of how to use them to sleep when receiving, and not miss any payloads.
+ * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter. + * Sleep functionality is built directly into my fork of the RF24Network library
+ */ + +/** + * @example pingpair_sleepy.ino + * Update: TMRh20
+ * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the GettingStarted_CallResponse example, but the
+ * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle, and the receiver sleeps between payloads.
+ */ + +/** + * @example pingpair_dyn.ino + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + +/** + * @example led_remote.pde + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + +/** + * @example scanner.ino + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +/** + * @mainpage Optimized High Speed Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + * + * @section Goals Design Goals + * + * This library fork is designed to be... + * @li More compliant with the manufacturer specified operation of the chip, while allowing advanced users + * to work outside the reccommended operation. + * @li Utilize the capabilities of the radio to their full potential via Arduino + * @li More reliable, responsive and feature rich + * @li Easy for beginners to use, with well documented examples and features + * @li Consumed with a public interface that's similiar to other Arduino standard libraries + * + * @section News News + * + * April 2014: Official Release: Still some work to do, but most benefits have been realized
+ * - The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds + * - Changes to read() and available () functionality have increased reliability and response + * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments + * - Delays have been removed where possible to ensure maximum efficiency + * - Full Due support with extended SPI functions + * - ATTiny 24/44/84 25/45/85 now supported. + * - More! See the links below and class documentation for more info. + * + * If issues are discovered with the documentation, please report them here: here + * @section Useful Useful References + * + * Please refer to: + * + * @li Documentation Main Page + * @li RF24 Class Documentation + * @li Source Code + * @li Downloads Page + * @li Chip Datasheet + * @li Original Library + * + * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or + * the SPI hardware will go into 'slave' mode. + * + * @section Board_Support Board Support + * + * Most standard Arduino based boards are supported: + * - ATMega 328 based boards (Uno, Nano, etc) + * - Mega Boards (1280, 2560, etc) + * - ARM based boards (Arduino Due) Note: Do not include printf.h or use printf begin. This functionality is already present. Must use one of the + * hardware SS/CSN pins as extended SPI methods are used. + * Initial Due support taken from https://github.com/mcrosson/RF24/tree/due + * - ATTiny board support added from https://github.com/jscrane/RF24 + * Note: ATTiny support is built into the library. Do not include SPI.h.
+ * ATTiny 85: D0(pin 5): MISO, D1(pin6) MOSI, D2(pin7) SCK, D3(pin2):CSN/SS, D4(pin3): CE
+ * ATTiny 84: PA6:MISO, PA5:MOSI, PA4:SCK, PA7:CSN/SS, CE as desired
+ * + * @section More More Information + * + * @section Info and Projects + * + * @li Project blog: + * @li TMRh20.blogspot.com + * @li RF24 Wireless Audio Library (Coming Soon) + * @li Optimized RF24 Network Layer + * @li ManiacBug on GitHub (Original Library Author) + */ + +#endif // __RF24_H__ + + diff --git a/libraries/RF24/RF24_config.h b/libraries/RF24/RF24_config.h new file mode 100644 index 0000000..b7154a5 --- /dev/null +++ b/libraries/RF24/RF24_config.h @@ -0,0 +1,129 @@ + +/* + Copyright (C) 2011 J. Coliz + + 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. + + Added Arduino Due support from https://github.com/mcrosson/ + */ + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ + + #if ARDUINO < 100 + #include + #else + #include + #endif + + #include + + //TMRh20: + //#define MINIMAL + + // Define _BV for non-Arduino platforms and for Arduino DUE +#if defined (ARDUINO) + #include +#else + #include + #include + #include + extern HardwareSPI SPI; + #define _BV(x) (1<<(x)) + #endif + + + #undef SERIAL_DEBUG + #ifdef SERIAL_DEBUG + #define IF_SERIAL_DEBUG(x) ({x;}) + #else + #define IF_SERIAL_DEBUG(x) + #if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) + #define printf_P(...) + #endif + #endif + +// Avoid spurious warnings +// Arduino DUE is arm and uses traditional PROGMEM constructs +#if 1 +#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) +#undef PROGMEM +#define PROGMEM __attribute__(( section(".progmem.data") )) +#undef PSTR +#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) +#endif +#endif + +// Progmem is Arduino-specific +// Arduino DUE is arm and does not include avr/pgmspace +#if defined(ARDUINO) && ! defined(__arm__) + #include + #define PRIPSTR "%S" +#else +#if ! defined(ARDUINO) // This doesn't work on Arduino DUE + typedef char const char; +#else // Fill in pgm_read_byte that is used, but missing from DUE + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif + + typedef uint16_t prog_uint16_t; + #define PSTR(x) (x) + #define printf_P printf + #define strlen_P strlen + #define PROGMEM + #define pgm_read_word(p) (*(p)) + #define PRIPSTR "%s" +#endif + + + +// ATTiny support code is from https://github.com/jscrane/RF24 + +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +#include +#include +#include + +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 +//#define SPI_CLOCK_DIV64 0x07 + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + + + +class SPIClass { +public: + static byte transfer(byte _data); + + // SPI Configuration methods + + inline static void attachInterrupt(); + inline static void detachInterrupt(); // Default + + static void begin(); // Default + static void end(); + + static void setBitOrder(uint8_t); + static void setDataMode(uint8_t); + static void setClockDivider(uint8_t); +}; +extern SPIClass SPI; + +#endif //ATTiny +#endif // __RF24_CONFIG_H__ + diff --git a/libraries/RF24/RPi/RF24/Makefile b/libraries/RF24/RPi/RF24/Makefile new file mode 100644 index 0000000..c2ae37d --- /dev/null +++ b/libraries/RF24/RPi/RF24/Makefile @@ -0,0 +1,55 @@ +############################################################################# +# +# Makefile for librf24-bcm on Raspberry Pi +# +# License: GPL (General Public License) +# Author: Charles-Henri Hallard +# Date: 2013/03/13 +# +# Description: +# ------------ +# use make all and mak install to install the library +# You can change the install directory by editing the LIBDIR line +# +PREFIX=/usr/local + +# Library parameters +# where to put the lib +LIBDIR=$(PREFIX)/lib +# lib name +LIB=librf24-bcm +# shared library name +LIBNAME=$(LIB).so.1.0 + + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s + +# make all +# reinstall the library after each recompilation +all: librf24-bcm install + +# Make the library +librf24-bcm: RF24.o bcm2835.o + g++ -shared -Wl,-soname,$@.so.1 ${CCFLAGS} -o ${LIBNAME} $^ + +# Library parts +RF24.o: RF24.cpp + g++ -Wall -fPIC ${CCFLAGS} -c $^ + +bcm2835.o: bcm2835.c + gcc -Wall -fPIC ${CCFLAGS} -c $^ + +# clear build files +clean: + rm -rf *.o ${LIB}.* + +# Install the library to LIBPATH +install: + @echo "[Install]" + @if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi + #@install -m 0755 ${LIB}.a ${LIBDIR} + @install -m 0755 ${LIBNAME} ${LIBDIR} + @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so.1 + @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so + @ldconfig diff --git a/libraries/RF24/RPi/RF24/RF24.cpp b/libraries/RF24/RPi/RF24/RF24.cpp new file mode 100644 index 0000000..c45afaf --- /dev/null +++ b/libraries/RF24/RPi/RF24/RF24.cpp @@ -0,0 +1,1145 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + + +03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 library + +TMRh20 2014: Updated to work with optimized RF24 and RF24 Network Arduino libs. +*/ + +#include "./RF24_config.h" +#include "./RF24.h" +#include "./nRF24L01.h" + + +/****************************************************************************/ +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size = len + 1; // Add register value to transmit buffer + + *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); + while (len--){ + *ptx++ = NOP ; // Dummy operation, just for reading + } + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + //bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) buf, size); + + status = *prx++; // status is 1st byte of receive buffer + + // decrement before to skip status byte + while ( --size ){ + *buf++ = *prx++; + } + + return status; +} + +/****************************************************************************/ +uint8_t RF24::read_register(uint8_t reg) +{ + uint8_t result; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + + *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); + *ptx++ = NOP ; // Dummy operation, just for reading + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + result = *++prx; // result is 2nd byte of receive buffer + + return result; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, uint8_t value) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + + *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); + *ptx = value ; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + status = *prx++; // status is 1st byte of receive buffer + + if (debug) + printf("write_register(%02x,%02x)\r\n",reg,value); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size = len + 1; // Add register value to transmit buffer + + *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + *ptx++ = *buf++; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + status = *prx; // status is 1st byte of receive buffer + + return status; +} + + +/****************************************************************************/ + +uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeType) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + size = data_len + blank_len + 1 ; // Add register value to transmit buffer + + if (debug) + printf("[Writing %u bytes %u blanks]",data_len,blank_len); + + *ptx++ = W_TX_PAYLOAD; + while ( data_len-- ) + *ptx++ = *current++; + while ( blank_len-- ) + *ptx++ = 0; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + status = *prx; // status is 1st byte of receive buffer + + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::read_payload(void* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + size = data_len + blank_len + 1; // Add register value to transmit buffer + + if (debug) + printf("[Reading %u bytes %u blanks]",data_len,blank_len); + + *ptx++ = R_RX_PAYLOAD; + while(size--) + *ptx++ = NOP; + + // Size has been lost during while, re affect + size = data_len + blank_len + 1; // Add register value to transmit buffer + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + // 1st byte is status + status = *prx++; + + // Decrement before to skip 1st status byte + while ( --size ) + *current++ = *prx++; + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_rx(void) +{ + uint8_t status; + + status = bcm2835_spi_transfer( FLUSH_RX ); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_tx(void) +{ + uint8_t status; + + status = bcm2835_spi_transfer( FLUSH_TX ); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::get_status(void) +{ + return bcm2835_spi_transfer( NOP ); +} + +/****************************************************************************/ + +void RF24::print_status(uint8_t status) +{ + printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n", + status, + (status & _BV(RX_DR))?1:0, + (status & _BV(TX_DS))?1:0, + (status & _BV(MAX_RT))?1:0, + ((status >> RX_P_NO) & 0b111), + (status & _BV(TX_FULL))?1:0 + ); +} + +/****************************************************************************/ + +void RF24::print_observe_tx(uint8_t value) +{ + printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n", + value, + (value >> PLOS_CNT) & 0b1111, + (value >> ARC_CNT) & 0b1111 + ); +} + +/****************************************************************************/ + +void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen(name) < 8 ? '\t' : 0; + printf("%s\t%c =", name, extra_tab); + while (qty--) + printf(" 0x%02x",read_register(reg++)); + printf("\n"); +} + +/****************************************************************************/ + +void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen(name) < 8 ? '\t' : 0; + printf("%s\t%c =",name,extra_tab); + + while (qty--) + { + uint8_t buffer[5]; + read_register(reg++,buffer,sizeof buffer); + + printf(" 0x"); + uint8_t* bufptr = buffer + sizeof buffer; + while( --bufptr >= buffer ) + printf("%02x",*bufptr); + } + + printf("\r\n"); +} + + +/****************************************************************************/ + +RF24::RF24(uint8_t _cepin, uint8_t _cspin, uint32_t _spi_speed): + ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), wide_band(true), p_variant(false), + payload_size(32), dynamic_payloads_enabled(false), + pipe0_reading_address(0) +{ +} + +/****************************************************************************/ + +void RF24::setChannel(uint8_t channel) +{ + // TODO: This method could take advantage of the 'wide_band' calculation + // done in setChannel() to require certain channel spacing. + + const uint8_t max_channel = 127; + write_register(RF_CH,min(channel,max_channel)); +} + +/****************************************************************************/ + +void RF24::setPayloadSize(uint8_t size) +{ + const uint8_t max_payload_size = 32; + payload_size = min(size,max_payload_size); +} + +/****************************************************************************/ + +uint8_t RF24::getPayloadSize(void) +{ + return payload_size; +} + +/****************************************************************************/ + +static const char rf24_datarate_e_str_0[] = "1MBPS"; +static const char rf24_datarate_e_str_1[] = "2MBPS"; +static const char rf24_datarate_e_str_2[] = "250KBPS"; +static const char * const rf24_datarate_e_str_P[] = { + rf24_datarate_e_str_0, + rf24_datarate_e_str_1, + rf24_datarate_e_str_2, +}; +static const char rf24_model_e_str_0[] = "nRF24L01"; +static const char rf24_model_e_str_1[] = "nRF24L01+"; +static const char * const rf24_model_e_str_P[] = { + rf24_model_e_str_0, + rf24_model_e_str_1, +}; +static const char rf24_crclength_e_str_0[] = "Disabled"; +static const char rf24_crclength_e_str_1[] = "8 bits"; +static const char rf24_crclength_e_str_2[] = "16 bits" ; +static const char * const rf24_crclength_e_str_P[] = { + rf24_crclength_e_str_0, + rf24_crclength_e_str_1, + rf24_crclength_e_str_2, +}; +static const char rf24_pa_dbm_e_str_0[] = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_3[] = "PA_MAX"; +static const char * const rf24_pa_dbm_e_str_P[] = { + rf24_pa_dbm_e_str_0, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + +static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)"; +static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)"; +static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)"; +static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven"; +static const char * const rf24_csn_e_str_P[] = { + rf24_csn_e_str_0, + rf24_csn_e_str_1, + rf24_csn_e_str_2, + rf24_csn_e_str_3, +}; + +// Display NRF24L01 details +void RF24::printDetails(void) +{ + printf("================ SPI Configuration ================\n" ); + + if (csn_pin < BCM2835_SPI_CS_NONE ) + { + printf("CSN Pin \t = %s\n",rf24_csn_e_str_P[csn_pin]); + } + else + { + printf("CSN Pin \t = Custom GPIO%d%s\n", csn_pin, + csn_pin==RPI_V2_GPIO_P1_26 ? " (CE1) Software Driven" : "" ); + } + + printf("CE Pin \t = Custom GPIO%d\n", ce_pin ); + + // SPI Bus Speed + printf("Clock Speed\t = " ); + switch (spi_speed) + { + case BCM2835_SPI_SPEED_64MHZ : printf("64 Mhz"); break ; + case BCM2835_SPI_SPEED_32MHZ : printf("32 Mhz"); break ; + case BCM2835_SPI_SPEED_16MHZ : printf("16 Mhz"); break ; + case BCM2835_SPI_SPEED_8MHZ : printf("8 Mhz"); break ; + case BCM2835_SPI_SPEED_4MHZ : printf("4 Mhz"); break ; + case BCM2835_SPI_SPEED_2MHZ : printf("2 Mhz"); break ; + case BCM2835_SPI_SPEED_1MHZ : printf("1 Mhz"); break ; + case BCM2835_SPI_SPEED_512KHZ: printf("512 KHz"); break ; + case BCM2835_SPI_SPEED_256KHZ: printf("256 KHz"); break ; + case BCM2835_SPI_SPEED_128KHZ: printf("128 KHz"); break ; + case BCM2835_SPI_SPEED_64KHZ : printf("64 KHz"); break ; + case BCM2835_SPI_SPEED_32KHZ : printf("32 KHz"); break ; + case BCM2835_SPI_SPEED_16KHZ : printf("16 KHz"); break ; + case BCM2835_SPI_SPEED_8KHZ : printf("8 KHz"); break ; + default : printf("Probably Bad !!!"); break ; + } + printf("\n"); + + printf("================ NRF Configuration ================\n" ); + + print_status(get_status()); + + print_address_register("RX_ADDR_P0-1",RX_ADDR_P0,2); + print_byte_register("RX_ADDR_P2-5",RX_ADDR_P2,4); + print_address_register("TX_ADDR",TX_ADDR); + + print_byte_register("RX_PW_P0-6",RX_PW_P0,6); + print_byte_register("EN_AA",EN_AA); + print_byte_register("EN_RXADDR",EN_RXADDR); + print_byte_register("RF_CH",RF_CH); + print_byte_register("RF_SETUP",RF_SETUP); + print_byte_register("CONFIG",CONFIG); + print_byte_register("DYNPD/FEATURE",DYNPD,2); + + printf("Data Rate\t = %s\r\n",rf24_datarate_e_str_P[getDataRate()]); + printf("Model\t\t = %s\r\n",rf24_model_e_str_P[isPVariant()]); + printf("CRC Length\t = %s\r\n",rf24_crclength_e_str_P[getCRCLength()]); + printf("PA Power\t = %s\r\n",rf24_pa_dbm_e_str_P[getPALevel()]); +} + +/****************************************************************************/ + +bool RF24::begin(void) +{ + debug = false; + //debug = true; + // This initialize the SPI bus with + // csn pin as chip select (custom or not) + + + // Init BCM2835 chipset for talking with us + if (!bcm2835_init()){ + return false; + } + + switch(csn_pin){ //Ensure valid hardware CS pin + case 0: break; + case 1: break; + case 8: csn_pin = 0; break; + case 7: csn_pin = 1; break; + default: csn_pin = 0; break; + } + + + bcm2835_spi_begin(); + + // used to drive custom I/O to trigger my logic analyser + // bcm2835_gpio_fsel(GPIO_CTRL_PIN , BCM2835_GPIO_FSEL_OUTP); + + // start the SPI library: + // Note the NRF24 wants mode 0, MSB first and default to 1 Mbps + bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); + bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); + + // Set SPI bus Speed + bcm2835_spi_setClockDivider(spi_speed); + + // Choose hardware CSN pin + bcm2835_spi_chipSelect(csn_pin); + + + // Initialise the CE pin of NRF24 (chip enable) after the CSN pin, so that + // The input mode is not changed if using one of the hardware CE pins + bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_write(ce_pin, LOW); + + // wait 100ms + delay(100); + + // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier + // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet + // sizes must never be used. See documentation for a more complete explanation. + //printf("write_register(%02X, %02X)\n", SETUP_RETR, (0b0100 << ARD) | (0b1111 << ARC)); + setRetries(5,15); + + // Determine if this is a p or non-p RF24 module and then + // reset our data rate back to default value. This works + // because a non-P variant won't allow the data rate to + // be set to 250Kbps. + if( setDataRate( RF24_250KBPS ) ) + { + p_variant = true ; + } + + // Then set the data rate to the slowest (and most reliable) speed supported by all + // hardware. + setDataRate( RF24_1MBPS ) ; + + // Initialize CRC and request 2-byte (16bit) CRC + setCRCLength( RF24_CRC_16 ) ; + + // Reset current status + // Notice reset and flush is the last thing we do + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Set up default configuration. Callers can always change it later. + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); + + // Flush buffers + flush_rx(); + flush_tx(); + + powerUp(); + + // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + // PTX should use only 22uA of power + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + + return true; +} + +/****************************************************************************/ + +void RF24::startListening(void) +{ + powerUp(); + write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address) + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + + // Flush buffers + flush_rx(); + flush_tx(); + + // Go! + bcm2835_gpio_write(ce_pin, HIGH); + + // wait for the radio to come up (130us actually only needed) + //delayMicroseconds(130); +} + +/****************************************************************************/ + +void RF24::stopListening(void) +{ + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); + flush_rx(); + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + +} + +/****************************************************************************/ + +void RF24::powerDown(void) +{ + bcm2835_gpio_write(ce_pin, LOW); + write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); +} + +/****************************************************************************/ + +void RF24::powerUp(void) +{ + bool up = read_register(CONFIG) & _BV(PWR_UP); + + if(! up ){ + write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) )); + delay(5); + } + + +} + +/******************************************************************/ + +bool RF24::write( const void* buf, uint8_t len, const bool multicast ){ + + // Begin the write + startFastWrite(buf,len, multicast); + + + //Wait until complete or failed + // If this hangs, it ain't coming back, no sense in timing out + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + + + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); + + //Max retries exceeded + if( status & _BV(MAX_RT)){ + flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush + return 0; + } + //TX OK 1 or 0 + return 1; +} + +bool RF24::write( const void* buf, uint8_t len ){ + return write(buf,len,0); +} + +/****************************************************************************/ + +//For general use, the interrupt flags are not important to clear +bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //This way the FIFO will fill up and allow blocking until packets go through + //The radio will auto-clear everything in the FIFO as long as CE remains high + + uint32_t timer = millis(); //Get the time that the payload transmission started + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout + + if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached + reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag + if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 + } + + } + + //Start Writing + startFastWrite(buf,len,0); //Write the payload if a buffer is clear + + return 1; //Return 1 to indicate successful transmission +} + +/****************************************************************************/ + +void RF24::reUseTX(){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + //spiTrans( REUSE_TX_PL ); + bcm2835_spi_transfer( REUSE_TX_PL); + bcm2835_gpio_write(ce_pin, LOW); //Re-Transfer packet + bcm2835_gpio_write(ce_pin, HIGH); +} + +/****************************************************************************/ + +bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //Return 0 so the user can control the retrys and set a timer or failure counter if required + //The radio will auto-clear everything in the FIFO as long as CE remains high + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail + + if( get_status() & _BV(MAX_RT)){ + //reUseTX(); //Set re-transmit + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + return 0; //Return 0. The previous payload has been retransmitted + //From the user perspective, if you get a 0, just keep trying to send the same payload + } + + } + //Start Writing + startFastWrite(buf,len,multicast); + + return 1; +} + +bool RF24::writeFast( const void* buf, uint8_t len ){ + return writeFast(buf,len,0); +} + +/****************************************************************************/ + +//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high +//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) +//Otherwise we enter Standby-II mode, which is still faster than standby mode +//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data + +void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 + + //write_payload( buf,len); + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + bcm2835_gpio_write(ce_pin, HIGH); + +} + +/****************************************************************************/ + +void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ) +{ + + // Send the payload + + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + bcm2835_gpio_write(ce_pin, HIGH); + delayMicroseconds(10); + bcm2835_gpio_write(ce_pin, LOW); +} + +/****************************************************************************/ + +bool RF24::txStandBy(){ + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); //Non blocking, flush the data + return 0; + } + } + + bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode + return 1; +} + +/****************************************************************************/ + +bool RF24::txStandBy(uint32_t timeout){ + + uint32_t start = millis(); + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); //Set re-transmit + bcm2835_gpio_write(ce_pin, HIGH); + if(millis() - start >= timeout){ + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); + return 0; + } + } + } + bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode + return 1; + +} +/****************************************************************************/ + +void RF24::maskIRQ(bool tx, bool fail, bool rx){ + + write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); +} + +/****************************************************************************/ + +uint8_t RF24::getDynamicPayloadSize(void) +{ + spi_txbuff[0] = R_RX_PL_WID; + spi_rxbuff[1] = 0xff; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + if(spi_rxbuff[1] > 32) { flush_rx(); return 0; } + + return spi_rxbuff[1]; +} + +/****************************************************************************/ + +bool RF24::available(void) +{ + return available(NULL); +} + +/****************************************************************************/ + +bool RF24::available(uint8_t* pipe_num) +{ + //Check the FIFO buffer to see if data is waitng to be read + + if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ + + + // If the caller wants the pipe number, include that + if ( pipe_num ){ + uint8_t status = get_status(); + *pipe_num = ( status >> RX_P_NO ) & 0b111; + } + return 1; + } + + return 0; +} + +/****************************************************************************/ + +void RF24::read( void* buf, uint8_t len ) +{ + // Fetch the payload + read_payload( buf, len ); + + //Clear the two possible interrupt flags with one command + write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); +} + +/****************************************************************************/ + +void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) +{ + // Read the status & reset the status in one easy call + // Or is that such a good idea? + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Report to the user what happened + tx_ok = status & _BV(TX_DS); + tx_fail = status & _BV(MAX_RT); + rx_ready = status & _BV(RX_DR); +} + +/****************************************************************************/ + +void RF24::openWritingPipe(uint64_t value) +{ + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) + // expects it LSB first too, so we're good. + + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); + write_register(TX_ADDR, reinterpret_cast(&value), 5); + + + write_register(RX_PW_P0,payload_size); +} + +/****************************************************************************/ + +static const uint8_t child_pipe[] = +{ + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 +}; +static const uint8_t child_payload_size[] = +{ + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 +}; +static const uint8_t child_pipe_enable[] = +{ + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 +}; + +void RF24::openReadingPipe(uint8_t child, uint64_t address) +{ + // If this is pipe 0, cache the address. This is needed because + // openWritingPipe() will overwrite the pipe 0 address, so + // startListening() will have to restore it. + if (child == 0) + pipe0_reading_address = address; + + if (child <= 6) + { + // For pipes 2-5, only write the LSB + if ( child < 2 ) + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); + else + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); + + write_register(pgm_read_byte(&child_payload_size[child]),payload_size); + + // Note it would be more efficient to set all of the bits for all open + // pipes at once. However, I thought it would make the calling code + // more simple to do it this way. + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); + } +} + +/****************************************************************************/ + +void RF24::toggle_features(void) +{ + bcm2835_spi_transfer( ACTIVATE ); + bcm2835_spi_transfer( 0x73 ); +} + +/****************************************************************************/ + +void RF24::enableDynamicPayloads(void) +{ + + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); + + if (debug) + printf("FEATURE=%i\r\n",read_register(FEATURE)); + + // Enable dynamic payload on all pipes + // + // Not sure the use case of only having dynamic payload on certain + // pipes, so the library does not support it. + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + + dynamic_payloads_enabled = true; +} + +/****************************************************************************/ + +void RF24::enableAckPayload(void) +{ + // + // enable ack payload and dynamic payload features + // + + + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + + if (debug) + printf("FEATURE=%i\r\n",read_register(FEATURE)); + + // + // Enable dynamic payload on pipes 0 & 1 + // + + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/****************************************************************************/ + +void RF24::enableDynamicAck(void){ + // + // enable dynamic ack features + // + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); + + if(debug){printf("FEATURE=%i\r\n",read_register(FEATURE));} + + +} + +/****************************************************************************/ + +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + + size = data_len + 1 ; // Add register value to transmit buffer + + if (debug){ + printf("[Writing %u bytes]",data_len); + } + *ptx++ = W_ACK_PAYLOAD | ( pipe & 0b111 ); + while ( data_len-- ){ + *ptx++ = *current++; + } + bcm2835_spi_transfern( (char *) spi_txbuff, size); + +} + + +/****************************************************************************/ + +bool RF24::isAckPayloadAvailable(void) +{ + return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); +} + +/****************************************************************************/ + +bool RF24::isPVariant(void) +{ + return p_variant ; +} + +/****************************************************************************/ + +void RF24::setAutoAck(bool enable) +{ + if ( enable ) + write_register(EN_AA, 0b111111); + else + write_register(EN_AA, 0); +} + +/****************************************************************************/ + +void RF24::setAutoAck( uint8_t pipe, bool enable ) +{ + if ( pipe <= 6 ) + { + uint8_t en_aa = read_register( EN_AA ) ; + if( enable ) + { + en_aa |= _BV(pipe) ; + } + else + { + en_aa &= ~_BV(pipe) ; + } + write_register( EN_AA, en_aa ) ; + } +} + +/****************************************************************************/ + +bool RF24::testCarrier(void) +{ + return ( read_register(CD) & 1 ); +} + +/****************************************************************************/ + +bool RF24::testRPD(void) +{ + return ( read_register(RPD) & 1 ) ; +} + +/****************************************************************************/ + +void RF24::setPALevel(uint8_t level) +{ + uint8_t setup = read_register(RF_SETUP) & 0b11111000; + + if(level > 3){ // If invalid level, go to max PA + level = (RF24_PA_MAX << 1) + 1; // +1 to support the SI24R1 chip extra bit + }else{ + level = (level << 1) + 1; // Else set level as requested + } + + + write_register( RF_SETUP, setup |= level ) ; // Write it to the chip +} + +/****************************************************************************/ + +uint8_t RF24::getPALevel(void) +{ + return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; +} + +/****************************************************************************/ + +bool RF24::setDataRate(rf24_datarate_e speed) +{ + bool result = false; + uint8_t setup = read_register(RF_SETUP) ; + + // HIGH and LOW '00' is 1Mbs - our default + wide_band = false ; + setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; + if( speed == RF24_250KBPS ) + { + // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 + // Making it '10'. + wide_band = false ; + setup |= _BV( RF_DR_LOW ) ; + } + else + { + // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 + // Making it '01' + if ( speed == RF24_2MBPS ) + { + wide_band = true ; + setup |= _BV(RF_DR_HIGH); + } + else + { + // 1Mbs + wide_band = false ; + } + } + write_register(RF_SETUP,setup); + + // Verify our result + if ( read_register(RF_SETUP) == setup ) + { + result = true; + } + else + { + wide_band = false; + } + + return result; +} + +/****************************************************************************/ + +rf24_datarate_e RF24::getDataRate( void ) +{ + rf24_datarate_e result ; + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + // switch uses RAM (evil!) + // Order matters in our case below + if ( dr == _BV(RF_DR_LOW) ) + { + // '10' = 250KBPS + result = RF24_250KBPS ; + } + else if ( dr == _BV(RF_DR_HIGH) ) + { + // '01' = 2MBPS + result = RF24_2MBPS ; + } + else + { + // '00' = 1MBPS + result = RF24_1MBPS ; + } + return result ; +} + +/****************************************************************************/ + +void RF24::setCRCLength(rf24_crclength_e length) +{ + uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; + + // switch uses RAM (evil!) + if ( length == RF24_CRC_DISABLED ) + { + // Do nothing, we turned it off above. + } + else if ( length == RF24_CRC_8 ) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV( CRCO ); + } + write_register( CONFIG, config ) ; +} + +/****************************************************************************/ + +rf24_crclength_e RF24::getCRCLength(void) +{ + rf24_crclength_e result = RF24_CRC_DISABLED; + uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; + + if ( config & _BV(EN_CRC ) ) + { + if ( config & _BV(CRCO) ) + result = RF24_CRC_16; + else + result = RF24_CRC_8; + } + + return result; +} + +/****************************************************************************/ + +void RF24::disableCRC( void ) +{ + uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; + write_register( CONFIG, disable ) ; +} + +/****************************************************************************/ +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR,(delay&0xf)< + + 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. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Modified to use the great bcm2835 library for I/O and SPI + + */ + +/** + * @file RF24.h + * + * Class declaration for RF24 and helper enums + */ + +#ifndef __RF24_H__ +#define __RF24_H__ + +#include "RF24_config.h" +#include "./bcm2835.h" + + +/** + * Power Amplifier level. + * + * For use with setPALevel() + */ +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; + +/** + * Data rate. How fast data moves through the air. + * + * For use with setDataRate() + */ +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +/** + * CRC Length. How big (if any) of a CRC is included. + * + * For use with setCRCLength() + */ +typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; + +/** + * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + */ + +class RF24 +{ +private: + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + uint16_t spi_speed; /**< SPI Bus Speed */ + bool wide_band; /* 2Mbs data rate in use? */ + bool p_variant; /* False for RF24L01 and true for RF24L01P */ + uint8_t payload_size; /**< Fixed size of payloads */ + //bool ack_payload_available; /**< Whether there is an ack payload waiting */ + bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ + //uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + //uint32_t spispeed; + uint8_t debug ; /* Debug flag */ + uint8_t spi_rxbuff[32+1] ; //SPI receive buffer (payload max 32 bytes) + uint8_t spi_txbuff[32+1] ; //SPI transmit buffer (payload max 32 bytes + 1 byte for the command) + +protected: + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. + */ + /**@{*/ + + + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); + + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); + + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); + + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @param len Number of bytes to be sent + * @return Current value of status register + */ + uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); + + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register + */ + uint8_t read_payload(void* buf, uint8_t len); + + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); + + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); + + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void); + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status); + + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value); + + /** + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); + /**@}*/ + +public: + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ + + /** + * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(uint8_t _cepin, uint8_t _cspin); + RF24(uint8_t _cepin, uint8_t _cspin, uint32_t spispeed ); + + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + */ + bool begin(void); + + /** + * Start listening on the pipes opened for reading. + * + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. + */ + void startListening(void); + + /** + * Stop listening for incoming messages + * + * Do this before calling write(). + */ + void stopListening(void); + + /** + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool write( const void* buf, uint8_t len ); + + /** + * Test whether there are bytes available to be read + * + * @return True if there is a payload available, false if none is + */ + bool available(void); + + /** + * Read the payload + * + * Return the last payload received + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer + * @return True if the payload was delivered successfully false if not + */ + void read( void* buf, uint8_t len ); + + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param address The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); + + /** + * Open a pipe for reading + * + * Up to 6 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning Pipes 1-5 should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * @code + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); + * @endcode + * + * @warning Pipe 0 is also used by the writing pipe. So if you open + * pipe 0 for reading, and then startListening(), it will overwrite the + * writing pipe. Ergo, do an openWritingPipe() again before write(). + * + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits + * + * @param number Which pipe# to open, 0-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); + + /**@}*/ + /** + * @name Optional Configurators + * + * Methods you can use to get or set the configuration of the chip. + * None are required. Calling begin() sets up a reasonable set of + * defaults. + */ + /**@{*/ + /** + * Set the number and delay of retries upon failed submit + * + * @param delay How long to wait between each retry, in multiples of 250us, + * max is 15. 0 means 250us, 15 means 4000us. + * @param count How many retries before giving up, max 15 + */ + void setRetries(uint8_t delay, uint8_t count); + + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); + + /** + * Set Static Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); + + /** + * Get Static Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void); + + /** + * Get Dynamic Payload Size + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @return Payload length of last-received dynamic payload + */ + uint8_t getDynamicPayloadSize(void); + + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde + */ + void enableAckPayload(void); + + /** + * Enable dynamically-sized payloads + * + * This way you don't always have to send large packets just to send them + * once in a while. This enables dynamic payloads on ALL pipes. + * + * @see examples/pingpair_pl/pingpair_dyn.pde + */ + void enableDynamicPayloads(void); + + /** + * Determine whether the hardware is an nRF24L01+ or not. + * + * @return true if the hardware is nRF24L01+ (or compatible) and false + * if its not. + */ + bool isPVariant(void) ; + + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); + + /** + * Enable or disable auto-acknowlede packets on a per pipeline basis. + * + * AA is enabled by default, so it's only needed if you want to turn + * it off/on for some reason on a per pipeline basis. + * + * @param pipe Which pipeline to modify + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck( uint8_t pipe, bool enable ) ; + + /** + * Set Power Amplifier (PA) level to one of four levels. + * Relative mnemonics have been used to allow for future PA level + * changes. According to 6.5 of the nRF24L01+ specification sheet, + * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, + * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. + * + * @param level Desired PA level. + */ + void setPALevel( uint8_t level ) ; + + /** + * Fetches the current PA level. + * + * @return Returns a value from the rf24_pa_dbm_e enum describing + * the current PA setting. Please remember, all values represented + * by the enum mnemonics are negative dBm. See setPALevel for + * return value descriptions. + */ + uint8_t getPALevel( void ) ; + + /** + * Set the transmission data rate + * + * @warning setting RF24_250KBPS will fail for non-plus units + * + * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps + * @return true if the change was successful + */ + bool setDataRate(rf24_datarate_e speed); + + /** + * Fetches the transmission data rate + * + * @return Returns the hardware's currently configured datarate. The value + * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the + * rf24_datarate_e enum. + */ + rf24_datarate_e getDataRate( void ) ; + + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); + + /** + * Get the CRC length + * + * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + rf24_crclength_e getCRCLength(void); + + /** + * Disable CRC validation + * + */ + void disableCRC( void ) ; + + /**@}*/ + /** + * @name Advanced Operation + * + * Methods you can use to drive the chip in more advanced ways + */ + /**@{*/ + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); + + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening, or powerUp(). + */ + void powerDown(void); + + /** + * Leave low-power mode - making radio more responsive + * + * To return to low power mode, call powerDown(). + */ + void powerUp(void) ; + +/** + * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * + * Can be used with enableAckPayload() to request a response + * @see enableDynamicAck() + * @see setAutoAck() + * @see write() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0), NOACK (1) + */ + bool write( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command * + * This will not block until the 3 FIFO buffers are filled with data. + * Once the FIFOs are full, writeFast will simply wait for success or + * timeout, and return 1 or 0 respectively. From a user perspective, just + * keep trying to send the same data. The library will keep auto retrying + * the current payload using the built in functionality. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeFast is called + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(1000); // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0. + * @endcode + * + * @see txStandBy() + * @see write() + * @see writeBlocking() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool writeFast( const void* buf, uint8_t len ); + + /** + * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + */ + bool writeFast( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command + * This function extends the auto-retry mechanism to any specified duration. + * It will not block until the 3 FIFO buffers are filled with data. + * If so the library will auto retry until a new payload is written + * or the user specified timeout period is reached. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeBlocking is called + * @code + * Example (Full blocking): + * + * radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers + * txStandBy(1000); //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed. + * //Blocks only until user timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @see txStandBy() + * @see write() + * @see writeFast() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param timeout User defined timeout in milliseconds. + * @return True if the payload was loaded into the buffer successfully false if not + */ + bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); + + /** + * @note Optimization: New Command + * This function should be called as soon as transmission is finished to + * drop the radio back to STANDBY-I mode. If not issued, the radio will + * remain in STANDBY-II mode which, per the data sheet, is not a recommended + * operating mode. + * + * @note When transmitting data in rapid succession, it is still recommended by + * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is + * time enough between sends for the FIFOs to empty. + * + * Relies on built-in auto retry functionality. + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. + * //Blocks only until MAX_RT timeout or success. Data flushed on fail. + * @endcode + * @see txStandBy(unsigned long timeout) + * @return True if transmission is successful + * + */ + bool txStandBy(); + + /** + * @note Optimization: New Command + * + * This function allows extended blocking and auto-retries per a user defined timeout + * @code + * Fully Blocking Example: + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. + * //Blocks only until user defined timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @param timeout Number of milliseconds to retry failed payloads + * @return True if transmission is successful + * + */ + bool txStandBy(uint32_t timeout); + + /** + * Test whether there are bytes available to be read + * + * Use this version to discover on which pipe the message + * arrived. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + bool available(uint8_t* pipe_num); + +/** + * Non-blocking write to the open writing pipe used for buffered writes + * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * @see write() + * @see writeFast() + * @see startWrite() + * @see writeBlocking() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * @return True if the payload was delivered successfully false if not + */ + void startFastWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @note Optimization: This function again behaves as it did previously for backwards-compatibility. + * with user code. The library uses startFastWrite() internally. + * This is mainly used for single-payload transactions. + * + * @see write() + * @see writeFast() + * @see startFastWrite() + * @see whatHappened() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * + */ + void startWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Optimization: New Command + * + * This function is mainly used internally to take advantage of the auto payload + * re-use functionality of the chip, but can be beneficial to users as well. + * + * The function will instruct the radio to re-use the data in the FIFO buffers, + * and instructs the radio to re-send once the timeout limit has been reached. + * Used by writeFast and writeBlocking to initiate retries when a TX failure + * occurs. Retries are automatically initiated except with the standard write(). + * This way, data is not flushed from the buffer until switching between modes. + * + * @note This is to be used AFTER auto-retry fails if wanting to resend + * using the built-in payload reuse features. + * After issuing reUseTX(), it will keep reending the same payload forever or until + * a payload is written to the FIFO, or a flush_tx command is given. + */ + void reUseTX(); + + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time. I have not tested this. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + /** + * Enable dynamic ACKs (single write multicasting) for chosen messages + * + * @note To enable full multicasting or per-pipe multicast, use setAutoAck() + * + * @warning This MUST be called prior to attempting single write NOACK calls + * @code + * radio.enableDynamicAck(); + * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested + * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK + * @endcode + */ + void enableDynamicAck(); + + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @warning Calling this function clears the internal flag which indicates + * a payload is available. If it returns true, you must read the packet + * out as the very next interaction with the radio, or the results are + * undefined. + * + * @return True if an ack payload is available. + */ + bool isAckPayloadAvailable(void); + + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + bool testCarrier(void); + + /** + * Test whether a signal (carrier or otherwise) greater than + * or equal to -64dBm is present on the channel. Valid only + * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * + * Useful to check for interference on the current channel and + * channel hopping strategies. + * + * @return true if signal => -64dBm, false if not + */ + bool testRPD(void) ; + + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + + /** + * The radio will generate interrupt signals when a transmission is complete, + * a transmission fails, or a payload is received. This allows users to mask + * those interrupts to prevent them from generating a signal on the interrupt + * pin. + * + * @code + * Mask all interrupts except the receive interrupt: + * + * radio.maskIRQ(1,1,0); + * @endcode + * + * @param tx_ok Mask transmission complete interrupts + * @param tx_fail Mask transmit failure interrupts + * @param rx_ready Mask payload received interrupts + */ + void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); + + /**@}*/ +}; + +/** + * @example GettingStarted.pde + * + * This is an example which corresponds to my "Getting Started" blog post: + * Getting Started with nRF24L01+ on Arduino. + * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + +/** + * @example led_remote.pde + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + +/** + * @example pingpair.pde + * + * This is an example of how to use the RF24 class. Write this sketch to two + * different nodes, connect the role_pin to ground on one. The ping node sends + * the current time to the pong node, which responds by sending the value back. + * The ping node can then see how long the whole cycle took. + */ + +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +/** + * @example starping.pde + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +/** + * @example pingpair_pl.pde + * + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within + * the Ack packet of each transmission. Note that the payload is set BEFORE + * the sender's message arrives. + */ + +/** + * @example pingpair_irq.pde + * + * This is an example of how to user interrupts to interact with the radio. + * It builds on the pingpair_pl example, and uses ack payloads. + */ + +/** + * @example pingpair_sleepy.pde + * + * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the pingpair.pde example, but the + * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle. + */ + +/** + * @example scanner.pde + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +/** + * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + * + * @section Goals Design Goals + * + * This library is designed to be... + * @li Maximally compliant with the intended operation of the chip + * @li Easy for beginners to use + * @li Consumed with a public interface that's similiar to other Arduino standard libraries + * + * @section News News + * + * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. + * Please open an issue if you find any problems using it with any version of Arduino. + * + * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the + * Maple Native, + * and should work with any Maple board. See the pingpair_maple example. + * Note that only the pingpair_maple example has been tested on Maple, although + * the others can certainly be adapted. + * + * @section Useful Useful References + * + * Please refer to: + * + * @li Documentation Main Page + * @li RF24 Class Documentation + * @li Source Code + * @li Downloads Page + * @li Chip Datasheet + * + * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or + * the SPI hardware will go into 'slave' mode. + * + * @section More More Information + * + * @subpage FAQ + * + * @section Projects Projects + * + * Stuff I have built with RF24 + * + * RF24 Getting Started - Finished Product + * + * Getting Started with nRF24L01+ on Arduino + * + * Nordic FOB and nRF24L01+ + * + * Using the Sparkfun Nordic FOB + * + * RF Duinode V3 (2V4) + * + * Low-Power Wireless Sensor Node + * + * nRF24L01+ connected to Leaf Labs Maple Native + * + * nRF24L01+ Running on Maple + */ + +#endif // __RF24_H__ +// vim:ai:cin:sts=2 sw=2 ft=cpp + diff --git a/libraries/RF24/RPi/RF24/RF24_config.h b/libraries/RF24/RPi/RF24/RF24_config.h new file mode 100644 index 0000000..eef0704 --- /dev/null +++ b/libraries/RF24/RPi/RF24/RF24_config.h @@ -0,0 +1,34 @@ + +/* + Copyright (C) 2011 J. Coliz + + 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. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Modified to use the great bcm2835 library for I/O and SPI + + */ + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ + +#include +#include +#include +#include +#include +#include +#include "bcm2835.h" + +// GCC a Arduino Missing +#define max(a,b) (a>b?a:b) +#define min(a,b) (a +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "bcm2835.h" + +// This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11) +// You can do some safe, non-destructive testing on any platform with: +// gcc bcm2835.c -D BCM2835_TEST +// ./a.out +//#define BCM2835_TEST + +// Uncommenting this define compiles alternative I2C code for the version 1 RPi +// The P1 header I2C pins are connected to SDA0 and SCL0 on V1. +// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. +// #define I2C_V1 + +// Pointers to the hardware register bases +volatile uint32_t *bcm2835_gpio = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pwm = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_clk = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pads = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_spi0 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc0 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc1 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_st = (volatile 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; + +// I2C The time needed to transmit one byte. In microseconds. +static int i2c_byte_wait_us = 0; + +// SPI Custom Chip Select Pin +//static int spi_custom_cs = 0; + +// Time for millis() +static unsigned long long epoch ; + +// +// Low level register access functions +// + +void bcm2835_set_debug(uint8_t d) +{ + debug = d; +} + +// safe read from peripheral +uint32_t bcm2835_peri_read(volatile uint32_t* paddr) +{ + if (debug) + { + printf("bcm2835_peri_read paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + // Make sure we dont return the _last_ read which might get lost + // if subsequent code changes to a different peripheral + uint32_t ret = *paddr; + *paddr; // Read without assigneing to an unused variable + return ret; + } +} + +// read from peripheral without the read barrier +uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) +{ + if (debug) + { + printf("bcm2835_peri_read_nb paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + return *paddr; + } +} + +// safe write to peripheral +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); + } + else + { + // Make sure we don't rely on the first write, which may get + // lost if the previous access was to a different peripheral. + *paddr = value; + *paddr = value; + } +} + +// write to peripheral without the write barrier +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); + } + else + { + *paddr = value; + } +} + +// Set/clear only the bits in value covered by the mask +void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask) +{ + uint32_t v = bcm2835_peri_read(paddr); + v = (v & ~mask) | (value & mask); + bcm2835_peri_write(paddr, v); +} + +// +// Low level convenience functions +// + +// Function select +// pin is a BCM2835 GPIO pin number NOT RPi pin number +// There are 6 control registers, each control the functions of a block +// of 10 pins. +// Each control register has 10 sets of 3 bits per GPIO pin: +// +// 000 = GPIO Pin X is an input +// 001 = GPIO Pin X is an output +// 100 = GPIO Pin X takes alternate function 0 +// 101 = GPIO Pin X takes alternate function 1 +// 110 = GPIO Pin X takes alternate function 2 +// 111 = GPIO Pin X takes alternate function 3 +// 011 = GPIO Pin X takes alternate function 4 +// 010 = GPIO Pin X takes alternate function 5 +// +// So the 3 bits for port X are: +// X / 10 + ((X % 10) * 3) +void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode) +{ + // Function selects are 10 pins per 32 bit word, 3 bits per pin + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10); + uint8_t shift = (pin % 10) * 3; + uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift; + uint32_t value = mode << shift; + bcm2835_peri_set_bits(paddr, value, mask); +} + +// Set output pin +void bcm2835_gpio_set(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +// Clear output pin +void bcm2835_gpio_clr(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +// Set all output pins in the mask +void bcm2835_gpio_set_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4; + bcm2835_peri_write(paddr, mask); +} + +// Clear all output pins in the mask +void bcm2835_gpio_clr_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4; + bcm2835_peri_write(paddr, mask); +} + +// Read input pin +uint8_t bcm2835_gpio_lev(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEV0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +// See if an event detection bit is set +// Sigh cant support interrupts yet +uint8_t bcm2835_gpio_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +// Write a 1 to clear the bit in EDS +void bcm2835_gpio_set_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_write(paddr, value); +} + +// Rising edge detect enable +void bcm2835_gpio_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Falling edge detect enable +void bcm2835_gpio_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// High detect enable +void bcm2835_gpio_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Low detect enable +void bcm2835_gpio_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Async rising edge detect enable +void bcm2835_gpio_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Async falling edge detect enable +void bcm2835_gpio_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Set pullup/down +void bcm2835_gpio_pud(uint8_t pud) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; + bcm2835_peri_write(paddr, pud); +} + +// Pullup/down clock +// Clocks the value of pud into the GPIO pin +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); +} + +// Read GPIO pad behaviour for groups of GPIOs +uint32_t bcm2835_gpio_pad(uint8_t group) +{ + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; + return bcm2835_peri_read(paddr); +} + +// Set GPIO pad behaviour for groups of GPIOs +// powerup value for al pads is +// BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA +void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) +{ + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; + bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD); +} + +// Some convenient arduino-like functions +// milliseconds +void bcm2835_delay(unsigned int millis) +{ + struct timespec sleeper; + + sleeper.tv_sec = (time_t)(millis / 1000); + sleeper.tv_nsec = (long)(millis % 1000) * 1000000; + nanosleep(&sleeper, NULL); +} + +// microseconds +void bcm2835_delayMicroseconds(uint64_t micros) +{ + struct timespec t1; + uint64_t start; + + // Calling nanosleep() takes at least 100-200 us, so use it for + // long waits and use a busy wait on the System Timer for the rest. + start = bcm2835_st_read(); + + if (micros > 450) + { + t1.tv_sec = 0; + t1.tv_nsec = 1000 * (long)(micros - 200); + nanosleep(&t1, NULL); + } + + bcm2835_st_delay(start, micros); +} + +// This function is added in order to simulate arduino millis() function +unsigned int bcm2835_millis(void) +{ +struct timeval now; +unsigned long long ms; + +gettimeofday(&now, NULL); + +ms = (now.tv_sec * 1000000 + now.tv_usec) / 1000 ; + +return ((uint32_t) (ms - epoch )); +} + +// +// Higher level convenience functions +// + +// Set the state of an output +void bcm2835_gpio_write(uint8_t pin, uint8_t on) +{ + if (on) + bcm2835_gpio_set(pin); + else + bcm2835_gpio_clr(pin); +} + +// Set the state of a all 32 outputs in the mask to on or off +void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on) +{ + if (on) + bcm2835_gpio_set_multi(mask); + else + bcm2835_gpio_clr_multi(mask); +} + +// Set the state of a all 32 outputs in the mask to the values in value +void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) +{ + bcm2835_gpio_set_multi(value & mask); + bcm2835_gpio_clr_multi((~value) & mask); +} + +// Set the pullup/down resistor for a pin +// +// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on +// the respective GPIO pins. These registers must be used in conjunction with the GPPUD +// register to effect GPIO Pull-up/down changes. The following sequence of events is +// required: +// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither +// to remove the current Pull-up/down) +// 2. Wait 150 cycles ? this provides the required set-up time for the control signal +// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to +// modify ? NOTE only the pads which receive a clock will be modified, all others will +// retain their previous state. +// 4. Wait 150 cycles ? this provides the required hold time for the control signal +// 5. Write to GPPUD to remove the control signal +// 6. Write to GPPUDCLK0/1 to remove the clock +// +// RPi has P1-03 and P1-05 with 1k8 pullup resistor +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); +} + +void bcm2835_spi_begin() +{ + + // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them + //bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + + // Enable CE0 by default, will be disabled if bcm2835_chipSelect() is called using another pin + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 + + + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK + + // Set the SPI CS register to the some sensible defaults + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + bcm2835_peri_write(paddr, 0); // All 0s + + // Clear TX and RX fifos + bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); +} + +void bcm2835_spi_end(void) +{ + /* // Set all the SPI0 pins back to input + if (spi_custom_cs == 0) + { + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 + } + else + { + bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_INPT); // Custom GPIO + } + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK + */ + + // Set all the SPI0 pins back to input + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK + +} + +/* +// Drive Custom chip select pin +void bcm2835_spi_setChipSelect(uint8_t level) +{ + // Do this only if we are using custom ChipSelect I/O + if ( spi_custom_cs > BCM2835_SPI_CS_NONE ) + bcm2835_gpio_write(spi_custom_cs, level); +}*/ + + + void bcm2835_spi_setBitOrder(uint8_t order) +{ + // BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one suported by SPI0 +} + +// defaults to 0, which means a divider of 65536. +// The divisor must be a power of 2. Odd numbers +// rounded down. The maximum SPI clock rate is +// of the APB clock +void bcm2835_spi_setClockDivider(uint16_t divider) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CLK/4; + bcm2835_peri_write(paddr, divider); +} + +void bcm2835_spi_setDataMode(uint8_t mode) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + // Mask in the CPO and CPHA bits of CS + bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA); +} + +// Writes (and reads) a single byte to SPI +uint8_t bcm2835_spi_transfer(uint8_t value) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + + // Custom chip select LOW + /*if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(LOW); + }*/ + + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + // 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)){ + delayMicroseconds(10); + } + + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, value); + + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ + delayMicroseconds(10); + } + + // Read any byte that was sent back by the slave while we sere sending to it + uint32_t ret = bcm2835_peri_read_nb(fifo); + + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); + + // Custom chip select HIGH + //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + // bcm2835_spi_setChipSelect(HIGH); + //} + return ret; +} + +// Writes (and reads) an number of bytes to SPI +void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + uint32_t TXCnt=0; + uint32_t RXCnt=0; + + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + + // 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); + + // Use the FIFO's to reduce the interbyte times + 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 )) + { + delayMicroseconds(10); + bcm2835_peri_write_nb(fifo, tbuf[TXCnt]); + TXCnt++; + } + //Rx fifo not empty, so get the next received bytes + while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) + { + delayMicroseconds(10); + rbuf[RXCnt] = bcm2835_peri_read_nb(fifo); + RXCnt++; + } + } + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ + delayMicroseconds(10); + } + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); +} + +// Writes an number of bytes to SPI +void bcm2835_spi_writenb(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; + + //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + // bcm2835_spi_setChipSelect(LOW); + //} + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + + // 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); + + uint32_t i; + for (i = 0; i < len; i++) + { + // Maybe wait for TXD + while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) + ; + + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, tbuf[i]); + + // Read from FIFO to prevent stalling + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + } + + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) { + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + }; + + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); + + //if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + // bcm2835_spi_setChipSelect(HIGH); + //} +} + +// Writes (and reads) an number of bytes to SPI +// Read bytes are copied over onto the transmit buffer +void bcm2835_spi_transfern(char* buf, uint32_t len) +{ + bcm2835_spi_transfernb(buf, buf, len); +} + +void bcm2835_spi_chipSelect(uint8_t cs) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + + if(cs){ + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // Revert default of CE0 back to input + }else{ + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 + } + + // Mask in the CS bits of CS + bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); +} + +void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + uint8_t shift = 21 + cs; + // Mask in the appropriate CSPOLn bit + bcm2835_peri_set_bits(paddr, active << shift, 1 << shift); +} + +void bcm2835_i2c_begin(void) +{ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; + // Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; + // Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL +#endif + + // Read the clock divider register + uint16_t cdiv = bcm2835_peri_read(paddr); + // Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9; +} + +void bcm2835_i2c_end(void) +{ +#ifdef I2C_V1 + // Set all the I2C/BSC0 pins back to input + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL +#else + // Set all the I2C/BSC1 pins back to input + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL +#endif +} + +void bcm2835_i2c_setSlaveAddress(uint8_t addr) +{ + // Set I2C Device Address +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4; +#endif + bcm2835_peri_write(paddr, addr); +} + +// defaults to 0x5dc, should result in a 166.666 kHz I2C clock frequency. +// The divisor must be a power of 2. Odd numbers +// rounded down. +void bcm2835_i2c_setClockDivider(uint16_t divider) +{ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; +#endif + bcm2835_peri_write(paddr, divider); + // Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + i2c_byte_wait_us = ((float)divider / BCM2835_CORE_CLK_HZ) * 1000000 * 9; +} + +// set I2C clock divider by means of a baudrate number +void bcm2835_i2c_set_baudrate(uint32_t baudrate) +{ + uint32_t divider; + // use 0xFFFE mask to limit a max value and round down any odd number + divider = (BCM2835_CORE_CLK_HZ / baudrate) & 0xFFFE; + bcm2835_i2c_setClockDivider( (uint16_t)divider ); +} + +// Writes an number of bytes to I2C +uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, len); + // pre populate FIFO with max buffer + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, buf[i]); + i++; + remaining--; + } + + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // Transfer is over when BCM2835_BSC_S_DONE + while(!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE )) + { + while ( remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TXD )) + { + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, buf[i]); + i++; + remaining--; + } + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read an number of bytes from I2C +uint8_t bcm2835_i2c_read(char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, len); + // Start read + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ); + + // 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) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is received + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read an number of bytes from I2C sending a repeated start after writing +// the required register. Only works if your device supports this mode +uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, 1); + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN); + bcm2835_peri_write_nb(fifo, regaddr[0]); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // poll for transfer has started + while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) + { + // Linux may cause us to miss entire transfer stage + if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) + break; + } + + // Send a repeated start with read bit set in address + bcm2835_peri_write_nb(dlen, len); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + // Wait for write to complete and first byte back. + bcm2835_delayMicroseconds(i2c_byte_wait_us * 3); + + // 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 (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Sending an arbitrary number of bytes before issuing a repeated start +// (with no prior stop) and reading a response. Some devices require this behavior. +uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = cmds_len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + 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 + bcm2835_peri_write_nb(dlen, cmds_len); + + // pre populate FIFO with max buffer + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, cmds[i]); + i++; + remaining--; + } + + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // poll for transfer has started (way to do repeated start, from BCM2835 datasheet) + while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) + { + // Linux may cause us to miss entire transfer stage + if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) + break; + } + + remaining = buf_len; + i = 0; + + // Send a repeated start with read bit set in address + bcm2835_peri_write_nb(dlen, buf_len); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + // Wait for write to complete and first byte back. + bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1)); + + // 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 (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read the System Timer Counter (64-bits) +uint64_t bcm2835_st_read(void) +{ + volatile uint32_t* paddr; + uint64_t st; + paddr = bcm2835_st + BCM2835_ST_CHI/4; + st = bcm2835_peri_read(paddr); + st <<= 32; + paddr = bcm2835_st + BCM2835_ST_CLO/4; + st += bcm2835_peri_read(paddr); + return st; +} + +// Delays for the specified number of microseconds with offset +void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros) +{ + uint64_t compare = offset_micros + micros; + + while(bcm2835_st_read() < compare) + ; +} + +// PWM + +void bcm2835_pwm_set_clock(uint32_t divisor) +{ + // From Gerts code + divisor &= 0xfff; + // Stop PWM clock + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01); + bcm2835_delay(110); // Prevents clock going slow + // Wait for the clock to be not busy + while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) + bcm2835_delay(1); + // set the clock divider and enable PWM clock + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); // Source=osc and enable +} + +void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled) +{ + uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL); + + if (channel == 0) + { + if (markspace) + control |= BCM2835_PWM0_MS_MODE; + else + control &= ~BCM2835_PWM0_MS_MODE; + if (enabled) + control |= BCM2835_PWM0_ENABLE; + else + control &= ~BCM2835_PWM0_ENABLE; + } + else if (channel == 1) + { + if (markspace) + control |= BCM2835_PWM1_MS_MODE; + else + control &= ~BCM2835_PWM1_MS_MODE; + if (enabled) + control |= BCM2835_PWM1_ENABLE; + else + control &= ~BCM2835_PWM1_ENABLE; + } + + // If you use the barrier here, wierd things happen, and the commands dont work + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control); + // bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); + +} + +void bcm2835_pwm_set_range(uint8_t channel, uint32_t range) +{ + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range); +} + +void bcm2835_pwm_set_data(uint8_t channel, uint32_t data) +{ + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data); +} + +// Allocate page-aligned memory. +void *malloc_aligned(size_t size) +{ + void *mem; + errno = posix_memalign(&mem, BCM2835_PAGE_SIZE, size); + return (errno ? NULL : mem); +} + +// Map 'size' bytes starting at 'off' in file 'fd' to memory. +// Return mapped address on success, MAP_FAILED otherwise. +// On error print message. +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_FAILED == map) + fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); + return map; +} + +static void unmapmem(void **pmem, size_t size) +{ + if (*pmem == MAP_FAILED) return; + munmap(*pmem, size); + *pmem = MAP_FAILED; +} + +// Initialise this library. +int bcm2835_init(void) +{ + struct timeval tv ; + + if (debug) + { + bcm2835_pads = (uint32_t*)BCM2835_GPIO_PADS; + bcm2835_clk = (uint32_t*)BCM2835_CLOCK_BASE; + bcm2835_gpio = (uint32_t*)BCM2835_GPIO_BASE; + bcm2835_pwm = (uint32_t*)BCM2835_GPIO_PWM; + bcm2835_spi0 = (uint32_t*)BCM2835_SPI0_BASE; + bcm2835_bsc0 = (uint32_t*)BCM2835_BSC0_BASE; + bcm2835_bsc1 = (uint32_t*)BCM2835_BSC1_BASE; + bcm2835_st = (uint32_t*)BCM2835_ST_BASE; + return 1; // Success + } + int memfd = -1; + int ok = 0; + // Open the master /dev/memory device + if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) + { + fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", + strerror(errno)) ; + goto exit; + } + + // GPIO: + bcm2835_gpio = (volatile uint32_t *)mapmem("gpio", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_BASE); + if (bcm2835_gpio == MAP_FAILED) goto exit; + + // PWM + bcm2835_pwm = (volatile uint32_t *)mapmem("pwm", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PWM); + if (bcm2835_pwm == MAP_FAILED) goto exit; + + // Clock control (needed for PWM) + bcm2835_clk = (volatile uint32_t *)mapmem("clk", BCM2835_BLOCK_SIZE, memfd, BCM2835_CLOCK_BASE); + if (bcm2835_clk == MAP_FAILED) goto exit; + + bcm2835_pads = (volatile uint32_t *)mapmem("pads", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PADS); + if (bcm2835_pads == MAP_FAILED) goto exit; + + bcm2835_spi0 = (volatile uint32_t *)mapmem("spi0", BCM2835_BLOCK_SIZE, memfd, BCM2835_SPI0_BASE); + if (bcm2835_spi0 == MAP_FAILED) goto exit; + + // I2C + bcm2835_bsc0 = (volatile uint32_t *)mapmem("bsc0", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC0_BASE); + if (bcm2835_bsc0 == MAP_FAILED) goto exit; + + bcm2835_bsc1 = (volatile uint32_t *)mapmem("bsc1", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC1_BASE); + if (bcm2835_bsc1 == MAP_FAILED) goto exit; + + // ST + bcm2835_st = (volatile uint32_t *)mapmem("st", BCM2835_BLOCK_SIZE, memfd, BCM2835_ST_BASE); + if (bcm2835_st == MAP_FAILED) goto exit; + + ok = 1; + +exit: + if (memfd >= 0) + close(memfd); + + if (!ok) + bcm2835_close(); + + gettimeofday (&tv, NULL) ; + epoch = (tv.tv_sec * 1000000 + tv.tv_usec) / 1000 ; + + return ok; +} + +// Close this library and deallocate everything +int bcm2835_close(void) +{ + if (debug) return 1; // Success + unmapmem((void**) &bcm2835_gpio, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_pwm, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_clk, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_spi0, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_bsc0, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_bsc1, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_st, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_pads, BCM2835_BLOCK_SIZE); + return 1; // Success +} + +#ifdef BCM2835_TEST +// this is a simple test program that prints out what it will do rather than +// actually doing it +int main(int argc, char **argv) +{ + // Be non-destructive + bcm2835_set_debug(1); + + if (!bcm2835_init()) + return 1; + + // Configure some GPIO pins fo some testing + // Set RPI pin P1-11 to be an output + bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP); + // Set RPI pin P1-15 to be an input + bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT); + // with a pullup + bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP); + // And a low detect enable + bcm2835_gpio_len(RPI_GPIO_P1_15); + // and input hysteresis disabled on GPIOs 0 to 27 + bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA); + +#if 1 + // Blink + while (1) + { + // Turn it on + bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH); + + // wait a bit + bcm2835_delay(500); + + // turn it off + bcm2835_gpio_write(RPI_GPIO_P1_11, LOW); + + // wait a bit + bcm2835_delay(500); + } +#endif + +#if 0 + // Read input + while (1) + { + // Read some data + uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15); + printf("read from pin 15: %d\n", value); + + // wait a bit + bcm2835_delay(500); + } +#endif + +#if 0 + // Look for a low event detection + // eds will be set whenever pin 15 goes low + while (1) + { + if (bcm2835_gpio_eds(RPI_GPIO_P1_15)) + { + // Now clear the eds flag by setting it to 1 + bcm2835_gpio_set_eds(RPI_GPIO_P1_15); + printf("low event detect for pin 15\n"); + } + + // wait a bit + bcm2835_delay(500); + } +#endif + + if (!bcm2835_close()) + return 1; + + return 0; +} +#endif + + + diff --git a/libraries/RF24/RPi/RF24/bcm2835.h b/libraries/RF24/RPi/RF24/bcm2835.h new file mode 100644 index 0000000..f550fcb --- /dev/null +++ b/libraries/RF24/RPi/RF24/bcm2835.h @@ -0,0 +1,1353 @@ +// bcm2835.h +// +// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi +// +// Author: Mike McCauley +// Copyright (C) 2011-2013 Mike McCauley +// $Id: bcm2835.h,v 1.13 2013/12/06 22:24:52 mikem Exp mikem $ +// +//****************************************/ +// TMRh20 2014 - Merge updated lib from here: http://www.airspayce.com/mikem/bcm2835/group__spi.html#ga2fa186568605c21e9166a19b1d82ea95 +// with lib from here: https://github.com/farconada/RF24Network/blob/master/bcm2835.c +// Uses ONLY hardware CS pins +// Any CE pin can be used +// For use with optimized RPi RF24 and RF24 Network libs - see github.com/TMRh20 +//****************************************/ +// +/// \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi +/// +/// This is a C library for Raspberry Pi (RPi). It provides access to +/// GPIO and other IO functions on the Broadcom BCM 2835 chip, +/// 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. +/// +/// It provides functions for reading digital inputs and setting digital outputs, using SPI and I2C, +/// and for accessing the system timers. +/// Pin event detection is supported by polling (interrupts are not supported). +/// +/// 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.36.tar.gz +/// You can find the latest version at http://www.airspayce.com/mikem/bcm2835 +/// +/// Several example programs are provided. +/// +/// Based on data in http://elinux.org/RPi_Low-level_peripherals and +/// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +/// and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// +/// 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. +/// +/// Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian +/// and Occidentalisv01 +/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() +/// are used and the pin is pulled LOW +/// it can cause temporary hangs on 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian +/// and Occidentalisv01. +/// Reason for this is not yet determined, but we suspect that an interrupt handler is +/// hitting a hard loop on those OSs. +/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with +/// bcm2835_gpio_clr_len() and friends after use. +/// +/// \par Installation +/// +/// This library consists of a single non-shared library and header file, which will be +/// installed in the usual places by make install +/// +/// \code +/// # download the latest version of the library, say bcm2835-1.xx.tar.gz, then: +/// tar zxvf bcm2835-1.xx.tar.gz +/// cd bcm2835-1.xx +/// ./configure +/// make +/// sudo make check +/// sudo make install +/// \endcode +/// +/// \par Physical Addresses +/// +/// The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() +/// are low level peripheral register access functions. They are designed to use +/// physical addresses as described in section 1.2.3 ARM physical addresses +/// of the BCM2835 ARM Peripherals manual. +/// Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus +/// addresses for peripherals are set up to map onto the peripheral bus address range starting at +/// 0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at +/// physical address 0x20nnnnnn. +/// +/// The base address of the various peripheral registers are available with the following +/// externals: +/// bcm2835_gpio +/// bcm2835_pwm +/// bcm2835_clk +/// bcm2835_pads +/// bcm2835_spio0 +/// bcm2835_st +/// bcm2835_bsc0 +/// bcm2835_bsc1 +/// +/// \par Pin Numbering +/// +/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying +/// BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs +/// +/// RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, +/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the +/// IDE header. +/// +/// RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd. +/// +/// The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ +/// the RPi pin number. There are symbolic definitions for each of the available pins +/// that you should use for convenience. See \ref RPiGPIOPin. +/// +/// \par SPI Pins +/// +/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, +/// allowing you to send and received data by SPI (Serial Peripheral Interface). +/// For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus +/// +/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their +/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able +/// to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). +/// When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be +/// configured and controled with the usual bcm2835_gpio_* calls. +/// +/// The Raspberry Pi GPIO pins used for SPI are: +/// +/// - P1-19 (MOSI) +/// - P1-21 (MISO) +/// - P1-23 (CLK) +/// - P1-24 (CE0) +/// - P1-26 (CE1) +/// +/// \par I2C Pins +/// +/// The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface, +/// allowing you to send and received data by I2C ("eye-squared cee"; generically referred to as "two-wire interface") . +/// For more information about I?C, see http://en.wikipedia.org/wiki/I%C2%B2C +/// +/// The Raspberry Pi V2 GPIO pins used for I2C are: +/// +/// - P1-03 (SDA) +/// - P1-05 (SLC) +/// +/// \par PWM +/// +/// The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides +/// functions for configuring and controlling PWM output on these pins. +/// +/// The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset of +/// GPIO pins. The following GPIO pins may be connected to the following PWM channels (from section 9.5): +/// \code +/// GPIO PIN RPi pin PWM Channel ALT FUN +/// 12 0 0 +/// 13 1 0 +/// 18 1-12 0 5 +/// 19 1 5 +/// 40 0 0 +/// 41 1 0 +/// 45 1 0 +/// 52 0 1 +/// 53 1 1 +/// \endcode +/// In order for a GPIO pin to emit output from its PWM channel, it must be set to the Alt Function given above. +/// Note carefully that current versions of the Raspberry Pi only expose one of these pins (GPIO 18 = RPi Pin 1-12) +/// on the IO headers, and therefore this is the only IO pin on the RPi that can be used for PWM. +/// Further it must be set to ALT FUN 5 to get PWM output. +/// +/// Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using +/// bcm2835_pwm_set_clock(). Each channel can be separately enabled with bcm2835_pwm_set_mode(). +/// The average output of the PWM channel is determined by the ratio of DATA/RANGE for that channel. +/// Use bcm2835_pwm_set_range() to set the range and bcm2835_pwm_set_data() to set the data in that ratio +/// +/// Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware +/// sends a combination of clock pulses that results in an overall DATA pulses per RANGE pulses. +/// In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by +/// LOW for RANGE-DATA clock pulses. +/// +/// The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from +/// a 19.2MHz clock. You can set any divider, but some common ones are provided by the BCM2835_PWM_CLOCK_DIVIDER_* +/// values of \ref bcm2835PWMClockDivider. +/// +/// For example, say you wanted to drive a DC motor with PWM at about 1kHz, +/// and control the speed in 1/1024 increments from +/// 0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the +/// clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be +/// 1.2MHz/1024 = 1171.875Hz. +/// +/// \par Real Time performance constraints +/// +/// The bcm2835 is a library for user programs (i.e. they run in 'userland'). +/// Such programs are not part of the kernel and are usually +/// subject to paging and swapping by the kernel while it does other things besides running your program. +/// This means that you should not expect to get real-time performance or +/// real-time timing constraints from such programs. In particular, there is no guarantee that the +/// bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. +/// In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times +/// than the one you asked for. So please dont expect to get exactly the time delay you request. +/// +/// Arjan reports that you can prevent swapping on Linux with the following code fragment: +/// +/// \code +/// struct sched_param sp; +/// memset(&sp, 0, sizeof(sp)); +/// sp.sched_priority = sched_get_priority_max(SCHED_FIFO); +/// sched_setscheduler(0, SCHED_FIFO, &sp); +/// mlockall(MCL_CURRENT | MCL_FUTURE); +/// \endcode +/// +/// \par Bindings to other languages +/// +/// mikem has made Perl bindings available at CPAN: +/// http://search.cpan.org/~mikem/Device-BCM2835-1.9/lib/Device/BCM2835.pm +/// Matthew Baker has kindly made Python bindings available at: +/// https://github.com/mubeta06/py-libbcm2835 +/// Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility +/// for Raspberry Pi, based on the bcm2835 library. The +/// utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as a +/// free download with source included: http://ipsolutionscorp.com/raspberry-pi-spi-utility/ +/// +/// \par Open Source Licensing GPL V2 +/// +/// This is the appropriate option if you want to share the source code of your +/// application with everyone you distribute it to, and you also want to give them +/// 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 +/// +/// \par Acknowledgements +/// +/// Some of this code has been inspired by Dom and Gert. +/// The I2C code has been inspired by Alan Barr. +/// +/// \par Revision History +/// +/// \version 1.0 Initial release +/// \version 1.1 Minor bug fixes +/// \version 1.2 Added support for SPI +/// \version 1.3 Added bcm2835_spi_transfern() +/// \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson. +/// \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz +/// \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 +/// Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() +/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now +/// changes only the pin specified. Other pins that were already previously +/// enabled stay enabled. +/// Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() +/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() +/// to clear the enable for individual pins, suggested by Andreas Sundstrom. +/// \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write. +/// \version 1.8 Improvements to read barrier, as suggested by maddin. +/// \version 1.9 Improvements contributed by mikew: +/// I noticed that it was mallocing memory for the mmaps on /dev/mem. +/// It's not necessary to do that, you can just mmap the file directly, +/// so I've removed the mallocs (and frees). +/// I've also modified delayMicroseconds() to use nanosleep() for long waits, +/// and a busy wait on a high resolution timer for the rest. This is because +/// I've found that calling nanosleep() takes at least 100-200 us. +/// You need to link using '-lrt' using this version. +/// I've added some unsigned casts to the debug prints to silence compiler +/// warnings I was getting, fixed some typos, and changed the value of +/// BCM2835_PAD_HYSTERESIS_ENABLED to 0x08 as per Gert van Loo's doc at +/// http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// Also added a define for the passwrd value that Gert says is needed to +/// change pad control settings. +/// \version 1.10 Changed the names of the delay functions to bcm2835_delay() +/// and bcm2835_delayMicroseconds() to prevent collisions with wiringPi. +/// Macros to map delay()-> bcm2835_delay() and +/// Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which +/// can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY +/// \version 1.11 Fixed incorrect link to download file +/// \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) +/// \version 1.13 New GPIO pin definitions for RPi version 2 plug P5 +/// Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio +/// bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0. +/// \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead. +/// Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. +/// Reported by Ben Simpson. +/// \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe. +/// \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi +/// to allow a mask of pins to be set all at once. Requested by Sebastian Loncar. +/// \version 1.17 Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar. +/// \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: +/// now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. +/// No need to link with -lrt now. Contributed by Arjan van Vught. +/// \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. +/// Reported by olly. +/// \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted. +/// \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. +/// Patch courtesy Jeremy Mortis. +/// \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set +/// falling edge events. Reported by Mark Dootson. +/// \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. +/// Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions +/// to fix ocasional reads not completing. Patched by Mark Dootson. +/// \version 1.24 Mark Dootson p[atched a problem with his previously submitted code +/// under high load from other processes. +/// \version 1.25 Updated author and distribution location details to airspayce.com +/// \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. +/// Reported by Hartmut Henkel. +/// \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. +/// \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. +/// \version 1.29 Further fix to SPI from Peter Würtz. +/// \version 1.30 10 microsecond delays from bcm2835_spi_transfer and bcm2835_spi_transfern for +/// significant performance improvements, Patch by Alan Watson. +/// \version 1.31 Fix a GCC warning about dummy variable, patched by Alan Watson. Thanks. +/// \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. +/// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. +/// Contributed by Malcolm Wiles based on work by Arvi Govindaraj. +/// \version 1.33 Added command line utilities i2c and gpio to examples. Contributed by Shahrooz Shahparnia. +/// \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, +/// sends a repeat start, and reads from the device. Contributed by Eduardo Steinhorst. +/// \version 1.35 Fix build errors when compiled under Qt. Also performance improvements with SPI transfers. Contributed by Udo Klaas. +/// \version 1.36 Make automake's test runner detect that we're skipping tests when not root, the second +/// one makes us skip the test when using fakeroot (as used when building +/// Debian packages). Contributed by Guido Günther. +/// +/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS + + + +// Defines for BCM2835 +#ifndef BCM2835_H +#define BCM2835_H + +#include + +/// \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. +/// @{ + + +/// This means pin HIGH, true, 3.3volts on a pin. +#define HIGH 0x1 +/// This means pin LOW, false, 0volts on a pin. +#define LOW 0x0 + +/// Speed of the core clock core_clk +#define BCM2835_CORE_CLK_HZ 250000000 ///< 250 MHz + +// Physical addresses for various peripheral register sets +/// Base Physical Address of the BCM 2835 peripheral registers +#define BCM2835_PERI_BASE 0x20000000 +/// Base Physical Address of the System Timer registers +#define BCM2835_ST_BASE (BCM2835_PERI_BASE + 0x3000) +/// Base Physical Address of the Pads registers +#define BCM2835_GPIO_PADS (BCM2835_PERI_BASE + 0x100000) +/// Base Physical Address of the Clock/timer registers +#define BCM2835_CLOCK_BASE (BCM2835_PERI_BASE + 0x101000) +/// Base Physical Address of the GPIO registers +#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) +/// Base Physical Address of the SPI0 registers +#define BCM2835_SPI0_BASE (BCM2835_PERI_BASE + 0x204000) +/// Base Physical Address of the BSC0 registers +#define BCM2835_BSC0_BASE (BCM2835_PERI_BASE + 0x205000) +/// Base Physical Address of the PWM registers +#define BCM2835_GPIO_PWM (BCM2835_PERI_BASE + 0x20C000) + /// Base Physical Address of the BSC1 registers +#define BCM2835_BSC1_BASE (BCM2835_PERI_BASE + 0x804000) + + +/// Base of the ST (System Timer) registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_st; + +/// Base of the GPIO registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_gpio; + +/// Base of the PWM registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pwm; + +/// Base of the CLK registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_clk; + +/// Base of the PADS registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pads; + +/// Base of the SPI0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_spi0; + +/// Base of the BSC0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc0; + +/// Base of the BSC1 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc1; + +/// Size of memory page on RPi +#define BCM2835_PAGE_SIZE (4*1024) +/// Size of memory block on RPi +#define BCM2835_BLOCK_SIZE (4*1024) + + +// Defines for GPIO +// The BCM2835 has 54 GPIO pins. +// BCM2835 data sheet, Page 90 onwards. +/// GPIO register offsets from BCM2835_GPIO_BASE. Offsets into the GPIO Peripheral block in bytes per 6.1 Register View +#define BCM2835_GPFSEL0 0x0000 ///< GPIO Function Select 0 +#define BCM2835_GPFSEL1 0x0004 ///< GPIO Function Select 1 +#define BCM2835_GPFSEL2 0x0008 ///< GPIO Function Select 2 +#define BCM2835_GPFSEL3 0x000c ///< GPIO Function Select 3 +#define BCM2835_GPFSEL4 0x0010 ///< GPIO Function Select 4 +#define BCM2835_GPFSEL5 0x0014 ///< GPIO Function Select 5 +#define BCM2835_GPSET0 0x001c ///< GPIO Pin Output Set 0 +#define BCM2835_GPSET1 0x0020 ///< GPIO Pin Output Set 1 +#define BCM2835_GPCLR0 0x0028 ///< GPIO Pin Output Clear 0 +#define BCM2835_GPCLR1 0x002c ///< GPIO Pin Output Clear 1 +#define BCM2835_GPLEV0 0x0034 ///< GPIO Pin Level 0 +#define BCM2835_GPLEV1 0x0038 ///< GPIO Pin Level 1 +#define BCM2835_GPEDS0 0x0040 ///< GPIO Pin Event Detect Status 0 +#define BCM2835_GPEDS1 0x0044 ///< GPIO Pin Event Detect Status 1 +#define BCM2835_GPREN0 0x004c ///< GPIO Pin Rising Edge Detect Enable 0 +#define BCM2835_GPREN1 0x0050 ///< GPIO Pin Rising Edge Detect Enable 1 +#define BCM2835_GPFEN0 0x0058 ///< GPIO Pin Falling Edge Detect Enable 0 +#define BCM2835_GPFEN1 0x005c ///< GPIO Pin Falling Edge Detect Enable 1 +#define BCM2835_GPHEN0 0x0064 ///< GPIO Pin High Detect Enable 0 +#define BCM2835_GPHEN1 0x0068 ///< GPIO Pin High Detect Enable 1 +#define BCM2835_GPLEN0 0x0070 ///< GPIO Pin Low Detect Enable 0 +#define BCM2835_GPLEN1 0x0074 ///< GPIO Pin Low Detect Enable 1 +#define BCM2835_GPAREN0 0x007c ///< GPIO Pin Async. Rising Edge Detect 0 +#define BCM2835_GPAREN1 0x0080 ///< GPIO Pin Async. Rising Edge Detect 1 +#define BCM2835_GPAFEN0 0x0088 ///< GPIO Pin Async. Falling Edge Detect 0 +#define BCM2835_GPAFEN1 0x008c ///< GPIO Pin Async. Falling Edge Detect 1 +#define BCM2835_GPPUD 0x0094 ///< GPIO Pin Pull-up/down Enable +#define BCM2835_GPPUDCLK0 0x0098 ///< GPIO Pin Pull-up/down Enable Clock 0 +#define BCM2835_GPPUDCLK1 0x009c ///< GPIO Pin Pull-up/down Enable Clock 1 + +/// \brief bcm2835PortFunction +/// Port function select modes for bcm2835_gpio_fsel() +typedef enum +{ + BCM2835_GPIO_FSEL_INPT = 0b000, ///< Input + BCM2835_GPIO_FSEL_OUTP = 0b001, ///< Output + BCM2835_GPIO_FSEL_ALT0 = 0b100, ///< Alternate function 0 + BCM2835_GPIO_FSEL_ALT1 = 0b101, ///< Alternate function 1 + BCM2835_GPIO_FSEL_ALT2 = 0b110, ///< Alternate function 2 + BCM2835_GPIO_FSEL_ALT3 = 0b111, ///< Alternate function 3 + BCM2835_GPIO_FSEL_ALT4 = 0b011, ///< Alternate function 4 + BCM2835_GPIO_FSEL_ALT5 = 0b010, ///< Alternate function 5 + BCM2835_GPIO_FSEL_MASK = 0b111 ///< Function select bits mask +} bcm2835FunctionSelect; + +/// \brief bcm2835PUDControl +/// Pullup/Pulldown defines for bcm2835_gpio_pud() +typedef enum +{ + BCM2835_GPIO_PUD_OFF = 0b00, ///< Off ? disable pull-up/down + BCM2835_GPIO_PUD_DOWN = 0b01, ///< Enable Pull Down control + BCM2835_GPIO_PUD_UP = 0b10 ///< Enable Pull Up control +} bcm2835PUDControl; + +/// 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 +#define BCM2835_PADS_GPIO_46_53 0x0034 ///< Pad control register for pads 46 to 53 + +/// Pad Control masks +#define BCM2835_PAD_PASSWRD (0x5A << 24) ///< Password to enable setting pad mask +#define BCM2835_PAD_SLEW_RATE_UNLIMITED 0x10 ///< Slew rate unlimited +#define BCM2835_PAD_HYSTERESIS_ENABLED 0x08 ///< Hysteresis enabled +#define BCM2835_PAD_DRIVE_2mA 0x00 ///< 2mA drive current +#define BCM2835_PAD_DRIVE_4mA 0x01 ///< 4mA drive current +#define BCM2835_PAD_DRIVE_6mA 0x02 ///< 6mA drive current +#define BCM2835_PAD_DRIVE_8mA 0x03 ///< 8mA drive current +#define BCM2835_PAD_DRIVE_10mA 0x04 ///< 10mA drive current +#define BCM2835_PAD_DRIVE_12mA 0x05 ///< 12mA drive current +#define BCM2835_PAD_DRIVE_14mA 0x06 ///< 14mA drive current +#define BCM2835_PAD_DRIVE_16mA 0x07 ///< 16mA drive current + +/// \brief bcm2835PadGroup +/// Pad group specification for bcm2835_gpio_pad() +typedef enum +{ + BCM2835_PAD_GROUP_GPIO_0_27 = 0, ///< Pad group for GPIO pads 0 to 27 + BCM2835_PAD_GROUP_GPIO_28_45 = 1, ///< Pad group for GPIO pads 28 to 45 + BCM2835_PAD_GROUP_GPIO_46_53 = 2 ///< Pad group for GPIO pads 46 to 53 +} bcm2835PadGroup; + +/// \brief GPIO Pin Numbers +/// +/// Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers. +/// These can be passed as a pin number to any function requiring a pin. +/// Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins +/// and some can adopt an alternate function. +/// RPi version 2 has some slightly different pinouts, and these are values RPI_V2_*. +/// At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively +/// When SPI0 is in use (ie after bcm2835_spi_begin()), pins 19, 21, 23, 24, 26 are dedicated to SPI +/// and cant be controlled independently +typedef enum +{ + RPI_GPIO_P1_03 = 0, ///< Version 1, Pin P1-03 + RPI_GPIO_P1_05 = 1, ///< Version 1, Pin P1-05 + RPI_GPIO_P1_07 = 4, ///< Version 1, Pin P1-07 + RPI_GPIO_P1_08 = 14, ///< Version 1, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_GPIO_P1_10 = 15, ///< Version 1, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_GPIO_P1_11 = 17, ///< Version 1, Pin P1-11 + RPI_GPIO_P1_12 = 18, ///< Version 1, Pin P1-12, can be PWM channel 0 in ALT FUN 5 + RPI_GPIO_P1_13 = 21, ///< Version 1, Pin P1-13 + RPI_GPIO_P1_15 = 22, ///< Version 1, Pin P1-15 + RPI_GPIO_P1_16 = 23, ///< Version 1, Pin P1-16 + RPI_GPIO_P1_18 = 24, ///< Version 1, Pin P1-18 + RPI_GPIO_P1_19 = 10, ///< Version 1, Pin P1-19, MOSI when SPI0 in use + RPI_GPIO_P1_21 = 9, ///< Version 1, Pin P1-21, MISO when SPI0 in use + RPI_GPIO_P1_22 = 25, ///< Version 1, Pin P1-22 + RPI_GPIO_P1_23 = 11, ///< Version 1, Pin P1-23, CLK when SPI0 in use + RPI_GPIO_P1_24 = 8, ///< Version 1, Pin P1-24, CE0 when SPI0 in use + RPI_GPIO_P1_26 = 7, ///< Version 1, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2 + RPI_V2_GPIO_P1_03 = 2, ///< Version 2, Pin P1-03 + RPI_V2_GPIO_P1_05 = 3, ///< Version 2, Pin P1-05 + RPI_V2_GPIO_P1_07 = 4, ///< Version 2, Pin P1-07 + RPI_V2_GPIO_P1_08 = 14, ///< Version 2, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_V2_GPIO_P1_10 = 15, ///< Version 2, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_V2_GPIO_P1_11 = 17, ///< Version 2, Pin P1-11 + RPI_V2_GPIO_P1_12 = 18, ///< Version 2, Pin P1-12, can be PWM channel 0 in ALT FUN 5 + RPI_V2_GPIO_P1_13 = 27, ///< Version 2, Pin P1-13 + RPI_V2_GPIO_P1_15 = 22, ///< Version 2, Pin P1-15 + RPI_V2_GPIO_P1_16 = 23, ///< Version 2, Pin P1-16 + RPI_V2_GPIO_P1_18 = 24, ///< Version 2, Pin P1-18 + RPI_V2_GPIO_P1_19 = 10, ///< Version 2, Pin P1-19, MOSI when SPI0 in use + RPI_V2_GPIO_P1_21 = 9, ///< Version 2, Pin P1-21, MISO when SPI0 in use + RPI_V2_GPIO_P1_22 = 25, ///< Version 2, Pin P1-22 + RPI_V2_GPIO_P1_23 = 11, ///< Version 2, Pin P1-23, CLK when SPI0 in use + RPI_V2_GPIO_P1_24 = 8, ///< Version 2, Pin P1-24, CE0 when SPI0 in use + RPI_V2_GPIO_P1_26 = 7, ///< Version 2, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2, new plug P5 + RPI_V2_GPIO_P5_03 = 28, ///< Version 2, Pin P5-03 + RPI_V2_GPIO_P5_04 = 29, ///< Version 2, Pin P5-04 + RPI_V2_GPIO_P5_05 = 30, ///< Version 2, Pin P5-05 + RPI_V2_GPIO_P5_06 = 31, ///< Version 2, Pin P5-06 + +} RPiGPIOPin; + +// Defines for SPI +// GPIO register offsets from BCM2835_SPI0_BASE. +// Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map +#define BCM2835_SPI0_CS 0x0000 ///< SPI Master Control and Status +#define BCM2835_SPI0_FIFO 0x0004 ///< SPI Master TX and RX FIFOs +#define BCM2835_SPI0_CLK 0x0008 ///< SPI Master Clock Divider +#define BCM2835_SPI0_DLEN 0x000c ///< SPI Master Data Length +#define BCM2835_SPI0_LTOH 0x0010 ///< SPI LOSSI mode TOH +#define BCM2835_SPI0_DC 0x0014 ///< SPI DMA DREQ Controls + +// Register masks for SPI0_CS +#define BCM2835_SPI0_CS_LEN_LONG 0x02000000 ///< Enable Long data word in Lossi mode if DMA_LEN is set +#define BCM2835_SPI0_CS_DMA_LEN 0x01000000 ///< Enable DMA mode in Lossi mode +#define BCM2835_SPI0_CS_CSPOL2 0x00800000 ///< Chip Select 2 Polarity +#define BCM2835_SPI0_CS_CSPOL1 0x00400000 ///< Chip Select 1 Polarity +#define BCM2835_SPI0_CS_CSPOL0 0x00200000 ///< Chip Select 0 Polarity +#define BCM2835_SPI0_CS_RXF 0x00100000 ///< RXF - RX FIFO Full +#define BCM2835_SPI0_CS_RXR 0x00080000 ///< RXR RX FIFO needs Reading ( full) +#define BCM2835_SPI0_CS_TXD 0x00040000 ///< TXD TX FIFO can accept Data +#define BCM2835_SPI0_CS_RXD 0x00020000 ///< RXD RX FIFO contains Data +#define BCM2835_SPI0_CS_DONE 0x00010000 ///< Done transfer Done +#define BCM2835_SPI0_CS_TE_EN 0x00008000 ///< Unused +#define BCM2835_SPI0_CS_LMONO 0x00004000 ///< Unused +#define BCM2835_SPI0_CS_LEN 0x00002000 ///< LEN LoSSI enable +#define BCM2835_SPI0_CS_REN 0x00001000 ///< REN Read Enable +#define BCM2835_SPI0_CS_ADCS 0x00000800 ///< ADCS Automatically Deassert Chip Select +#define BCM2835_SPI0_CS_INTR 0x00000400 ///< INTR Interrupt on RXR +#define BCM2835_SPI0_CS_INTD 0x00000200 ///< INTD Interrupt on Done +#define BCM2835_SPI0_CS_DMAEN 0x00000100 ///< DMAEN DMA Enable +#define BCM2835_SPI0_CS_TA 0x00000080 ///< Transfer Active +#define BCM2835_SPI0_CS_CSPOL 0x00000040 ///< Chip Select Polarity +#define BCM2835_SPI0_CS_CLEAR 0x00000030 ///< Clear FIFO Clear RX and TX +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX +#define BCM2835_SPI0_CS_CPOL 0x00000008 ///< Clock Polarity +#define BCM2835_SPI0_CS_CPHA 0x00000004 ///< Clock Phase +#define BCM2835_SPI0_CS_CS 0x00000003 ///< Chip Select + +/// \brief bcm2835SPIBitOrder SPI Bit order +/// Specifies the SPI data bit ordering for bcm2835_spi_setBitOrder() +typedef enum +{ + BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, ///< LSB First + BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 ///< MSB First +}bcm2835SPIBitOrder; + +/// \brief SPI Data mode +/// Specify the SPI data mode to be passed to bcm2835_spi_setDataMode() +typedef enum +{ + BCM2835_SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0 + BCM2835_SPI_MODE1 = 1, ///< CPOL = 0, CPHA = 1 + BCM2835_SPI_MODE2 = 2, ///< CPOL = 1, CPHA = 0 + BCM2835_SPI_MODE3 = 3, ///< CPOL = 1, CPHA = 1 +}bcm2835SPIMode; + +/// \brief bcm2835SPIChipSelect +/// Specify the SPI chip select pin(s) +typedef enum +{ + // Can also use non-standard pins with this lib + BCM2835_SPI_CS0 = 0, ///< Chip Select 0 + BCM2835_SPI_CS1 = 1, ///< Chip Select 1 + BCM2835_SPI_CS2 = 2, ///< Chip Select 2 (ie pins CS1 and CS2 are asserted) + BCM2835_SPI_CS_NONE = 3, ///< No CS, control it yourself + + // Only GPIO > 3 can be used (to not interfere with the previous value just above ) + // Lucky we have plenty of theese pins + BCM2835_SPI_CS_GPIO4 = RPI_V2_GPIO_P1_07, /// BCM GPIO 4 + BCM2835_SPI_CS_GPIO17 = RPI_V2_GPIO_P1_11, /// BCM GPIO 17 + BCM2835_SPI_CS_GPIO18 = RPI_V2_GPIO_P1_12, /// BCM GPIO 18 + BCM2835_SPI_CS_GPIO22 = RPI_V2_GPIO_P1_15, /// BCM GPIO 22 + BCM2835_SPI_CS_GPIO23 = RPI_V2_GPIO_P1_16, /// BCM GPIO 23 + BCM2835_SPI_CS_GPIO24 = RPI_V2_GPIO_P1_18, /// BCM GPIO 24 + BCM2835_SPI_CS_GPIO25 = RPI_V2_GPIO_P1_22, /// BCM GPIO 25 + BCM2835_SPI_CS_GPIO28 = RPI_V2_GPIO_P5_03, /// BCM GPIO 28 + BCM2835_SPI_CS_GPIO29 = RPI_V2_GPIO_P5_04, /// BCM GPIO 29 + BCM2835_SPI_CS_GPIO30 = RPI_V2_GPIO_P5_05, /// BCM GPIO 30 + BCM2835_SPI_CS_GPIO31 = RPI_V2_GPIO_P5_06, /// BCM GPIO 31 + +} bcm2835SPIChipSelect; + +/// \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 +/// It is reported that (contrary to the documentation) any even divider may used. +/// The frequencies shown for each divider have been confirmed by measurement +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.5625MHz + 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 +} bcm2835SPIClockDivider; + +/// \brief bcm2835SPISpeed +/// Specifies the divider used to generate the SPI clock from the system clock. +/// Figures below give the clock speed instead of clock divider. +#define BCM2835_SPI_SPEED_64MHZ BCM2835_SPI_CLOCK_DIVIDER_4 +#define BCM2835_SPI_SPEED_32MHZ BCM2835_SPI_CLOCK_DIVIDER_8 +#define BCM2835_SPI_SPEED_16MHZ BCM2835_SPI_CLOCK_DIVIDER_16 +#define BCM2835_SPI_SPEED_8MHZ BCM2835_SPI_CLOCK_DIVIDER_32 +#define BCM2835_SPI_SPEED_4MHZ BCM2835_SPI_CLOCK_DIVIDER_64 +#define BCM2835_SPI_SPEED_2MHZ BCM2835_SPI_CLOCK_DIVIDER_128 +#define BCM2835_SPI_SPEED_1MHZ BCM2835_SPI_CLOCK_DIVIDER_256 +#define BCM2835_SPI_SPEED_512KHZ BCM2835_SPI_CLOCK_DIVIDER_512 +#define BCM2835_SPI_SPEED_256KHZ BCM2835_SPI_CLOCK_DIVIDER_1024 +#define BCM2835_SPI_SPEED_128KHZ BCM2835_SPI_CLOCK_DIVIDER_2048 +#define BCM2835_SPI_SPEED_64KHZ BCM2835_SPI_CLOCK_DIVIDER_4096 +#define BCM2835_SPI_SPEED_32KHZ BCM2835_SPI_CLOCK_DIVIDER_8192 +#define BCM2835_SPI_SPEED_16KHZ BCM2835_SPI_CLOCK_DIVIDER_16384 +#define BCM2835_SPI_SPEED_8KHZ BCM2835_SPI_CLOCK_DIVIDER_32768 + +// Defines for I2C +// GPIO register offsets from BCM2835_BSC*_BASE. +// Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map +#define BCM2835_BSC_C 0x0000 ///< BSC Master Control +#define BCM2835_BSC_S 0x0004 ///< BSC Master Status +#define BCM2835_BSC_DLEN 0x0008 ///< BSC Master Data Length +#define BCM2835_BSC_A 0x000c ///< BSC Master Slave Address +#define BCM2835_BSC_FIFO 0x0010 ///< BSC Master Data FIFO +#define BCM2835_BSC_DIV 0x0014 ///< BSC Master Clock Divider +#define BCM2835_BSC_DEL 0x0018 ///< BSC Master Data Delay +#define BCM2835_BSC_CLKT 0x001c ///< BSC Master Clock Stretch Timeout + +// Register masks for BSC_C +#define BCM2835_BSC_C_I2CEN 0x00008000 ///< I2C Enable, 0 = disabled, 1 = enabled +#define BCM2835_BSC_C_INTR 0x00000400 ///< Interrupt on RX +#define BCM2835_BSC_C_INTT 0x00000200 ///< Interrupt on TX +#define BCM2835_BSC_C_INTD 0x00000100 ///< Interrupt on DONE +#define BCM2835_BSC_C_ST 0x00000080 ///< Start transfer, 1 = Start a new transfer +#define BCM2835_BSC_C_CLEAR_1 0x00000020 ///< Clear FIFO Clear +#define BCM2835_BSC_C_CLEAR_2 0x00000010 ///< Clear FIFO Clear +#define BCM2835_BSC_C_READ 0x00000001 ///< Read transfer + +// Register masks for BSC_S +#define BCM2835_BSC_S_CLKT 0x00000200 ///< Clock stretch timeout +#define BCM2835_BSC_S_ERR 0x00000100 ///< ACK error +#define BCM2835_BSC_S_RXF 0x00000080 ///< RXF FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_TXE 0x00000040 ///< TXE FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_RXD 0x00000020 ///< RXD FIFO contains data +#define BCM2835_BSC_S_TXD 0x00000010 ///< TXD FIFO can accept data +#define BCM2835_BSC_S_RXR 0x00000008 ///< RXR FIFO needs reading (full) +#define BCM2835_BSC_S_TXW 0x00000004 ///< TXW FIFO needs writing (full) +#define BCM2835_BSC_S_DONE 0x00000002 ///< Transfer DONE +#define BCM2835_BSC_S_TA 0x00000001 ///< Transfer Active + +#define BCM2835_BSC_FIFO_SIZE 16 ///< BSC FIFO size + +/// \brief bcm2835I2CClockDivider +/// Specifies the divider used to generate the I2C clock from the system clock. +/// Clock divided is based on nominal base clock rate of 250MHz +typedef enum +{ + BCM2835_I2C_CLOCK_DIVIDER_2500 = 2500, ///< 2500 = 10us = 100 kHz + BCM2835_I2C_CLOCK_DIVIDER_626 = 626, ///< 622 = 2.504us = 399.3610 kHz + BCM2835_I2C_CLOCK_DIVIDER_150 = 150, ///< 150 = 60ns = 1.666 MHz (default at reset) + BCM2835_I2C_CLOCK_DIVIDER_148 = 148, ///< 148 = 59ns = 1.689 MHz +} bcm2835I2CClockDivider; + +/// \brief bcm2835I2CReasonCodes +/// Specifies the reason codes for the bcm2835_i2c_write and bcm2835_i2c_read functions. +typedef enum +{ + BCM2835_I2C_REASON_OK = 0x00, ///< Success + BCM2835_I2C_REASON_ERROR_NACK = 0x01, ///< Received a NACK + BCM2835_I2C_REASON_ERROR_CLKT = 0x02, ///< Received Clock Stretch Timeout + BCM2835_I2C_REASON_ERROR_DATA = 0x04, ///< Not all data is sent / received +} bcm2835I2CReasonCodes; + +// Defines for ST +// GPIO register offsets from BCM2835_ST_BASE. +// Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers +// The System Timer peripheral provides four 32-bit timer channels and a single 64-bit free running counter. +// BCM2835_ST_CLO is the System Timer Counter Lower bits register. +// The system timer free-running counter lower register is a read-only register that returns the current value +// of the lower 32-bits of the free running counter. +// BCM2835_ST_CHI is the System Timer Counter Upper bits register. +// The system timer free-running counter upper register is a read-only register that returns the current value +// of the upper 32-bits of the free running counter. +#define BCM2835_ST_CS 0x0000 ///< System Timer Control/Status +#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 +#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 +#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 +#define BCM2835_PWM1_USEFIFO 0x2000 ///< Data from FIFO +#define BCM2835_PWM1_REVPOLAR 0x1000 ///< Reverse polarity +#define BCM2835_PWM1_OFFSTATE 0x0800 ///< Ouput Off state +#define BCM2835_PWM1_REPEATFF 0x0400 ///< Repeat last value if FIFO empty +#define BCM2835_PWM1_SERIAL 0x0200 ///< Run in serial mode +#define BCM2835_PWM1_ENABLE 0x0100 ///< Channel Enable + +#define BCM2835_PWM0_MS_MODE 0x0080 ///< Run in Mark/Space mode +#define BCM2835_PWM_CLEAR_FIFO 0x0040 ///< Clear FIFO +#define BCM2835_PWM0_USEFIFO 0x0020 ///< Data from FIFO +#define BCM2835_PWM0_REVPOLAR 0x0010 ///< Reverse polarity +#define BCM2835_PWM0_OFFSTATE 0x0008 ///< Ouput Off state +#define BCM2835_PWM0_REPEATFF 0x0004 ///< Repeat last value if FIFO empty +#define BCM2835_PWM0_SERIAL 0x0002 ///< Run in serial mode +#define BCM2835_PWM0_ENABLE 0x0001 ///< Channel Enable + + +/// \brief bcm2835PWMClockDivider +/// Specifies the divider used to generate the PWM clock from the system clock. +/// Figures below give the divider, clock period and clock frequency. +/// Clock divided is based on nominal PWM base clock rate of 19.2MHz +/// The frequencies shown for each divider have been confirmed by measurement +typedef enum +{ + BCM2835_PWM_CLOCK_DIVIDER_32768 = 32768, ///< 32768 = 585Hz + BCM2835_PWM_CLOCK_DIVIDER_16384 = 16384, ///< 16384 = 1171.8Hz + BCM2835_PWM_CLOCK_DIVIDER_8192 = 8192, ///< 8192 = 2.34375kHz + BCM2835_PWM_CLOCK_DIVIDER_4096 = 4096, ///< 4096 = 4.6875kHz + BCM2835_PWM_CLOCK_DIVIDER_2048 = 2048, ///< 2048 = 9.375kHz + BCM2835_PWM_CLOCK_DIVIDER_1024 = 1024, ///< 1024 = 18.75kHz + BCM2835_PWM_CLOCK_DIVIDER_512 = 512, ///< 512 = 37.5kHz + BCM2835_PWM_CLOCK_DIVIDER_256 = 256, ///< 256 = 75kHz + BCM2835_PWM_CLOCK_DIVIDER_128 = 128, ///< 128 = 150kHz + BCM2835_PWM_CLOCK_DIVIDER_64 = 64, ///< 64 = 300kHz + BCM2835_PWM_CLOCK_DIVIDER_32 = 32, ///< 32 = 600.0kHz + BCM2835_PWM_CLOCK_DIVIDER_16 = 16, ///< 16 = 1.2MHz + BCM2835_PWM_CLOCK_DIVIDER_8 = 8, ///< 8 = 2.4MHz + BCM2835_PWM_CLOCK_DIVIDER_4 = 4, ///< 4 = 4.8MHz + BCM2835_PWM_CLOCK_DIVIDER_2 = 2, ///< 2 = 9.6MHz, fastest you can get + 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) +#define delayMicroseconds(x) bcm2835_delayMicroseconds(x) +#define millis() bcm2835_millis() +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /// \defgroup init Library initialisation and management + /// These functions allow you to intialise and control the bcm2835 library + /// @{ + + /// Initialise the library by opening /dev/mem and getting pointers to the + /// internal memory for BCM 2835 device registers. You must call this (successfully) + /// before calling any other + /// functions in this library (except bcm2835_set_debug). + /// If bcm2835_init() fails by returning 0, + /// calling any other function may result in crashes or other failures. + /// Prints messages to stderr in case of errors. + /// \return 1 if successful else 0 + extern int bcm2835_init(void); + + /// Close the library, deallocating any allocated memory and closing /dev/mem + /// \return 1 if successful else 0 + extern int bcm2835_close(void); + + /// Sets the debug level of the library. + /// A value of 1 prevents mapping to /dev/mem, and makes the library print out + /// what it would do, rather than accessing the GPIO registers. + /// A value of 0, the default, causes normal operation. + /// Call this before calling bcm2835_init(); + /// \param[in] debug The new debug level. 1 means debug + extern void bcm2835_set_debug(uint8_t debug); + + /// @} // end of init + + /// \defgroup lowlevel Low level register access + /// These functions provide low level register access, and should not generally + /// need to be used + /// + /// @{ + + /// Reads 32 bit value from a peripheral address + /// The read is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr); + + + /// Reads 32 bit value from a peripheral address without the read barrier + /// You should only use this when your code has previously called bcm2835_peri_read() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr); + + + /// Writes 32 bit value from a peripheral address + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value); + + /// Writes 32 bit value from a peripheral address without the write barrier + /// You should only use this when your code has previously called bcm2835_peri_write() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); + + /// Alters a number of bits in a 32 peripheral regsiter. + /// It reads the current valu and then alters the bits deines as 1 in mask, + /// according to the bit value in value. + /// All other bits that are 0 in the mask are unaffected. + /// Use this to alter a subset of the bits in a register. + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write, masked in by mask. + /// \param[in] mask Bitmask that defines the bits that will be altered in the register. + /// \sa Physical Addresses + extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); + /// @} // end of lowlevel + + /// \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. + /// @{ + + /// Sets the Function Select register for the given pin, which configures + /// the pin as Input, Output or one of the 6 alternate functions. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect + extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); + + /// Sets the specified pin output to + /// HIGH. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_set(uint8_t pin); + + /// Sets the specified pin output to + /// LOW. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_clr(uint8_t pin); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// HIGH. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_set_multi(uint32_t mask); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// LOW. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_clr_multi(uint32_t mask); + + /// Reads the current level on the specified + /// pin and returns either HIGH or LOW. Works whether or not the pin + /// is an input or an output. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return the current level either HIGH or LOW + extern uint8_t bcm2835_gpio_lev(uint8_t pin); + + /// Event Detect Status. + /// Tests whether the specified pin has detected a level or edge + /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), + /// bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). + /// Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return HIGH if the event detect status for the given pin is true. + extern uint8_t bcm2835_gpio_eds(uint8_t pin); + + /// Sets the Event Detect Status register for a given pin to 1, + /// which has the effect of clearing the flag. Use this afer seeing + /// an Event Detect Status on the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_set_eds(uint8_t pin); + + /// Enable Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?011? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_ren(uint8_t pin); + + /// Disable Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_ren(uint8_t pin); + + /// Enable Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?100? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_fen(uint8_t pin); + + /// Disable Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_fen(uint8_t pin); + + /// Enable High Detect Enable for the specified pin. + /// When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_hen(uint8_t pin); + + /// Disable High Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_hen(uint8_t pin); + + /// Enable Low Detect Enable for the specified pin. + /// When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_len(uint8_t pin); + + /// Disable Low Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_len(uint8_t pin); + + /// Enable Asynchronous Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// rising edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_aren(uint8_t pin); + + /// Disable Asynchronous Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_aren(uint8_t pin); + + /// Enable Asynchronous Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// falling edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_afen(uint8_t pin); + + /// Disable Asynchronous Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_afen(uint8_t pin); + + /// Sets the Pull-up/down register for the given pin. This is + /// 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 + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pud(uint8_t pud); + + /// Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. + /// \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. + /// \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. + /// \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. + /// \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 + /// is automatically included. + extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); + + /// Delays for the specified number of milliseconds. + /// Uses nanosleep(), and therefore does not use CPU until the time is up. + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// \param[in] millis Delay in milliseconds + extern void bcm2835_delay (unsigned int millis); + + /// Delays for the specified number of microseconds. + /// Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers, + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// For times less than about 450 microseconds, uses a busy wait on the System Timer. + /// It is reported that a delay of 0 microseconds on RaspberryPi will in fact + /// result in a delay of about 80 microseconds. Your mileage may vary. + /// \param[in] micros Delay in microseconds + extern void bcm2835_delayMicroseconds (uint64_t micros); + + /// Indicate the number of milliseconds since startup of PI + /// This function is like the Arduino millis function + /// \return Number of milliseconds + extern unsigned int bcm2835_millis(void); + + /// Sets the output state of the specified pin + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write(uint8_t pin, uint8_t on); + + /// Sets any of the first 32 GPIO output pins specified in the mask to the state given by on + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on); + + /// Sets the first 32 GPIO output pins specified in the mask to the value given by value + /// \param[in] value values required for each bit masked in by mask, eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask); + + /// Sets the Pull-up/down mode for the specified pin. This is more convenient than + /// clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk(). + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); + + /// @} + + /// \defgroup spi SPI access + /// These functions let you use SPI0 (Serial Peripheral Interface) to + /// interface with an external SPI device. + /// @{ + + /// Start SPI operations. + /// Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// to alternate function ALT0, which enables those pins for SPI interface. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// by default PI hardware driven using CE0 + /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to + /// their default functions + /// \sa bcm2835_spi_end() + extern void bcm2835_spi_begin(); + + /// End SPI operations. + /// SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// are returned to their default INPUT behaviour. + extern void bcm2835_spi_end(void); + + /// Sets the SPI custom Chip Select pin to correct level + /// Defaults to + /// \param[in] level The desired level, LOW or HIGH, + extern void bcm2835_spi_setChipSelect(uint8_t level); + + /// Sets the SPI bit order + /// NOTE: has no effect. Not supported by SPI0. + /// Defaults to + /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, + /// see \ref bcm2835SPIBitOrder + extern void bcm2835_spi_setBitOrder(uint8_t order); + + /// Sets the SPI clock divider and therefore the + /// SPI clock speed. + /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, + /// see \ref bcm2835SPIClockDivider + extern void bcm2835_spi_setClockDivider(uint16_t divider); + + /// Sets the SPI data mode + /// Sets the clock polariy and phase + /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, + /// see \ref bcm2835SPIMode + extern void bcm2835_spi_setDataMode(uint8_t mode); + + /// Sets the chip select pin(s) + /// When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the + /// transfer. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + extern void bcm2835_spi_chipSelect(uint8_t cs); + + /// Sets the chip select pin polarity for a given pin + /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) + /// will be asserted to the + /// value given by active. When transfers are not happening, the chip select pin(s) + /// return to the complement (inactive) value. + /// \param[in] cs The chip select pin to affect + /// \param[in] active Whether the chip select pin is to be active HIGH + extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); + + /// Transfers one byte 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] value The 8 bit data byte to write to MOSI + /// \return The 8 bit byte simultaneously read from MISO + /// \sa bcm2835_spi_transfern() + extern uint8_t bcm2835_spi_transfer(uint8_t value); + + /// Transfers any number of bytes 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 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 + /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + /// \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 + /// \sa bcm2835_spi_transfer() + extern void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len); + + /// Transfers any number of bytes to and from the currently selected SPI slave + /// using bcm2835_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_spi_transfer() + extern void bcm2835_spi_transfern(char* buf, uint32_t len); + + /// Transfers any number of bytes to the currently selected SPI slave. + /// 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 tbuf buffer, and the number of bytes to send + extern void bcm2835_spi_writenb(char* buf, uint32_t len); + + /// @} + + /// \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. + /// @{ + + /// Start I2C operations. + /// Forces RPi I2C pins P1-03 (SDA) and P1-05 (SCL) + /// to alternate function ALT0, which enables those pins for I2C interface. + /// You should call bcm2835_i2c_end() when all I2C functions are complete to return the pins to + /// their default functions + /// \sa bcm2835_i2c_end() + extern void bcm2835_i2c_begin(void); + + /// End I2C operations. + /// I2C pins P1-03 (SDA) and P1-05 (SCL) + /// are returned to their default INPUT behaviour. + extern void bcm2835_i2c_end(void); + + /// Sets the I2C slave address. + /// \param[in] addr The I2C slave address. + extern void bcm2835_i2c_setSlaveAddress(uint8_t addr); + + /// Sets the I2C clock divider and therefore the I2C clock speed. + /// \param[in] divider The desired I2C clock divider, one of BCM2835_I2C_CLOCK_DIVIDER_*, + /// see \ref bcm2835I2CClockDivider + extern void bcm2835_i2c_setClockDivider(uint16_t divider); + + /// Sets the I2C clock divider by converting the baudrate parameter to + /// the equivalent I2C clock divider. ( see \sa bcm2835_i2c_setClockDivider) + /// For the I2C standard 100khz you would set baudrate to 100000 + /// The use of baudrate corresponds to its use in the I2C kernel device + /// driver. (Of course, bcm2835 has nothing to do with the kernel driver) + extern void bcm2835_i2c_set_baudrate(uint32_t baudrate); + + /// Transfers any number of bytes to the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \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. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_write(const char * buf, uint32_t len); + + /// Transfers any number of bytes from the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read(char* buf, uint32_t len); + + /// Allows reading from I2C slaves that require a repeated start (without any prior stop) + /// to read after the required slave register has been set. For example, the popular + /// MPL3115A2 pressure and temperature sensor. Note that your device must support or + /// require this mode. If your device does not require this mode then the standard + /// combined: + /// \sa bcm2835_i2c_write + /// \sa bcm2835_i2c_read + /// are a better choice. + /// Will read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + /// \param[in] regaddr Buffer containing the slave register you wish to read from. + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len); + + /// Allows sending an arbitrary number of bytes to I2C slaves before issuing a repeated + /// start (with no prior stop) and reading a response. + /// Necessary for devices that require such behavior, such as the MLX90620. + /// Will write to and read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + /// \param[in] cmds Buffer containing the bytes to send before the repeated start condition. + /// \param[in] cmds_len Number of bytes to send from cmds buffer + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] buf_len Number of bytes to receive in the buf buffer. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len); + + /// @} + + /// \defgroup st System Timer access + /// Allows access to and delays using the System Timer Counter. + /// @{ + + /// Read the System Timer Counter register. + /// \return the value read from the System Timer Counter Lower 32 bits register + extern uint64_t bcm2835_st_read(void); + + /// Delays for the specified number of microseconds with offset. + /// \param[in] offset_micros Offset in microseconds + /// \param[in] micros Delay in microseconds + extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); + + /// @} + + /// \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 + /// documentation on the Main Page. + /// @{ + + /// Sets the PWM clock divisor, + /// to control the basic PWM pulse widths. + /// \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common + /// values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider + extern void bcm2835_pwm_set_clock(uint32_t divisor); + + /// Sets the mode of the given PWM channel, + /// allowing you to control the PWM mode and enable/disable that channel + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode. + /// \param[in] enabled Set true to enable this channel and produce PWM pulses. + extern void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled); + + /// Sets the maximum range of the PWM output. + /// The data value can vary between 0 and this range to control PWM output + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] range The maximum value permitted for DATA. + extern void bcm2835_pwm_set_range(uint8_t channel, uint32_t range); + + /// Sets the PWM pulse ratio to emit to DATA/RANGE, + /// where RANGE is set by bcm2835_pwm_set_range(). + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] data Controls the PWM output ratio as a fraction of the range. + /// Can vary from 0 to RANGE. + extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data); + + /// @} +#ifdef __cplusplus +} +#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. diff --git a/libraries/RF24/RPi/RF24/examples/Makefile b/libraries/RF24/RPi/RF24/examples/Makefile new file mode 100644 index 0000000..73da217 --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/Makefile @@ -0,0 +1,40 @@ +############################################################################# +# +# Makefile for librf24 examples on Raspberry Pi +# +# License: GPL (General Public License) +# Author: gnulnulf +# Date: 2013/02/07 (version 1.0) +# +# Description: +# ------------ +# use make all and make install to install the examples +# You can change the install directory by editing the prefix line +# +prefix := /usr/local + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s +#CCFLAGS= + +# define all programs +#PROGRAMS = scanner pingtest gettingstarted +PROGRAMS = gettingstarted gettingstarted_call_response transfer pingpair_dyn +SOURCES = ${PROGRAMS:=.cpp} + +all: ${PROGRAMS} + +${PROGRAMS}: ${SOURCES} + g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ + +clean: + rm -rf $(PROGRAMS) + +install: all + test -d $(prefix) || mkdir $(prefix) + test -d $(prefix)/bin || mkdir $(prefix)/bin + for prog in $(PROGRAMS); do \ + install -m 0755 $$prog $(prefix)/bin; \ + done + +.PHONY: install diff --git a/libraries/RF24/RPi/RF24/examples/extra/Makefile b/libraries/RF24/RPi/RF24/examples/extra/Makefile new file mode 100644 index 0000000..c313b5b --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/extra/Makefile @@ -0,0 +1,40 @@ +############################################################################# +# +# Makefile for librf24 examples on Raspberry Pi +# +# License: GPL (General Public License) +# Author: gnulnulf +# Date: 2013/02/07 (version 1.0) +# +# Description: +# ------------ +# use make all and make install to install the examples +# You can change the install directory by editing the prefix line +# +prefix := /usr/local + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s +#CCFLAGS= + +# define all programs +PROGRAMS = rpi-hub scanner + +SOURCES = ${PROGRAMS:=.cpp} + +all: ${PROGRAMS} + +${PROGRAMS}: ${SOURCES} + g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ + +clean: + rm -rf $(PROGRAMS) + +install: all + test -d $(prefix) || mkdir $(prefix) + test -d $(prefix)/bin || mkdir $(prefix)/bin + for prog in $(PROGRAMS); do \ + install -m 0755 $$prog $(prefix)/bin; \ + done + +.PHONY: install diff --git a/libraries/RF24/RPi/RF24/examples/extra/rpi-hub.cpp b/libraries/RF24/RPi/RF24/examples/extra/rpi-hub.cpp new file mode 100644 index 0000000..4785148 --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/extra/rpi-hub.cpp @@ -0,0 +1,134 @@ +/* + * + * Filename : rpi-hub.cpp + * + * This program makes the RPi as a hub listening to all six pipes from the remote sensor nodes ( usually Arduino ) + * and will return the packet back to the sensor on pipe0 so that the sender can calculate the round trip delays + * when the payload matches. + * + * I encounter that at times, it also receive from pipe7 ( or pipe0 ) with content of FFFFFFFFF that I will not sent + * back to the sender + * + * Refer to RF24/examples/rpi_hub_arduino/ for the corresponding Arduino sketches to work with this code. + * + * + * CE is not used and CSN is GPIO25 (not pinout) + * + * Refer to RPi docs for GPIO numbers + * + * Author : Stanley Seow + * e-mail : stanleyseow@gmail.com + * date : 6th Mar 2013 + * + * 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + * Modified to use with Arduipi board http://hallard.me/arduipi + * Changed to use modified bcm2835 and RF24 library + * + * + */ + +#include +#include +#include "../RF24.h" + +using namespace std; + +// Radio pipe addresses for the 2 nodes to communicate. +// First pipe is for writing, 2nd, 3rd, 4th, 5th & 6th is for reading... +const uint64_t pipes[6] = + { 0xF0F0F0F0D2LL, 0xF0F0F0F0E1LL, + 0xF0F0F0F0E2LL, 0xF0F0F0F0E3LL, + 0xF0F0F0F0F1, 0xF0F0F0F0F2 + }; + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +int main(int argc, char** argv) +{ + uint8_t len; + + // Refer to RF24.h or nRF24L01 DS for settings + radio.begin(); + radio.enableDynamicPayloads(); + radio.setAutoAck(1); + radio.setRetries(15,15); + radio.setDataRate(RF24_1MBPS); + radio.setPALevel(RF24_PA_MAX); + radio.setChannel(76); + radio.setCRCLength(RF24_CRC_16); + + // Open 6 pipes for readings ( 5 plus pipe0, also can be used for reading ) + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + radio.openReadingPipe(2,pipes[2]); + radio.openReadingPipe(3,pipes[3]); + radio.openReadingPipe(4,pipes[4]); + radio.openReadingPipe(5,pipes[5]); + + // + // Start listening + // + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + radio.printDetails(); + + printf("Output below : \n"); + delay(1); + + while(1) + { + char receivePayload[32]; + uint8_t pipe = 1; + + // Start listening + radio.startListening(); + + while ( radio.available(&pipe) ) + { + len = radio.getDynamicPayloadSize(); + radio.read( receivePayload, len ); + + // Display it on screen + printf("Recv: size=%i payload=%s pipe=%i",len,receivePayload,pipe); + + // Send back payload to sender + radio.stopListening(); + + // if pipe is 7, do not send it back + if ( pipe != 7 ) + { + radio.write(receivePayload,len); + receivePayload[len]=0; + printf("\t Send: size=%i payload=%s pipe:%i\n",len,receivePayload,pipe); + } + else + { + printf("\n"); + } + + pipe++; + + // reset pipe to 0 + if ( pipe > 6 ) + pipe = 0; + } + + delayMicroseconds(20); + } + + return 0; +} + diff --git a/libraries/RF24/RPi/RF24/examples/extra/scanner.cpp b/libraries/RF24/RPi/RF24/examples/extra/scanner.cpp new file mode 100644 index 0000000..1390495 --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/extra/scanner.cpp @@ -0,0 +1,147 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 and RF24 library + + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include +#include "../RF24.h" + +using namespace std; + +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +// +// Channel info +// +//const uint8_t num_channels = 128; +const uint8_t num_channels = 120; +uint8_t values[num_channels]; + + +const int num_reps = 100; +int reset_array=0; + + +int main(int argc, char** argv) +{ + // + // Print preamble + // + + //Serial.begin(57600); + //printf_begin(); + printf("RF24/examples/scanner/\n"); + + // + // Setup and configure rf radio + // + radio.begin(); + + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + radio.printDetails(); + + // Print out header, high then low digit + int i = 0; + + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n"); + + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n"); + + // forever loop + while(1) + { + if ( reset_array == 1 ) + { + // Clear measurement values + memset(values,0,sizeof(values)); + printf("\n"); + } + + // Scan all channels num_reps times + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(128); + //radio.stopListening(); + + // Did we get a carrier? + if ( radio.testCarrier() ) + ++values[i]; + if ( values[i] == 0xf ) + { + reset_array = 2; + } + radio.stopListening(); + } + + // Print out channel measurements, clamped to a single hex digit + i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,(values[i]&0xf))); + ++i; + } + + printf("\n"); + } + + return 0; +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/RPi/RF24/examples/gettingstarted.cpp b/libraries/RF24/RPi/RF24/examples/gettingstarted.cpp new file mode 100644 index 0000000..5539c4d --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/gettingstarted.cpp @@ -0,0 +1,187 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 and RF24 library +TMRh20 2014 - Updated to work with optimized RF24 Arduino library + + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class on RPi, communicating to an Arduino running + * the GettingStarted sketch. + */ + +#include +#include +#include +#include +#include "./RF24.h" + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; + +int main(int argc, char** argv){ + + + // Print preamble: + printf("RF24/examples/pingtest/\n"); + + // Setup and configure rf radio + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // Dump the configuration of the rf unit for debugging + radio.printDetails(); + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit) \n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + + } + + // forever loop + while (1) + { + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + + printf("Now sending...\n"); + unsigned long time = millis(); + + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (!ok){ + printf("failed.\n"); + } + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) { + // by bcatalin » Thu Feb 14, 2013 11:26 am + //delay(5); //add a small delay to let radio.available to check payload + if (millis() - started_waiting_at > 200 ) + timeout = true; + } + + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n",got_time,millis()-got_time); + } + + // Try again 1s later + // delay(1000); + sleep(1); + + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + //printf("Check available...\n"); + //delay(3); + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + + + // Fetch the payload, and see if this was the last one. + radio.read( &got_time, sizeof(unsigned long) ); + + radio.stopListening(); + //delay(1); + // Seem to need a delay, or the RPi is too quick + radio.write( &got_time, sizeof(unsigned long) ); + + //delay(1); + // Now, resume listening so we catch the next packets. + radio.startListening(); + //delay(1); + // Spew it + printf("Got payload(%d) %lu...\n",sizeof(unsigned long), got_time); + } + //delay(5); + } + } // forever loop + + return 0; +} + diff --git a/libraries/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp b/libraries/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp new file mode 100644 index 0000000..19a16cc --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp @@ -0,0 +1,137 @@ +/* +TMRh20 2014 - Updated to work with optimized RF24 Arduino library +*/ + + +/** + * Example for efficient call-response using ack-payloads + * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. + * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to + * primary transmitter if wanting to initiate communication instead of respond to a commmunication. + */ + +#include +#include +#include +#include +#include "./RF24.h" + +using namespace std; + +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0, role = 0; + +uint8_t counter = 1; // A single byte to keep track of the data being sent back and forth + + +int main(int argc, char** argv){ + + + printf("RF24/examples/gettingstarted_call_response\n"); + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.enableAckPayload(); // Allow optional ack payloads + radio.setRetries(1,15); // Smallest time between retries, max no. of retries + radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed + //radio.powerUp(); + radio.printDetails(); // Dump the configuration of the rf unit for debugging + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit)\n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + + if ( role == role_ping_out ) { + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); + }else{ + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); + radio.startListening(); + } + +// forever loop +while (1){ + + +/****************** Ping Out Role ***************************/ + + if (role == role_ping_out){ // Radio is in ping mode + + uint8_t gotByte; // Initialize a variable for the incoming response + + radio.stopListening(); // First, stop listening so we can talk. + printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload + unsigned long time = millis(); // Record the current microsecond count + + if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio + if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank + printf("Got blank response. round-trip delay: %lu ms\n\r",millis()-time); + }else{ + while(radio.available() ){ // If an ack with payload was received + radio.read( &gotByte, 1 ); // Read it, and display the response time + printf("Got response %d, round-trip delay: %lu ms\n\r",gotByte,millis()-time); + counter++; // Increment the counter variable + } + } + + }else{ printf("Sending failed.\n\r"); } // If no ack response, sending failed + + sleep(1); // Try again later + } + +/****************** Pong Back Role ***************************/ + + if ( role == role_pong_back ) { + uint8_t pipeNo, gotByte; // Declare variables for the pipe and the byte received + while( radio.available(&pipeNo)){ // Read all available payloads + radio.read( &gotByte, 1 ); + // Since this is a call-response. Respond directly with an ack payload. + // Ack payloads are much more efficient than switching to transmit mode to respond to a call + radio.writeAckPayload(pipeNo,&gotByte, 1 ); // This can be commented out to send empty payloads. + printf("Sent response %d \n\r", gotByte); + } + } + +} //while 1 +} //main + + diff --git a/libraries/RF24/RPi/RF24/examples/pingpair_dyn.cpp b/libraries/RF24/RPi/RF24/examples/pingpair_dyn.cpp new file mode 100644 index 0000000..09f94e6 --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/pingpair_dyn.cpp @@ -0,0 +1,181 @@ +/* + TMRh20 2014 - Optimized RF24 Library Fork +*/ + +/** + * Example using Dynamic Payloads + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +#include +#include +#include +#include +#include "./RF24.h" + + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; + +const int min_payload_size = 4; +const int max_payload_size = 32; +const int payload_size_increments_by = 1; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + +int main(int argc, char** argv){ + + + // Print preamble: + printf("RF24/examples/pingpair_dyn/\n"); + + // Setup and configure rf radio + radio.begin(); + radio.enableDynamicPayloads(); + radio.setRetries(5,15); + radio.printDetails(); + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit) \n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + } + + +// forever loop + while (1) + { + +if (role == role_ping_out) + { + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + printf("Now sending length %i...",next_payload_size); + radio.write( send_payload, next_payload_size ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 500 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + uint8_t len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got response size=%i value=%s\n\r",len,receive_payload); + } + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; + + // Try again 1s later + delay(100); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + uint8_t len; + + while (radio.available()) + { + // Fetch the payload, and see if this was the last one. + len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got payload size=%i value=%s\n\r",len,receive_payload); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( receive_payload, len ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +} + + diff --git a/libraries/RF24/RPi/RF24/examples/transfer.cpp b/libraries/RF24/RPi/RF24/examples/transfer.cpp new file mode 100644 index 0000000..9d55470 --- /dev/null +++ b/libraries/RF24/RPi/RF24/examples/transfer.cpp @@ -0,0 +1,148 @@ +/* +TMRh20 2014 + + 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. + */ + +/** General Data Transfer Rate Test + * This example demonstrates basic data transfer functionality with the + updated library. This example will display the transfer rates acheived using + the slower form of high-speed transfer using blocking-writes. + */ + +#include +#include +#include +#include +#include "./RF24.h" + + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; +uint8_t data[32]; +unsigned long startTime, stopTime, counter, rxTimer=0; + +int main(int argc, char** argv){ + + + // Print preamble: + + printf("RF24/examples/Transfer/\n"); + + radio.begin(); // Setup and configure rf radio + radio.setChannel(1); + radio.setPALevel(RF24_PA_MAX); + radio.setDataRate(RF24_2MBPS); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries + radio.setCRCLength(RF24_CRC_8); + radio.printDetails(); +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit)\n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + + if ( role == role_ping_out ) { + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); + } else { + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); + radio.startListening(); + } + + + for(int i=0; i<32; i++){ + data[i] = rand() % 255; //Load the buffer with random data + } + + // forever loop + while (1){ + + if (role == role_ping_out){ + sleep(2); + printf("Initiating Basic Data Transfer\n\r"); + + long int cycles = 10000; //Change this to a higher or lower number. + + startTime = millis(); + + for(int i=0; i 1000){ + rxTimer = millis(); + printf("Rate: "); + float numBytes = counter*32; + printf("%.2f KB/s \n\r",numBytes/1000); + printf("Payload Count: %lu \n\r", counter); + counter = 0; + } + } + +} // loop +} // main + + + + + + diff --git a/libraries/RF24/RPi/RF24/nRF24L01.h b/libraries/RF24/RPi/RF24/nRF24L01.h new file mode 100644 index 0000000..ba83534 --- /dev/null +++ b/libraries/RF24/RPi/RF24/nRF24L01.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2007 Stefan Engelke + Portions Copyright (C) 2011 Greg Copeland + + 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. +*/ + +/* Memory Map */ +#define CONFIG 0x00 +#define EN_AA 0x01 +#define EN_RXADDR 0x02 +#define SETUP_AW 0x03 +#define SETUP_RETR 0x04 +#define RF_CH 0x05 +#define RF_SETUP 0x06 +#define STATUS 0x07 +#define OBSERVE_TX 0x08 +#define CD 0x09 +#define RX_ADDR_P0 0x0A +#define RX_ADDR_P1 0x0B +#define RX_ADDR_P2 0x0C +#define RX_ADDR_P3 0x0D +#define RX_ADDR_P4 0x0E +#define RX_ADDR_P5 0x0F +#define TX_ADDR 0x10 +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D + +/* Bit Mnemonics */ +#define MASK_RX_DR 6 +#define MASK_TX_DS 5 +#define MASK_MAX_RT 4 +#define EN_CRC 3 +#define CRCO 2 +#define PWR_UP 1 +#define PRIM_RX 0 +#define ENAA_P5 5 +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW 0 +#define ARD 4 +#define ARC 0 +#define PLL_LOCK 4 +#define RF_DR 3 +#define RF_PWR 6 +#define RX_DR 6 +#define TX_DS 5 +#define MAX_RT 4 +#define RX_P_NO 1 +#define TX_FULL 0 +#define PLOS_CNT 4 +#define ARC_CNT 0 +#define TX_REUSE 6 +#define FIFO_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 + +/* Instruction Mnemonics */ +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define NOP 0xFF + +/* Non-P omissions */ +#define LNA_HCURR 0 + +/* P model memory Map */ +#define RPD 0x09 +#define W_TX_PAYLOAD_NO_ACK 0xB0 + +/* P model bit Mnemonics */ +#define RF_DR_LOW 5 +#define RF_DR_HIGH 3 +#define RF_PWR_LOW 1 +#define RF_PWR_HIGH 2 diff --git a/libraries/RF24/RPi/RF24/readme.md b/libraries/RF24/RPi/RF24/readme.md new file mode 100644 index 0000000..a7880d6 --- /dev/null +++ b/libraries/RF24/RPi/RF24/readme.md @@ -0,0 +1,117 @@ + +Optimized Raspberry Pi RF24 and RF24 Network Libraries +============================================= + General Documentation: http://tmrh20.github.io + + Library functions are mostly the same. + See the included examples for RPi specific usage + +##Raspberry Pi - PreConfig + + +####Possible pre-configuration: + If SPI is not already enabled, load it on boot: +``` + sudo raspi-config +``` +A. Update the tool via the menu as required +B. Select Advanced and enable the SPI kernel module +C. Update other software and libraries: +``` + sudo apt-get update + sudo apt-get upgrade +``` +RPi - RF24 Quick-Start +====================== +A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: +``` + mkdir ~/rf24libs + cd ~/rf24libs +``` +B. Clone the RF24 Repo +``` + git clone https://github.com/tmrh20/RF24.git rtemp +``` +C. Copy the RPi library folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24 ./ + rm -r rtemp + cd RF24 +``` +D. Build the library, and run an example file: +``` + sudo make + cd examples + sudo make + sudo ./gettingstarted +``` + +RPi - RF24Network Quick-Start +============================= +A. Enter the same directory that contains the RF24 library folder +``` + cd ~/rf24libs +``` +B. Clone the RF24Network Repo +``` + git clone https://github.com/tmrh20/RF24Network.git ntemp +``` +C. Copy the RF24Network folder to the current directory, and delete the rest +``` + mv ntemp/RPi/RF24Network ./ + rm -r ntemp + cd RF24Network +``` +D. Build the library +``` + sudo make + cd examples + sudo make + sudo ./helloworld_rx OR sudo ./helloworld_tx +``` + +Connection Info +=============== + +Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN + +Can use either RPi CE0 or CE1 pins for radio CSN. +Choose any RPi output pin for radio CE pin. + +**Constructor:** +``` +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); +or +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS1, BCM2835_SPI_SPEED_8MHZ); +``` + +**Pins:** + + |NRF24L01 | RPI | P1 Connector | + |-----|-----------|-------------| + | VCC | rpi-3v3 | (17) | + | GND | rpi-gnd | (25) | + | CE | rpi-gpio22 | (15) | + | CSN | rpi-gpio8 | (24) | + | SCK | rpi-sckl | (23) | + | MOSI | rpi-mosi | (19) | + | MISO | rpi-miso | (21) | + + +See http://www.airspayce.com/mikem/bcm2835/index.html for BCM2835 class documentation. +Note: The BCM library has been customized slightly to allow use of hardware CE pins not +in use for SPI, and to include a millis() function. + +**************** + + +Based on the arduino lib from J. Coliz . +the library was berryfied by Purinda Gunasekara . +then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi +Network lib also based on https://github.com/farconada/RF24Network + +Currently optimized and aligned with Arduino fork of libraries by TMRh20: +https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi +Documentation: http://tmrh20.github.io + + diff --git a/libraries/RF24/RPi/readme.md b/libraries/RF24/RPi/readme.md new file mode 100644 index 0000000..a7880d6 --- /dev/null +++ b/libraries/RF24/RPi/readme.md @@ -0,0 +1,117 @@ + +Optimized Raspberry Pi RF24 and RF24 Network Libraries +============================================= + General Documentation: http://tmrh20.github.io + + Library functions are mostly the same. + See the included examples for RPi specific usage + +##Raspberry Pi - PreConfig + + +####Possible pre-configuration: + If SPI is not already enabled, load it on boot: +``` + sudo raspi-config +``` +A. Update the tool via the menu as required +B. Select Advanced and enable the SPI kernel module +C. Update other software and libraries: +``` + sudo apt-get update + sudo apt-get upgrade +``` +RPi - RF24 Quick-Start +====================== +A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: +``` + mkdir ~/rf24libs + cd ~/rf24libs +``` +B. Clone the RF24 Repo +``` + git clone https://github.com/tmrh20/RF24.git rtemp +``` +C. Copy the RPi library folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24 ./ + rm -r rtemp + cd RF24 +``` +D. Build the library, and run an example file: +``` + sudo make + cd examples + sudo make + sudo ./gettingstarted +``` + +RPi - RF24Network Quick-Start +============================= +A. Enter the same directory that contains the RF24 library folder +``` + cd ~/rf24libs +``` +B. Clone the RF24Network Repo +``` + git clone https://github.com/tmrh20/RF24Network.git ntemp +``` +C. Copy the RF24Network folder to the current directory, and delete the rest +``` + mv ntemp/RPi/RF24Network ./ + rm -r ntemp + cd RF24Network +``` +D. Build the library +``` + sudo make + cd examples + sudo make + sudo ./helloworld_rx OR sudo ./helloworld_tx +``` + +Connection Info +=============== + +Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN + +Can use either RPi CE0 or CE1 pins for radio CSN. +Choose any RPi output pin for radio CE pin. + +**Constructor:** +``` +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); +or +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS1, BCM2835_SPI_SPEED_8MHZ); +``` + +**Pins:** + + |NRF24L01 | RPI | P1 Connector | + |-----|-----------|-------------| + | VCC | rpi-3v3 | (17) | + | GND | rpi-gnd | (25) | + | CE | rpi-gpio22 | (15) | + | CSN | rpi-gpio8 | (24) | + | SCK | rpi-sckl | (23) | + | MOSI | rpi-mosi | (19) | + | MISO | rpi-miso | (21) | + + +See http://www.airspayce.com/mikem/bcm2835/index.html for BCM2835 class documentation. +Note: The BCM library has been customized slightly to allow use of hardware CE pins not +in use for SPI, and to include a millis() function. + +**************** + + +Based on the arduino lib from J. Coliz . +the library was berryfied by Purinda Gunasekara . +then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi +Network lib also based on https://github.com/farconada/RF24Network + +Currently optimized and aligned with Arduino fork of libraries by TMRh20: +https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi +Documentation: http://tmrh20.github.io + + diff --git a/libraries/RF24/doxygen-custom.css b/libraries/RF24/doxygen-custom.css new file mode 100644 index 0000000..d7d0e12 --- /dev/null +++ b/libraries/RF24/doxygen-custom.css @@ -0,0 +1,835 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 12px; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + padding: 2px; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code { + color: #4665A2; +} + +a.codeRef { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 10px; + margin-right: 5px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + +} + +.memdoc { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 2px 5px; + background-color: #FBFCFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: left; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + diff --git a/libraries/RF24/examples/GettingStarted/GettingStarted.ino b/libraries/RF24/examples/GettingStarted/GettingStarted.ino new file mode 100644 index 0000000..ff871be --- /dev/null +++ b/libraries/RF24/examples/GettingStarted/GettingStarted.ino @@ -0,0 +1,136 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. +//2014 - TMRh20 - Updated along with Optimized RF24 Library fork + */ + +/** + * Example for Getting Started with nRF24L01+ radios. + * + * This is an example of how to use the RF24 class to communicate on a basic level. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting with the serial monitor and + * sending a 'T'. The ping node sends the current time to the pong node, which responds by sending the value + * back. The ping node can then see how long the whole cycle took. + * Note: For a more efficient call-response scenario see the GettingStarted_CallResponse.ino example. + * Note: When switching between sketches, the radio may need to be powered down to clear settings that are not "un-set" otherwise + */ + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); + +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +// Set up roles to simplify testing +boolean role; // The main role variable, holds the current role identifier +boolean role_ping_out = 1, role_pong_back = 0; // The two different roles. + +void setup() { + + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // Setup and configure rf radio + radio.begin(); // Start up the radio + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(15,15); // Max delay between retries & number of retries + + radio.openWritingPipe(addresses[1]); // Open a writing pipe on pipe 0, with address 1 + radio.openReadingPipe(1,addresses[0]); // Open a reading pipe on pipe 1, with address 0 + + radio.startListening(); // Start listening + radio.printDetails(); // Dump the configuration of the rf unit for debugging +} + +void loop(void){ + + +/****************** Ping Out Role ***************************/ + if (role == role_ping_out) { + + radio.stopListening(); // First, stop listening so we can talk. + + + printf("Now sending \n\r"); + + unsigned long time = micros(); // Take the time, and send it. This will block until complete + if (!radio.write( &time, sizeof(unsigned long) )){ printf("failed.\n\r"); } + + radio.startListening(); // Now, continue listening + + unsigned long started_waiting_at = micros(); // Set up a timeout period, get the current microseconds + boolean timeout = false; // Set up a variable to indicate if a response was received or not + + while ( ! radio.available() ){ // While nothing is received + if (micros() - started_waiting_at > 200000 ){ // If waited longer than 200ms, indicate timeout and exit while loop + timeout = true; + break; + } + } + + if ( timeout ){ // Describe the results + printf("Failed, response timed out.\n\r"); + }else{ + unsigned long got_time; // Grab the response, compare, and send to debugging spew + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Sent %lu, Got response %lu, round-trip delay: %lu microseconds\n\r",time,got_time,micros()-got_time); + } + + // Try again 1s later + delay(1000); + } + +/****************** Pong Back Role ***************************/ + + if ( role == role_pong_back ) + { + if( radio.available()){ + unsigned long got_time; // Variable for the received timestamp + while (radio.available()) { // While there is data ready + radio.read( &got_time, sizeof(unsigned long) ); // Get the payload + } + + radio.stopListening(); // First, stop listening so we can talk + radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. + radio.startListening(); // Now, resume listening so we catch the next packets. + printf("Sent response %lu \n\r", got_time); + } + } + + +/****************** Change Roles via Serial Commands ***************************/ + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + role = role_ping_out; // Become the primary transmitter (ping out) + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + role = role_pong_back; // Become the primary receiver (pong back) + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); + } + } +} \ No newline at end of file diff --git a/libraries/RF24/examples/GettingStarted/printf.h b/libraries/RF24/examples/GettingStarted/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/GettingStarted/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino b/libraries/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino new file mode 100644 index 0000000..8b2d0d5 --- /dev/null +++ b/libraries/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino @@ -0,0 +1,124 @@ +/* + March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork + Parts derived from examples by J. Coliz +*/ +/** + * Example for efficient call-response using ack-payloads + * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. + * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to + * primary transmitter if wanting to initiate communication instead of respond to a commmunication. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); + // Topology +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management: Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. +typedef enum { role_ping_out = 1, role_pong_back } role_e; // The various roles supported by this sketch +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The debug-friendly names of those roles +role_e role = role_pong_back; // The role of the current running sketch + +byte counter = 1; // A single byte to keep track of the data being sent back and forth + + +void setup(){ + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // Setup and configure radio + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.enableAckPayload(); // Allow optional ack payloads + radio.setRetries(0,15); // Smallest time between retries, max no. of retries + radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed + radio.openWritingPipe(addresses[1]); // Both radios listen on the same pipes by default, and switch when writing + radio.openReadingPipe(1,addresses[0]); // Open a reading pipe on address 0, pipe 1 + radio.startListening(); // Start listening + radio.powerUp(); + radio.printDetails(); // Dump the configuration of the rf unit for debugging +} + +void loop(void) { + + +/****************** Ping Out Role ***************************/ + + if (role == role_ping_out){ // Radio is in ping mode + + byte gotByte; // Initialize a variable for the incoming response + + radio.stopListening(); // First, stop listening so we can talk. + printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload + unsigned long time = micros(); // Record the current microsecond count + + if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio + if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank + printf("Got blank response. round-trip delay: %lu microseconds\n\r",micros()-time); + }else{ + while(radio.available() ){ // If an ack with payload was received + radio.read( &gotByte, 1 ); // Read it, and display the response time + printf("Got response %d, round-trip delay: %lu microseconds\n\r",gotByte,micros()-time); + counter++; // Increment the counter variable + } + } + + }else{ printf("Sending failed.\n\r"); } // If no ack response, sending failed + + delay(1000); // Try again later + } + + +/****************** Pong Back Role ***************************/ + + if ( role == role_pong_back ) { + byte pipeNo, gotByte; // Declare variables for the pipe and the byte received + while( radio.available(&pipeNo)){ // Read all available payloads + radio.read( &gotByte, 1 ); + // Since this is a call-response. Respond directly with an ack payload. + // Ack payloads are much more efficient than switching to transmit mode to respond to a call + radio.writeAckPayload(pipeNo,&gotByte, 1 ); // This can be commented out to send empty payloads. + printf("Sent response %d \n\r", gotByte); + } + } + + + +/****************** Change Roles via Serial Commands ***************************/ + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + role = role_ping_out; // Change roles (ping out) + radio.openWritingPipe(addresses[0]); // Open different pipes when writing. Write on pipe 0, address 0 + radio.openReadingPipe(1,addresses[1]); // Read on pipe 1, as address 1 + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + role = role_pong_back; // Become the primary receiver (pong back) + radio.openWritingPipe(addresses[1]); // Since only two radios involved, both listen on the same addresses and pipe numbers in RX mode + radio.openReadingPipe(1,addresses[0]); // then switch pipes & addresses to transmit. + radio.startListening(); // Need to start listening after opening new reading pipes + } + } +} diff --git a/libraries/RF24/examples/GettingStarted_CallResponse/printf.h b/libraries/RF24/examples/GettingStarted_CallResponse/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/GettingStarted_CallResponse/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/Transfer/Transfer.ino b/libraries/RF24/examples/Transfer/Transfer.ino new file mode 100644 index 0000000..b9b1362 --- /dev/null +++ b/libraries/RF24/examples/Transfer/Transfer.ino @@ -0,0 +1,144 @@ +/* +TMRh20 2014 + + 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. + */ + +/** General Data Transfer Rate Test + * This example demonstrates basic data transfer functionality with the + updated library. This example will display the transfer rates acheived using + the slower form of high-speed transfer using blocking-writes. + */ + + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +/************* USER Configuration *****************************/ + // Hardware configuration +RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 7 & 8 +boolean RADIO_NO = 0; //SET THIS TO 0 or 1 for 1st or 2nd radio + +/***************************************************************/ + +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +byte data[32]; //Data buffer for testing data transfer speeds + +unsigned long counter, rxTimer; //Counter and timer for keeping track transfer info +unsigned long startTime, stopTime; +bool TX=1,RX=0,role=0; + +void setup(void) { + + Serial.begin(57600); + printf_begin(); + + radio.begin(); // Setup and configure rf radio + radio.setChannel(1); + radio.setPALevel(RF24_PA_MAX); + radio.setDataRate(RF24_1MBPS); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries + radio.setCRCLength(RF24_CRC_8); + //radio.disableCRC(); + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + + radio.startListening(); // Start listening + radio.printDetails(); // Dump the configuration of the rf unit for debugging + + printf("\n\rRF24/examples/Transfer Rates/\n\r"); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + randomSeed(analogRead(0)); //Seed for random number generation + + for(int i=0; i<32; i++){ + data[i] = random(255); //Load the buffer with random data + } + radio.powerUp(); //Power up the radio +} + +void loop(void){ + + + if(role == TX){ + + delay(2000); + + printf("Initiating Basic Data Transfer\n\r"); + + + unsigned long cycles = 10000; //Change this to a higher or lower number. + + startTime = millis(); + + for(int i=0; i 1000){ + rxTimer = millis(); + printf("Rate: "); + float numBytes = counter*32; + Serial.print(numBytes/1000); + printf(" KB/s\n\r"); + printf("Payload Count: %d \n\r", counter); + counter = 0; + } + } + // + // Change roles + // + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == RX ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.stopListening(); + role = TX; // Become the primary transmitter (ping out) + } + else if ( c == 'R' && role == TX ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + radio.startListening(); + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + role = RX; // Become the primary receiver (pong back) + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/Transfer/printf.h b/libraries/RF24/examples/Transfer/printf.h new file mode 100644 index 0000000..b40dd38 --- /dev/null +++ b/libraries/RF24/examples/Transfer/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/TransferTimeouts/TransferTimeouts.ino b/libraries/RF24/examples/TransferTimeouts/TransferTimeouts.ino new file mode 100644 index 0000000..65fce99 --- /dev/null +++ b/libraries/RF24/examples/TransferTimeouts/TransferTimeouts.ino @@ -0,0 +1,189 @@ +/* +TMRh20 2014 + + 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. + */ + +/** Reliably transmitting large volumes of data with a low signal or in noisy environments + * This example demonstrates data transfer functionality with the use of auto-retry + and auto-reUse functionality enabled. This sketch demonstrates how a user can extend + the auto-retry functionality to any chosen time period, preventing data loss and ensuring + the consistency of data. + + This sketh demonstrates use of the writeBlocking() functionality, and extends the standard + retry functionality of the radio. Payloads will be auto-retried until successful or the + extended timeout period is reached. + */ + + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +/************* USER Configuration *****************************/ + +RF24 radio(48,49); // Set up nRF24L01 radio on SPI bus plus pins 7 & 8 +unsigned long timeoutPeriod = 3000; // Set a user-defined timeout period. With auto-retransmit set to (15,15) retransmission will take up to 60ms and as little as 7.5ms with it set to (1,15). + // With a timeout period of 1000, the radio will retry each payload for up to 1 second before giving up on the transmission and starting over + +/***************************************************************/ + +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +byte data[32]; //Data buffer + +volatile unsigned long counter; +unsigned long rxTimer,startTime, stopTime, payloads = 0; +bool TX=1,RX=0,role=0, transferInProgress = 0; + + +void setup(void) { + + Serial.begin(57600); + printf_begin(); + + radio.begin(); // Setup and configure rf radio + radio.setChannel(1); // Set the channel + radio.setPALevel(RF24_PA_LOW); // Set PA LOW for this demonstration. We want the radio to be as lossy as possible for this example. + radio.setDataRate(RF24_1MBPS); // Raise the data rate to reduce transmission distance and increase lossiness + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(2,15); // Optionally, increase the delay between retries. Want the number of auto-retries as high as possible (15) + radio.setCRCLength(RF24_CRC_16); // Set CRC length to 16-bit to ensure quality of data + radio.openWritingPipe(pipes[0]); // Open the default reading and writing pipe + radio.openReadingPipe(1,pipes[1]); + + radio.startListening(); // Start listening + radio.printDetails(); // Dump the configuration of the rf unit for debugging + + printf("\n\rRF24/examples/Transfer Rates/\n\r"); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + randomSeed(analogRead(0)); //Seed for random number generation + for(int i=0; i<32; i++){ + data[i] = random(255); //Load the buffer with random data + } + radio.powerUp(); //Power up the radio + + +} + + + +void loop(void){ + + + if(role == TX){ + delay(2000); // Pause for a couple seconds between transfers + printf("Initiating Extended Timeout Data Transfer\n\r"); + + unsigned long cycles = 1000; // Change this to a higher or lower number. This is the number of payloads that will be sent. + + unsigned long transferCMD[] = {'H','S',cycles }; // Indicate to the other radio that we are starting, and provide the number of payloads that will be sent + radio.writeFast(&transferCMD,12); // Send the transfer command + if(radio.txStandBy(timeoutPeriod)){ // If transfer initiation was successful, do the following + + startTime = millis(); // For calculating transfer rate + boolean timedOut = 0; // Boolean for keeping track of failures + + for(int i=0; i timeoutPeriod){ // If no data available, check the timeout period + Serial.println("Transfer Failed"); // If per-payload timeout exceeeded, end the transfer + transferInProgress = 0; + }else + if(counter >= payloads){ // If the specified number of payloads is reached, transfer is completed + startTime = millis() - startTime; // Calculate the total time spent during transfer + float numBytes = counter*32; // Calculate the number of bytes transferred + Serial.print("Rate: "); // Print the transfer rate and number of payloads + Serial.print(numBytes/startTime); + Serial.println(" KB/s"); + printf("Payload Count: %d \n\r", counter); + transferInProgress = 0; // End the transfer as complete + } + } + + + } + + // + // Change roles + // + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == RX ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.stopListening(); + role = TX; // Become the primary transmitter (ping out) + } + else if ( c == 'R' && role == TX ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + radio.startListening(); + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + role = RX; // Become the primary receiver (pong back) + } + } +} + + diff --git a/libraries/RF24/examples/TransferTimeouts/printf.h b/libraries/RF24/examples/TransferTimeouts/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/TransferTimeouts/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/Usage/led_remote/Jamfile b/libraries/RF24/examples/Usage/led_remote/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/libraries/RF24/examples/Usage/led_remote/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/libraries/RF24/examples/Usage/led_remote/led_remote.pde b/libraries/RF24/examples/Usage/led_remote/led_remote.pde new file mode 100644 index 0000000..80e2955 --- /dev/null +++ b/libraries/RF24/examples/Usage/led_remote/led_remote.pde @@ -0,0 +1,255 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Example LED Remote + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * On the 'remote', connect any number of buttons or switches from + * an arduino pin to ground. Update 'button_pins' to reflect the + * pins used. + * + * On the 'led' board, connect the same number of LED's from an + * arduino pin to a resistor to ground. Update 'led_pins' to reflect + * the pins used. Also connect a separate pin to ground and change + * the 'role_pin'. This tells the sketch it's running on the LED board. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(9,10); + +// sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver +// Leave open to be the 'remote' transmitter +const int role_pin = A4; + +// Pins on the remote for buttons +const uint8_t button_pins[] = { 2,3,4,5,6,7 }; +const uint8_t num_button_pins = sizeof(button_pins); + +// Pins on the LED board for LED's +const uint8_t led_pins[] = { 2,3,4,5,6,7 }; +const uint8_t num_led_pins = sizeof(led_pins); + +// +// Topology +// + +// Single radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_remote = 1, role_led } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Remote", "LED Board"}; + +// The role of the current running sketch +role_e role; + +// +// Payload +// + +uint8_t button_states[num_button_pins]; +uint8_t led_states[num_led_pins]; + +// +// Setup +// + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_remote; + else + role = role_led; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/led_remote/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens a single pipes for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. + + if ( role == role_remote ) + { + radio.openWritingPipe(pipe); + } + else + { + radio.openReadingPipe(1,pipe); + } + + // + // Start listening + // + + if ( role == role_led ) + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Set up buttons / LED's + // + + // Set pull-up resistors for all buttons + if ( role == role_remote ) + { + int i = num_button_pins; + while(i--) + { + pinMode(button_pins[i],INPUT); + digitalWrite(button_pins[i],HIGH); + } + } + + // Turn LED's ON until we start getting keys + if ( role == role_led ) + { + int i = num_led_pins; + while(i--) + { + pinMode(led_pins[i],OUTPUT); + led_states[i] = HIGH; + digitalWrite(led_pins[i],led_states[i]); + } + } + +} + +// +// Loop +// + +void loop(void) +{ + // + // Remote role. If the state of any button has changed, send the whole state of + // all buttons. + // + + if ( role == role_remote ) + { + // Get the current state of buttons, and + // Test if the current state is different from the last state we sent + int i = num_button_pins; + bool different = false; + while(i--) + { + uint8_t state = ! digitalRead(button_pins[i]); + if ( state != button_states[i] ) + { + different = true; + button_states[i] = state; + } + } + + // Send the state of the buttons to the LED board + if ( different ) + { + printf("Now sending..."); + bool ok = radio.write( button_states, num_button_pins ); + if (ok) + printf("ok\n\r"); + else + printf("failed\n\r"); + } + + // Try again in a short while + delay(20); + } + + // + // LED role. Receive the state of all buttons, and reflect that in the LEDs + // + + if ( role == role_led ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( button_states, num_button_pins ); + + // Spew it + printf("Got buttons\n\r"); + + // For each button, if the button now on, then toggle the LED + int i = num_led_pins; + while(i--) + { + if ( button_states[i] ) + { + led_states[i] ^= HIGH; + digitalWrite(led_pins[i],led_states[i]); + } + } + } + } + } +} +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/Usage/led_remote/printf.h b/libraries/RF24/examples/Usage/led_remote/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/Usage/led_remote/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/Usage/nordic_fob/Jamfile b/libraries/RF24/examples/Usage/nordic_fob/Jamfile new file mode 100644 index 0000000..ec519f7 --- /dev/null +++ b/libraries/RF24/examples/Usage/nordic_fob/Jamfile @@ -0,0 +1,219 @@ +# (1) Project Information + +PROJECT_LIBS = RF24 SPI ; +PROJECT_DIRS = $(PWD) ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_SPEED ?= 115200 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. + +if $(OS) = MACOSX +{ + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN = /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; +} + +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; + +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; + +# Search everywhere for headers + +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Library +{ + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +rule Arduino +{ + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : libs core ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } +} + +# +# Targets +# + +# Grab everything from the core directory +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/libraries/RF24/examples/Usage/nordic_fob/nordic_fob.pde b/libraries/RF24/examples/Usage/nordic_fob/nordic_fob.pde new file mode 100644 index 0000000..5a316a0 --- /dev/null +++ b/libraries/RF24/examples/Usage/nordic_fob/nordic_fob.pde @@ -0,0 +1,142 @@ +/* + Copyright (C) 2012 J. Coliz + + 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. + */ + +/** + * Example Nordic FOB Receiver + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. Thanks to Kirk Mower for providing test hardware. + * + * See blog post at http://maniacbug.wordpress.com/2012/01/08/nordic-fob/ + */ + +#include +#include +#include "nRF24L01.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(9,10); + +// +// Payload +// + +struct payload_t +{ + uint8_t buttons; + uint16_t id; + uint8_t empty; +}; + +const char* button_names[] = { "Up", "Down", "Left", "Right", "Center" }; +const int num_buttons = 5; + +// +// Forward declarations +// + +uint16_t flip_endian(uint16_t in); + +// +// Setup +// + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\r\nRF24/examples/nordic_fob/\r\n"); + + // + // Setup and configure rf radio according to the built-in parameters + // of the FOB. + // + + radio.begin(); + radio.setChannel(2); + radio.setPayloadSize(4); + radio.setAutoAck(false); + radio.setCRCLength(RF24_CRC_8); + radio.openReadingPipe(1,0xE7E7E7E7E7LL); + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +// +// Loop +// + +void loop(void) +{ + // + // Receive each packet, dump it out + // + + // if there is data ready + if ( radio.available() ) + { + // Get the packet from the radio + payload_t payload; + radio.read( &payload, sizeof(payload) ); + + // Print the ID of this message. Note that the message + // is sent 'big-endian', so we have to flip it. + printf("#%05u Buttons ",flip_endian(payload.id)); + + // Print the name of each button + int i = num_buttons; + while (i--) + { + if ( ! ( payload.buttons & _BV(i) ) ) + { + printf("%s ",button_names[i]); + } + } + + // If no buttons, print None + if ( payload.buttons == _BV(num_buttons) - 1 ) + printf("None"); + + printf("\r\n"); + } +} + +// +// Helper functions +// + +// Change a big-endian word into a little-endian +uint16_t flip_endian(uint16_t in) +{ + uint16_t low = in >> 8; + uint16_t high = in << 8; + + return high | low; +} + +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/Usage/nordic_fob/printf.h b/libraries/RF24/examples/Usage/nordic_fob/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/Usage/nordic_fob/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/Usage/pingpair_maple/Jamfile b/libraries/RF24/examples/Usage/pingpair_maple/Jamfile new file mode 100644 index 0000000..798096c --- /dev/null +++ b/libraries/RF24/examples/Usage/pingpair_maple/Jamfile @@ -0,0 +1,182 @@ +MCU = cortex-m3 ; +CHIP = STM32F103ZE ; +BOARD = maple_native ; + +#CHIP = at91sam3u4 ; +#BOARD = sam3u-ek ; + +if ! $(TOOLSET) +{ + TOOLSET = devkit ; + Echo "Assuming TOOLSET=devkit" ; +} + +if $(TOOLSET) = yagarto +{ + TOOLS_PATH = ~/Source/yagarto-4.6.2/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} +if $(TOOLSET) = yagarto-install +{ + TOOLS_PATH = ~/Source/yagarto/install/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} +else if $(TOOLSET) = devkit +{ + TOOLS_PATH = /opt/devkitARM/bin ; + TOOLS_ARCH = arm-eabi- ; +} +else if $(TOOLSET) = maple +{ + TOOLS_PATH = /opt/Maple/Resources/Java/hardware/tools/arm/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} +else if $(TOOLSET) = ports +{ + TOOLS_PATH = /opt/local/bin ; + TOOLS_ARCH = arm-none-eabi- ; +} + +CC = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc ; +C++ = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; +AS = $(TOOLS_PATH)/$(TOOLS_ARCH)gcc -c ; +LINK = $(TOOLS_PATH)/$(TOOLS_ARCH)g++ ; +OBJCOPY = $(TOOLS_PATH)/$(TOOLS_ARCH)objcopy ; +DFU = dfu-util ; + +DEFINES += VECT_TAB_FLASH BOARD_$(BOARD) MCU_$(CHIP) ERROR_LED_PORT=GPIOC ERROR_LED_PIN=15 STM32_HIGH_DENSITY MAPLE_IDE ; +OPTIM = -Os ; +MFLAGS = cpu=$(MCU) thumb arch=armv7-m ; +CCFLAGS = -Wall -m$(MFLAGS) -g -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections ; +C++FLAGS = $(CCFLAGS) -fno-rtti -fno-exceptions ; +LINKFLAGS += -m$(MFLAGS) -Xlinker --gc-sections ; +DFUFLAGS = -a1 -d 0x1eaf:0x0003 -R ; + +MAPLE_DIR = $(HOME)/Source/SAM3U/libmaple ; +MAPLE_LIBS = Servo LiquidCrystal Wire FreeRTOS ; +MAPLE_SUBDIRS = wirish wirish/comm wirish/boards libmaple libmaple/usb libmaple/usb/usb_lib ; + +SKETCH_DIR = $(HOME)/Source/Arduino ; +SKETCH_LIBS = RF24 ; + +MODULE_DIRS = . $(MAPLE_DIR)/$(MAPLE_SUBDIRS) $(MAPLE_DIR)/libraries/$(MAPLE_LIBS) $(SKETCH_DIR)/libraries/$(SKETCH_LIBS) ; +HDRS = $(MODULE_DIRS) ; +LOCATE_TARGET = out/$(TOOLSET) ; +LOCATE_SOURCE = $(LOCATE_TARGET) ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex $(>) $(<) +} + +rule Binary +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends binary : $(<) ; + Clean clean : $(<) ; +} + +actions Binary +{ + $(OBJCOPY) -O binary $(>) $(<) +} + +rule UserObject +{ + switch $(>:S) + { + case .S : As $(<) : $(>) ; + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Upload +{ + Depends up : $(<) ; + NotFile up ; + Always $(<) ; + Always up ; +} + +actions Upload +{ + $(DFU) $(DFUFLAGS) -D $(<) +} + +# Override base objects rule, so all output can go in the output dir +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +# Override base main rule, so all output can go in the output dir +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +# Modules +MODULES = [ GLOB $(MODULE_DIRS) : *.pde *.c *.cpp *.S ] ; + +# Main output executable +MAIN = $(PWD:B).elf ; + +# Linker script +LINK_DIR = $(MAPLE_DIR)/support/ld ; +LINKSCRIPT = $(LINK_DIR)/$(BOARD)/flash.ld ; + +# Bring in the map and link script +LINKFLAGS += -Wl,-Map=$(LOCATE_TARGET)/$(MAIN:B).map -T$(LINKSCRIPT) -L$(LINK_DIR) ; + +Main $(MAIN) : $(MODULES) ; +Binary $(MAIN:B).bin : $(MAIN) ; +Upload $(MAIN:B).bin ; diff --git a/libraries/RF24/examples/Usage/pingpair_maple/main.cpp b/libraries/RF24/examples/Usage/pingpair_maple/main.cpp new file mode 100644 index 0000000..b4f976d --- /dev/null +++ b/libraries/RF24/examples/Usage/pingpair_maple/main.cpp @@ -0,0 +1,87 @@ +#ifdef MAPLE_IDE + +#include +#include "wirish.h" + +extern void setup(void); +extern void loop(void); + +void board_start(const char* program_name) +{ + // Set up the LED to steady on + pinMode(BOARD_LED_PIN, OUTPUT); + digitalWrite(BOARD_LED_PIN, HIGH); + + // Setup the button as input + pinMode(BOARD_BUTTON_PIN, INPUT); + digitalWrite(BOARD_BUTTON_PIN, HIGH); + + SerialUSB.begin(); + SerialUSB.println("Press BUT"); + + // Wait for button press + while ( !isButtonPressed() ) + { + } + + SerialUSB.println("Welcome!"); + SerialUSB.println(program_name); + + int i = 11; + while (i--) + { + toggleLED(); + delay(50); + } +} + +/** + * Custom version of _write, which will print to the USB. + * In order to use it you MUST ADD __attribute__((weak)) + * to _write in libmaple/syscalls.c +*/ +extern "C" int _write (int file, char * ptr, int len) +{ + if ( (file != 1) && (file != 2) ) + return 0; + else + SerialUSB.write(ptr,len); + return len; +} + +/** + * Re-entrant version of _write. Yagarto and Devkit now use + * the re-entrant newlib, so these get called instead of the + * non_r versions. + */ +extern "C" int _write_r (void*, int file, char * ptr, int len) +{ + return _write( file, ptr, len); +} + +__attribute__((constructor)) __attribute__ ((weak)) void premain() +{ + init(); +} + +__attribute__((weak)) void setup(void) +{ + board_start("No program defined"); +} + +__attribute__((weak)) void loop(void) +{ +} + +__attribute__((weak)) int main(void) +{ + setup(); + + while (true) + { + loop(); + } + return 0; +} +#endif // ifdef MAPLE_IDE +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde b/libraries/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde new file mode 100644 index 0000000..2d3925b --- /dev/null +++ b/libraries/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde @@ -0,0 +1,242 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Example RF Radio Ping Pair ... for Maple + * + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +#include "WProgram.h" +#include +#include "nRF24L01.h" +#include "RF24.h" + +// +// Maple specific setup. Other than this section, the sketch is the same on Maple as on +// Arduino +// + +#ifdef MAPLE_IDE + +// External startup function +extern void board_start(const char* program_name); + +// Use SPI #2. +HardwareSPI SPI(2); + +#else +#define board_startup printf +#define toggleLED(x) (x) +#endif + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 7 & 6 +// (This works for the Getting Started board plugged into the +// Maple Native backwards.) + +RF24 radio(7,6); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 10; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + board_start("\n\rRF24/examples/pingpair/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // optionally, reduce the payload size. seems to + // improve reliability + radio.setPayloadSize(8); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + toggleLED(); + + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (ok) + printf("ok...\r\n"); + else + printf("failed.\r\n"); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\r\n"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\r\n",got_time,millis()-got_time); + } + + toggleLED(); + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + + // Delay just a little bit to let the other unit + // make the transition to receiver + delay(20); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\r\n"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/pingpair_ack/pingpair_ack.ino b/libraries/RF24/examples/pingpair_ack/pingpair_ack.ino new file mode 100644 index 0000000..4cd3fe3 --- /dev/null +++ b/libraries/RF24/examples/pingpair_ack/pingpair_ack.ino @@ -0,0 +1,123 @@ +/* + // March 2014 - TMRh20 - Updated along with High Speed RF24 Library fork + // Parts derived from examples by J. Coliz +*/ +/** + * Example for efficient call-response using ack-payloads + * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. + * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to if wanting + * to initiate communication instead of respond to a commmunication. + */ + + + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); + +// Topology +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management: Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. + +typedef enum { role_ping_out = 1, role_pong_back } role_e; // The various roles supported by this sketch +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The debug-friendly names of those roles +role_e role = role_pong_back; // The role of the current running sketch + +// A single byte to keep track of the data being sent back and forth +byte counter = 1; + +void setup(){ + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/GettingStarted/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("*** PRESS 'T' to begin transmitting to the other node\n\r"); + + // Setup and configure rf radio + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.enableAckPayload(); // Allow optional ack payloads + radio.setRetries(0,15); // Smallest time between retries, max no. of retries + radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed + radio.openWritingPipe(pipes[1]); // Both radios listen on the same pipes by default, and switch when writing + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); // Start listening + radio.printDetails(); // Dump the configuration of the rf unit for debugging +} + +void loop(void) { + + if (role == role_ping_out){ + + radio.stopListening(); // First, stop listening so we can talk. + + printf("Now sending %d as payload. ",counter); + byte gotByte; + unsigned long time = micros(); // Take the time, and send it. This will block until complete + //Called when STANDBY-I mode is engaged (User is finished sending) + if (!radio.write( &counter, 1 )){ + printf("failed.\n\r"); + }else{ + + if(!radio.available()){ + printf("Blank Payload Received\n\r"); + }else{ + while(radio.available() ){ + unsigned long tim = micros(); + radio.read( &gotByte, 1 ); + printf("Got response %d, round-trip delay: %lu microseconds\n\r",gotByte,tim-time); + counter++; + } + } + + } + // Try again later + delay(1000); + } + + // Pong back role. Receive each packet, dump it out, and send it back + + if ( role == role_pong_back ) { + byte pipeNo; + byte gotByte; // Dump the payloads until we've gotten everything + while( radio.available(&pipeNo)){ + radio.read( &gotByte, 1 ); + radio.writeAckPayload(pipeNo,&gotByte, 1 ); + } + } + + // Change roles + + if ( Serial.available() ) + { + char c = toupper(Serial.read()); + if ( c == 'T' && role == role_pong_back ) + { + printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r"); + + role = role_ping_out; // Become the primary transmitter (ping out) + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else if ( c == 'R' && role == role_ping_out ) + { + printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r"); + + role = role_pong_back; // Become the primary receiver (pong back) + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + } + } +} diff --git a/libraries/RF24/examples/pingpair_ack/printf.h b/libraries/RF24/examples/pingpair_ack/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/pingpair_ack/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_dyn/Jamfile b/libraries/RF24/examples/pingpair_dyn/Jamfile new file mode 100644 index 0000000..901f8da --- /dev/null +++ b/libraries/RF24/examples/pingpair_dyn/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/libraries/RF24/examples/pingpair_dyn/pingpair_dyn.ino b/libraries/RF24/examples/pingpair_dyn/pingpair_dyn.ino new file mode 100644 index 0000000..e1b1319 --- /dev/null +++ b/libraries/RF24/examples/pingpair_dyn/pingpair_dyn.ino @@ -0,0 +1,232 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Example using Dynamic Payloads + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(7,8); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 5; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +// +// Payload +// + +const int min_payload_size = 4; +const int max_payload_size = 32; +const int payload_size_increments_by = 1; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_dyn/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // enable dynamic payloads + radio.enableDynamicPayloads(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(5,15); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + printf("Now sending length %i...",next_payload_size); + radio.write( send_payload, next_payload_size ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 500 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + uint8_t len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got response size=%i value=%s\n\r",len,receive_payload); + } + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; + + // Try again 1s later + delay(100); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + uint8_t len; + bool done = false; + while (radio.available()) + { + // Fetch the payload, and see if this was the last one. + len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got payload size=%i value=%s\n\r",len,receive_payload); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( receive_payload, len ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/libraries/RF24/examples/pingpair_dyn/printf.h b/libraries/RF24/examples/pingpair_dyn/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/pingpair_dyn/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_irq/pingpair_irq.ino b/libraries/RF24/examples/pingpair_irq/pingpair_irq.ino new file mode 100644 index 0000000..fb3f429 --- /dev/null +++ b/libraries/RF24/examples/pingpair_irq/pingpair_irq.ino @@ -0,0 +1,137 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + Update 2014 - TMRh20 + */ + +/** + * Example of using interrupts + * + * This is an example of how to user interrupts to interact with the radio, and a demonstration + * of how to use them to sleep when receiving, and not miss any payloads. + * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter. + * Sleep functionality is built directly into my fork of the RF24Network library + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" +#include +#include + +// Hardware configuration +RF24 radio(7,8); // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +const short role_pin = 5; // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver + // Leave open to be the 'ping' transmitter + + const uint64_t address[2] = {0xABCDABCD71LL, 0x544d52687CLL}; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management + +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// This is done through the role_pin +typedef enum { role_sender = 1, role_receiver } role_e; // The various roles supported by this sketch +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; // The debug-friendly names of those roles +role_e role; // The role of the current running sketch + +boolean gotMsg = 0; // So we know when to go to sleep + +/********************** Setup *********************/ +void setup(){ + + pinMode(role_pin, INPUT); // set up the role pin + digitalWrite(role_pin,HIGH); // Change this to LOW/HIGH instead of using an external pin + delay(20); // Just to get a solid reading on the role pin + + if ( digitalRead(role_pin) ) // read the address pin, establish our role + role = role_sender; + else + role = role_receiver; + + + Serial.begin(115200); + printf_begin(); + printf("\n\rRF24/examples/pingpair_irq/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // Setup and configure rf radio + radio.begin(); + radio.enableAckPayload(); // We will be using the Ack Payload feature, so please enable it + // Open pipes to other node for communication + if ( role == role_sender ) { // This simple sketch opens a pipe on a single address for these two nodes to + radio.openWritingPipe(address[0]); // communicate back and forth. One listens on it, the other talks to it. + radio.openReadingPipe(1,address[1]); + }else{ + radio.openWritingPipe(address[1]); + radio.openReadingPipe(1,address[0]); + radio.startListening(); + } + radio.printDetails(); // Dump the configuration of the rf unit for debugging + delay(50); + attachInterrupt(0, check_radio, FALLING); // Attach interrupt handler to interrupt #0 (using pin 2) on BOTH the sender and receiver +} + +static uint32_t message_count = 0; + + +/********************** Main Loop *********************/ +void loop() { + + + if (role == role_sender) { // Sender role. Repeatedly send the current time + unsigned long time = millis(); // Take the time, and send it. + printf("Now sending %lu\n\r",time); + radio.startWrite( &time, sizeof(unsigned long) ,0); + delay(2000); // Try again soon + } + + + if(role == role_receiver){ // Receiver does nothing except in IRQ + } +} + + +/********************** Interrupt *********************/ + +void check_radio(void) // Receiver role: Does nothing! All the work is in IRQ +{ + + bool tx,fail,rx; + radio.whatHappened(tx,fail,rx); // What happened? + + if ( tx ) { // Have we successfully transmitted? + if ( role == role_sender ){ printf("Send:OK\n\r"); } + if ( role == role_receiver ){ printf("Ack Payload:Sent\n\r"); } + } + + if ( fail ) { // Have we failed to transmit? + if ( role == role_sender ){ printf("Send:Failed\n\r"); } + if ( role == role_receiver ){ printf("Ack Payload:Failed\n\r"); } + } + + if ( rx || radio.available()){ // Did we receive a message? + + if ( role == role_sender ) { // If we're the sender, we've received an ack payload + radio.read(&message_count,sizeof(message_count)); + printf("Ack:%lu\n\r",message_count); + } + + + if ( role == role_receiver ) { // If we're the receiver, we've received a time message + static unsigned long got_time; // Get this payload and dump it + radio.read( &got_time, sizeof(got_time) ); + printf("Got payload %lu\n\r",got_time); + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); // Add an ack packet for the next time around. This is a simple + ++message_count; // packet counter + + } + } +} diff --git a/libraries/RF24/examples/pingpair_irq/printf.h b/libraries/RF24/examples/pingpair_irq/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/pingpair_irq/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino b/libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino new file mode 100644 index 0000000..8a8bc4e --- /dev/null +++ b/libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino @@ -0,0 +1,225 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + + TMRh20 2014 - Updates to the library allow sleeping both in TX and RX modes: + TX Mode: The radio can be powered down (.9uA current) and the Arduino slept using the watchdog timer + RX Mode: The radio can be left in standby mode (22uA current) and the Arduino slept using an interrupt pin + */ + +/** + * Example RF Radio Ping Pair which Sleeps between Sends + * + * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the GettingStarted_CallResponse example, but the + * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle, and the receiver sleeps between payloads. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +#include +#include +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 +RF24 radio(7,8); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 5; + +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; // Radio pipe addresses for the 2 nodes to communicate. + +// Role management +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + + +// Sleep declarations +typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e; + +void setup_watchdog(uint8_t prescalar); +void do_sleep(void); + +const short sleep_cycles_per_transmission = 4; +volatile short sleep_cycles_remaining = sleep_cycles_per_transmission; + + + +void setup(){ + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_sleepy/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // Prepare sleep parameters + // Only the ping out role uses WDT. Wake up every 4s to send a ping + //if ( role == role_ping_out ) + setup_watchdog(wdt_4s); + + // Setup and configure rf radio + + radio.begin(); + + // Open pipes to other nodes for communication + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // Start listening + radio.startListening(); + + // Dump the configuration of the rf unit for debugging + //radio.printDetails(); +} + +void loop(){ + + + if (role == role_ping_out) { // Ping out role. Repeatedly send the current time + radio.powerUp(); // Power up the radio after sleeping + radio.stopListening(); // First, stop listening so we can talk. + + unsigned long time = millis(); // Take the time, and send it. + printf("Now sending... %lu \n\r",time); + + radio.write( &time, sizeof(unsigned long) ); + + radio.startListening(); // Now, continue listening + + unsigned long started_waiting_at = millis(); // Wait here until we get a response, or timeout (250ms) + bool timeout = false; + while ( ! radio.available() ){ + if (millis() - started_waiting_at > 250 ){ // Break out of the while loop if nothing available + timeout = true; + break; + } + } + + if ( timeout ) { // Describe the results + printf("Failed, response timed out.\n\r"); + }else{ + unsigned long got_time; // Grab the response, compare, and send to debugging spew + radio.read( &got_time, sizeof(unsigned long) ); + + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Shut down the system + delay(500); // Experiment with some delay here to see if it has an effect + // Power down the radio. + radio.powerDown(); // NOTE: The radio MUST be powered back up again manually + + // Sleep the MCU. + do_sleep(); + + + } + + + // Pong back role. Receive each packet, dump it out, and send it back + if ( role == role_pong_back ) { + + if ( radio.available() ) { // if there is data ready + + unsigned long got_time; + while (radio.available()) { // Dump the payloads until we've gotten everything + radio.read( &got_time, sizeof(unsigned long) ); // Get the payload, and see if this was the last one. + // Spew it. Include our time, because the ping_out millis counter is unreliable + printf("Got payload %lu @ %lu...",got_time,millis()); // due to it sleeping + } + + radio.stopListening(); // First, stop listening so we can talk + radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. + printf("Sent response.\n\r"); + radio.startListening(); // Now, resume listening so we catch the next packets. + }else{ + Serial.println("Sleeping"); + delay(50); // Delay so the serial data can print out + do_sleep(); + + } + } +} + +void wakeUp(){ + sleep_disable(); +} + +// Sleep helpers + +//Prescaler values +// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms +// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec + +void setup_watchdog(uint8_t prescalar){ + + uint8_t wdtcsr = prescalar & 7; + if ( prescalar & 8 ) + wdtcsr |= _BV(WDP3); + MCUSR &= ~_BV(WDRF); // Clear the WD System Reset Flag + WDTCSR = _BV(WDCE) | _BV(WDE); // Write the WD Change enable bit to enable changing the prescaler and enable system reset + WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE); // Write the prescalar bits (how long to sleep, enable the interrupt to wake the MCU +} + +ISR(WDT_vect) +{ + //--sleep_cycles_remaining; + Serial.println("WDT"); +} + +void do_sleep(void) +{ + set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here + sleep_enable(); + attachInterrupt(0,wakeUp,LOW); + WDTCSR |= _BV(WDIE); + sleep_mode(); // System sleeps here + // The WDT_vect interrupt wakes the MCU from here + sleep_disable(); // System continues execution here when watchdog timed out + detachInterrupt(0); + WDTCSR &= ~_BV(WDIE); +} diff --git a/libraries/RF24/examples/pingpair_sleepy/printf.h b/libraries/RF24/examples/pingpair_sleepy/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/pingpair_sleepy/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/scanner/Jamfile b/libraries/RF24/examples/scanner/Jamfile new file mode 100644 index 0000000..1bf541e --- /dev/null +++ b/libraries/RF24/examples/scanner/Jamfile @@ -0,0 +1,210 @@ +# (1) Project Information + +PROJECT_LIBS = SPI RF24 ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= stk500v1 ; +UPLOAD_SPEED ?= 57600 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. + +if $(OS) = MACOSX +{ + AVR_BIN = /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN = /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; +} + +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; + +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE = $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; + +# Search everywhere for headers + +HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +# +# Targets +# + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Grab everything from the current dir +PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ; + +# Main output executable +MAIN = $(PWD:B).elf ; + +Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +Hex $(MAIN:B).hex : $(MAIN) ; + +# Upload targets +for _p in $(PORTS) +{ + Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ; +} diff --git a/libraries/RF24/examples/scanner/output/core.a b/libraries/RF24/examples/scanner/output/core.a new file mode 100644 index 0000000000000000000000000000000000000000..6ae105d90023b9ce818bcb755ee9245f24c98185 GIT binary patch literal 187630 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 NkD<2iO@I%8{}ZTDOPT-x literal 0 HcmV?d00001 diff --git a/libraries/RF24/examples/scanner/output/scanner.cpp b/libraries/RF24/examples/scanner/output/scanner.cpp new file mode 100644 index 0000000..5eb1627 --- /dev/null +++ b/libraries/RF24/examples/scanner/output/scanner.cpp @@ -0,0 +1,128 @@ +#include +#line 1 "scanner.pde" + +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// +// Channel info +// + +const short num_channels = 128; +short values[num_channels]; + +// +// Setup +// + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/scanner/\n\r"); + + // + // Setup and configure rf radio + // + + radio.begin(); + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + // Print out header, high then low digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n\r"); + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n\r"); +} + +// +// Loop +// + +const short num_reps = 100; + +void loop(void) +{ + // Clear measurement values + memset(values,0,num_channels); + + // Scan all channels num_reps times + int rep_counter = num_reps; + while (rep_counter--) + { + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(128); + radio.stopListening(); + + // Did we get a carrier? + if ( radio.testCarrier() ) + ++values[i]; + } + } + + // Print out channel measurements, clamped to a single hex digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,values[i]&0xf)); + ++i; + } + printf("\n\r"); +} + + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/examples/scanner/output/scanner.elf b/libraries/RF24/examples/scanner/output/scanner.elf new file mode 100755 index 0000000000000000000000000000000000000000..5e0f4b153bcd3684fc9008dac0bf38d4a5fbfd97 GIT binary patch literal 100436 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 ZC5_yqj3YXVcR0Mu?yd|2_9q0v{|C_~feiov literal 0 HcmV?d00001 diff --git a/libraries/RF24/examples/scanner/output/scanner.hex b/libraries/RF24/examples/scanner/output/scanner.hex new file mode 100644 index 0000000..00a3175 --- /dev/null +++ b/libraries/RF24/examples/scanner/output/scanner.hex @@ -0,0 +1,325 @@ +:100000000C9463000C948B000C948B000C948B006C +:100010000C948B000C948B000C948B000C948B0034 +:100020000C948B000C948B000C948B000C948B0024 +:100030000C948B000C948B000C948B000C948B0014 +:100040000C948F050C948B000C9423060C948B005D +:100050000C948B000C948B000C948B000C948B00F4 +:100060000C948B000C948B000000000024002700EF +:100070002A0000000000250028002B0000000000DE +:1000800023002600290004040404040404040202DA +:100090000202020203030303030301020408102007 +:1000A0004080010204081020010204081020000012 +:1000B0000007000201000003040600000000000029 +:1000C000000090043B0711241FBECFEFD8E0DEBF35 +:1000D000CDBF11E0A0E0B1E0E6EFF3E102C0059092 +:1000E0000D92AA33B107D9F712E0AAE3B1E001C03B +:1000F0001D92A13FB107E1F710E0C6ECD0E004C0CB +:100100002297FE010E94F509C23CD107C9F70E945F +:100110001C060C94F9090C9400000F931F93CF93C5 +:10012000DF938C01EB01009731F46115710519F42F +:1001300020E030E038C081E090E06EE070E00E94A6 +:10014000CB02FC019C01009771F180E8838320972A +:1001500071F0D387C28781E883838091E702909111 +:10016000E802892B21F4F093E802E093E7020115FD +:100170001105C9F011870087838182608383809194 +:10018000E9029091EA02892B71F4F093EA02E0937C +:10019000E9028091EB029091EC02892B21F4F0931B +:1001A000EC02E093EB02C901DF91CF911F910F9117 +:1001B0000895A0E0B0E0EFEDF0E00C94CC09FE0172 +:1001C0003596619171918091E9029091EA02AF01B7 +:1001D0000E94EE002096E2E00C94E809ABE0B0E06B +:1001E000E4EFF0E00C94BC093C012B015A01FC0146 +:1001F00017821682838181FD03C06FEF7FEFC6C136 +:100200009AE0892E1E010894211C311CF3012381E0 +:10021000F20123FD859123FF81912F01882309F4A9 +:10022000B2C1853239F423FD859123FF81912F01DD +:10023000853229F490E0B3010E940604E7CF982F9D +:10024000FF24EE249924FFE1FF15D0F09B3269F0E2 +:100250009C3228F4903259F0933291F40EC09D32C2 +:1002600049F0903369F441E024C052E0F52A84E07B +:10027000F82A28C098E0F92A25C0E0E1FE2A22C029 +:10028000F7FC29C0892F80538A3070F4F6FE05C030 +:10029000989C902C1124980E15C0E89CE02C1124F9 +:1002A000E80EF0E2FF2A0EC09E3229F4F6FC6BC184 +:1002B00040E4F42A07C09C3619F450E8F52A02C03D +:1002C000983649F4F20123FD959123FF91912F0176 +:1002D000992309F0B8CF892F8554833018F08052C4 +:1002E000833038F444E050E0A40EB51E5FE3598338 +:1002F0000FC0933631F0933779F0933509F056C03B +:1003000020C0F5018081898342E050E0A40EB51E33 +:10031000610101E010E012C0F501C080D180F6FC5F +:1003200003C06FEF7FEF02C0692D70E042E050E044 +:10033000A40EB51EC6010E94FB038C015FE7F522E7 +:1003400014C0F501C080D180F6FC03C06FEF7FEFD1 +:1003500002C0692D70E042E050E0A40EB51EC60157 +:100360000E94E9038C0150E8F52AF3FE07C01AC089 +:1003700080E290E0B3010E940604EA948E2D90E0A2 +:1003800008171907A8F30EC0F601F7FC8591F7FED0 +:1003900081916F0190E0B3010E940604E110EA949C +:1003A000015010400115110579F7EAC0943611F09B +:1003B000993669F5F7FE08C0F50120813181428147 +:1003C000538184E090E00AC0F501808191819C0115 +:1003D000442737FD4095542F82E090E0A80EB91EC7 +:1003E0009FE6F92257FF09C0509540953095219519 +:1003F0003F4F4F4F5F4FE0E8FE2ACA01B901A1010C +:100400002AE030E00E943204D82ED21840C095373E +:1004100029F41F2D1F7E2AE030E01DC01F2D197FFB +:100420009F3661F0903720F4983509F0ACC00FC0CA +:10043000903739F0983709F0A6C004C028E030E0C2 +:100440000AC0106114FD146020E130E004C014FD06 +:10045000166020E132E017FF08C0F501608171816C +:100460008281938144E050E008C0F5018081918150 +:10047000BC0180E090E042E050E0A40EB51EA10176 +:100480000E943204D82ED2188FE7F82EF122F6FE01 +:100490000BC05EEFF522D91438F4F4FE07C0F2FC6D +:1004A00005C08FEEF82202C01D2D01C0192DF4FEEB +:1004B0000DC0FE01ED0DF11D8081803319F499EE20 +:1004C000F92208C01F5FF2FE05C003C08F2D867899 +:1004D00009F01F5F0F2DF3FC14C0F0FE0FC01E15B6 +:1004E00010F09D2C0BC09D2C9E0C911A1E2D06C049 +:1004F00080E290E0B3010E9406041F5F1E15C0F366 +:1005000004C01E1510F4E11A01C0EE2404FF0FC050 +:1005100080E390E0B3010E94060402FF1DC001FDCC +:1005200003C088E790E00EC088E590E00BC0802F04 +:10053000867891F001FF02C08BE201C080E2F7FCF7 +:100540008DE290E0B3010E94060406C080E390E0D3 +:10055000B3010E9406049A94D914C0F3DA94F1010D +:10056000ED0DF11D808190E0B3010E940604DD20B5 +:10057000A9F706C080E290E0B3010E940604EA9465 +:10058000EE20C1F743CEF30166817781CB012B9634 +:10059000E2E10C94D8090F931F93CF93DF93689FE8 +:1005A0008001699F100D789F100D1124C8010E94D1 +:1005B000E702EC01009729F060E070E0A8010E94DA +:1005C000F403CE01DF91CF911F910F910895CF9346 +:1005D000DF93BC018230910510F462E070E0A091DD +:1005E000EF02B091F002ED01E0E0F0E040E050E019 +:1005F00021C0888199818617970769F48A819B8138 +:10060000309719F09383828304C09093F002809313 +:10061000EF02FE0134C06817790738F4411551051F +:1006200019F08417950708F4AC01FE018A819B81BB +:100630009C01E9012097E9F641155105A9F1CA018C +:10064000861B970B049708F4BA01E0E0F0E02AC09B +:100650008D919C91119784179507F9F4641775078C +:1006600081F412968D919C911397309719F0938392 +:10067000828304C09093F0028093EF02FD013296D2 +:100680004CC0CA01861B970BFD01E80FF91F61934F +:10069000719302978D939C9340C0FD018281938159 +:1006A0009C01D9011097A1F68091ED029091EE0284 +:1006B000892B41F480912301909124019093EE02C3 +:1006C0008093ED024091250150912601411551057D +:1006D00041F44DB75EB78091210190912201481BF2 +:1006E000590B2091ED023091EE02CA01821B930B4F +:1006F0008617970780F0AB014E5F5F4F8417950711 +:1007000050F0420F531F5093EE024093ED02F90157 +:100710006193719302C0E0E0F0E0CF01DF91CF91EF +:100720000895CF93DF93009709F450C0EC0122970E +:100730001B821A82A091EF02B091F002109709F18A +:1007400040E050E0AC17BD0708F1BB83AA83FE016F +:1007500021913191E20FF31FAE17BF0779F48D910C +:100760009C911197280F391F2E5F3F4F39832883A3 +:1007700012968D919C9113979B838A834115510505 +:1007800071F4D093F002C093EF0220C012968D91C5 +:100790009C911397AD01009711F0DC01D3CFFA01C2 +:1007A000D383C28321913191E20FF31FCE17DF076C +:1007B00069F488819981280F391F2E5F3F4FFA0114 +:1007C000318320838A819B8193838283DF91CF91C0 +:1007D0000895FC010590615070400110D8F7809594 +:1007E00090958E0F9F1F0895DC0101C06D934150BD +:1007F0005040E0F70895FC016150704001900110F5 +:10080000D8F7809590958E0F9F1F08950F931F9393 +:10081000CF93DF938C01EB018B8181FF1BC082FFA3 +:100820000DC02E813F818C819D812817390764F48A +:10083000E881F9810193F983E88306C0E885F985A9 +:10084000802F0995892B31F48E819F8101969F839A +:100850008E8302C00FEF1FEFC801DF91CF911F9170 +:100860000F910895FA01AA27283051F1203181F122 +:10087000E8946F936E7F6E5F7F4F8F4F9F4FAF4FA8 +:10088000B1E03ED0B4E03CD0670F781F891F9A1FBB +:10089000A11D680F791F8A1F911DA11D6A0F711D6F +:1008A000811D911DA11D20D009F468943F912AE07B +:1008B000269F11243019305D3193DEF6CF01089563 +:1008C000462F4770405D4193B3E00FD0C9F7F6CF94 +:1008D000462F4F70405D4A3318F0495D31FD40525C +:1008E000419302D0A9F7EACFB4E0A69597958795F2 +:1008F00077956795BA95C9F70097610571050895D1 +:100900009B01AC010A2E069457954795379527957C +:10091000BA95C9F7620F731F841F951FA01D089514 +:100920008AE391E068E049E00E9475070895FF922C +:100930000F931F93CF93DF9380E8E7E4F1E0DF01AB +:100940001D928A95E9F704E610E021C08AE391E060 +:100950006F2D0E94F2078AE391E00E94840880E8EC +:1009600090E00E94D7058AE391E00E947C078AE329 +:1009700091E00E944608882329F088819981019698 +:10098000998388832297FA94BFEFFB16F9F60150FA +:100990001040EFEF0F3F1E0729F0C5E4D2E08FE7CC +:1009A000F82ED4CFC7E4D1E000E011E000D000D0B1 +:1009B000ADB7BEB712961C930E931197899199917A +:1009C0008F70907014969C938E9313970E94D90009 +:1009D0000F900F900F900F90B2E0C734DB0731F704 +:1009E00000D083E091E0EDB7FEB7928381830E944F +:1009F000D9000F900F90DF91CF911F910F91FF9031 +:100A0000089580E895E060E070E00E948D00089510 +:100A10000F931F93CF93DF9384ED92E040E051EE6C +:100A200060E070E00E9454060E94010500D086E05C +:100A300091E0EDB7FEB7928381830E94D9000F90B9 +:100A40000F908AE391E00E944B088AE391E060E016 +:100A50000E94E8078AE391E00E9484088AE391E01B +:100A60000E947C07C0E0D0E000E011E000D000D0A0 +:100A7000EDB7FEB712830183CE0124E095958795EB +:100A80002A95E1F7948383830E94D90021960F90E1 +:100A90000F900F900F90C038D10541F700D083E040 +:100AA00091E0EDB7FEB7928381830E94D900C0E048 +:100AB000D0E00F900F9000D000D0EDB7FEB70183CB +:100AC0001283CE018F709070948383830E94D9002B +:100AD00021960F900F900F900F90C038D10559F7C5 +:100AE00000D083E091E0EDB7FEB7928381830E944E +:100AF000D9000F900F90DF91CF911F910F91089522 +:100B00000F931F93082F84ED92E0602F0E94280717 +:100B1000112707FD1095C8011F910F9108951F928D +:100B20000F920FB60F9211242F933F938F939F93A1 +:100B3000AF93BF9380914B0290914C02A0914D02D4 +:100B4000B0914E0230914F020196A11DB11D232F8D +:100B50002D5F2D3720F02D570196A11DB11D20933B +:100B60004F0280934B0290934C02A0934D02B0939E +:100B70004E028091470290914802A0914902B091A3 +:100B80004A020196A11DB11D80934702909348022D +:100B9000A0934902B0934A02BF91AF919F918F9168 +:100BA0003F912F910F900FBE0F901F9018950197B6 +:100BB00039F0880F991F880F991F02970197F1F755 +:100BC0000895789484B5826084BD84B5816084BDC5 +:100BD00085B5826085BD85B5816085BDEEE6F0E0B6 +:100BE000808181608083E1E8F0E010828081826012 +:100BF0008083808181608083E0E8F0E08081816093 +:100C00008083E1EBF0E0808184608083E0EBF0E0C2 +:100C1000808181608083EAE7F0E080818460808366 +:100C20008081826080838081816080838081806810 +:100C300080831092C10008950E94E1050E9408057A +:100C40000E949704FDCF1F920F920FB60F921124AE +:100C50002F933F934F938F939F93EF93FF934091E5 +:100C6000C600E091D002F091D10231969F012F771A +:100C7000307031978091D2029091D30228173907B2 +:100C800039F0E05BFD4F40833093D1022093D002D6 +:100C9000FF91EF919F918F914F913F912F910F90E5 +:100CA0000FBE0F901F901895AF92BF92DF92EF92F8 +:100CB000FF920F931F93CF93DF93EC017A018B0187 +:100CC000DD24403081EE580780E0680780E0780737 +:100CD00011F0DD24D39491E0A92EB12CE885F9859B +:100CE000DD2069F0C5010A8802C0880F991F0A94A7 +:100CF000E2F7808360E079E08DE390E005C0108248 +:100D000060E874E88EE190E0A80197010E949A09DA +:100D10002150304040405040569547953795279593 +:100D200080E12030380720F0DD2011F0DD24D6CF1F +:100D3000EC81FD813083EE81FF812083EA85FB8594 +:100D4000208141E050E0CA010E8402C0880F991F43 +:100D50000A94E2F7282B2083EA85FB852081CA01CB +:100D60000F8402C0880F991F0A94E2F7282B208372 +:100D7000EA85FB858081088802C0440F551F0A94CC +:100D8000E2F7842B8083DF91CF911F910F91FF9029 +:100D9000EF90DF90BF90AF900895DC011296ED9137 +:100DA000FC911397E058FF4F2191319180819181FF +:100DB000281B390B2F773070C9010895DC0112967A +:100DC000ED91FC911397EE57FF4F20813181929165 +:100DD0008291E058F0408217930719F42FEF3FEF0C +:100DE00005C0E20FF31F8081282F30E0C90108956C +:100DF000DC011296ED91FC911397DF01AE57BF4FC6 +:100E00002D913C911197E058FF4F80819181E058DE +:100E1000F0408217930719F42FEF3FEF0BC0E20F5A +:100E2000F31F80812F5F3F4F2F7730702D933C93BE +:100E3000282F30E0C9010895DC011296ED91FC9154 +:100E40001397EE57FF4F808191819293829308957B +:100E5000FC01A085B18521898C9190E0022E02C011 +:100E6000959587950A94E2F780FFF6CF0484F5857F +:100E7000E02D608308958BE291E09093D5028093FA +:100E8000D40280E592E09093D7028093D60285EC5D +:100E900090E09093D9028093D80284EC90E09093F4 +:100EA000DB028093DA0280EC90E09093DD02809385 +:100EB000DC0281EC90E09093DF028093DE0286EC0E +:100EC00090E09093E1028093E00284E08093E2025C +:100ED00083E08093E30287E08093E40285E08093DF +:100EE000E50281E08093E6020895FC01608341837E +:100EF00080E2828313820895FC01808160E00E9479 +:100F0000CD080895FF920F931F938C01F62E80E079 +:100F10000E94650985E00E946A09F80181816F2DB0 +:100F20000E94CD081F910F91FF9008951F93CF93BA +:100F3000DF93EC0160E070E00E94820781EE8EBDDD +:100F40000DB407FEFDCF1EB5CE0161E070E00E943A +:100F50008207812FDF91CF911F9108951F93CF9327 +:100F6000DF93EC0160E070E00E94820782EE8EBDAC +:100F70000DB407FEFDCF1EB5CE0161E070E00E940A +:100F80008207812FDF91CF911F9108950F931F93B7 +:100F9000CF93DF93EC01162F042F60E070E00E94E6 +:100FA00082071F7110621EBD0DB407FEFDCF1EB576 +:100FB0000EBD0DB407FEFDCF8EB5CE0161E070E031 +:100FC0000E948207812FDF91CF911F910F91089589 +:100FD000662319F061E04FE302C061E040E00E9447 +:100FE000C6070895462F603808F04FE765E00E9475 +:100FF000C6070895EF92FF920F931F93CF93DF934D +:101000007C01162FEA01022F60E070E00E94820747 +:101010001F7110621EBD0DB407FEFDCF1EB508C0C6 +:1010200088818EBD0DB407FEFDCF21968EB501508F +:101030000023B1F7C70161E070E00E948207812FB1 +:10104000DF91CF911F910F91FF90EF9008951F9323 +:10105000CF93DF93EC01162F60E070E00E948207CF +:101060001F711EBD0DB407FEFDCF8EB58FEF8EBD77 +:101070000DB407FEFDCF1EB5CE0161E070E00E9409 +:101080008207812FDF91CF911F91089569E00E941F +:101090002708817008950F931F938C01FC018081B4 +:1010A00061E00E94A708F801818161E00E94A70821 +:1010B000F801808160E00E94CD08C80161E070E025 +:1010C0000E9482070E947B0981E00E945C0980E007 +:1010D0000E94650985E00E946A09C80164E04FEF3B +:1010E0000E94C607C80167E040E70E94C607C80122 +:1010F0000E94AE07C8010E949607C80161E00E94E5 +:10110000F2071F910F9108950F931F938C0160E0D8 +:101110004BE00E94C607C80167E040E70E94C6078F +:10112000A8014B5F5F4FC8016AE025E00E94FA0703 +:10113000C8010E94AE07F801808161E00E94CD08DD +:1011400082E890E00E94D7051F910F910895482FE3 +:1011500050E0CA0186569F4FFC0124914A575F4FC9 +:10116000FA0184918823C1F0E82FF0E0EE0FFF1F11 +:10117000E859FF4FA591B491662341F49FB7F894C5 +:101180008C91209582238C939FBF08959FB7F894EC +:101190008C91822B8C939FBF0895482F50E0CA01F9 +:1011A00082559F4FFC012491CA0186569F4FFC0136 +:1011B00034914A575F4FFA019491992309F444C03E +:1011C000222351F1233071F0243028F42130A1F092 +:1011D000223011F514C02630B1F02730C1F0243090 +:1011E000D9F404C0809180008F7703C08091800083 +:1011F0008F7D8093800010C084B58F7702C084B546 +:101200008F7D84BD09C08091B0008F7703C080912D +:10121000B0008F7D8093B000E92FF0E0EE0FFF1F4C +:10122000EE58FF4FA591B491662341F49FB7F8940F +:101230008C91309583238C939FBF08959FB7F8942A +:101240008C91832B8C939FBF08950F931F93CF9303 +:10125000DF938C01EB0109C02196D801ED91FC913F +:101260000190F081E02DC801099568816623A1F7FE +:10127000DF91CF911F910F910895EF92FF920F93FD +:101280001F93CF93DF938C017B01EA010CC0D70140 +:101290006D917D01D801ED91FC910190F081E02DDF +:1012A000C80109952197209791F7DF91CF911F9160 +:1012B0000F91FF90EF900895882319F48CB5806208 +:1012C00002C08CB58F7D8CBD08959CB5937F982B03 +:1012D0009CBD08952CB5382F33702C7F322B3CBD2C +:1012E0002DB590E0959587959595879581702E7F82 +:1012F000822B8DBD08958DE061E00E94A7088BE0F0 +:1013000061E00E94A7088AE061E00E94A7088DE0E2 +:1013100060E00E94CD088BE060E00E94CD088AE08A +:1013200061E00E94CD088CB580618CBD8CB5806475 +:101330008CBD0895A1E21A2EAA1BBB1BFD010DC096 +:10134000AA1FBB1FEE1FFF1FA217B307E407F50775 +:1013500020F0A21BB30BE40BF50B661F771F881F51 +:10136000991F1A9469F760957095809590959B01E7 +:10137000AC01BD01CF0108952F923F924F925F9231 +:101380006F927F928F929F92AF92BF92CF92DF9295 +:10139000EF92FF920F931F93CF93DF93CDB7DEB7FA +:1013A000CA1BDB0B0FB6F894DEBF0FBECDBF09948E +:1013B0002A88398848885F846E847D848C849B84E5 +:1013C000AA84B984C884DF80EE80FD800C811B81F3 +:1013D000AA81B981CE0FD11D0FB6F894DEBF0FBE22 +:1013E000CDBFED010895EE0FFF1F0590F491E02DA4 +:0613F0000994F894FFCF00 +:1013F6002578000A0D000A0D524632342F657861B1 +:101406006D706C65732F7363616E6E65722F0A0D56 +:10141600002000F102000000000000280725093D19 +:0A14260009CD06F806DE061C0700DB +:00000001FF diff --git a/libraries/RF24/examples/scanner/printf.h b/libraries/RF24/examples/scanner/printf.h new file mode 100644 index 0000000..66f6438 --- /dev/null +++ b/libraries/RF24/examples/scanner/printf.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/scanner/scanner.ino b/libraries/RF24/examples/scanner/scanner.ino new file mode 100644 index 0000000..e4aca24 --- /dev/null +++ b/libraries/RF24/examples/scanner/scanner.ino @@ -0,0 +1,126 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(9,10); + +// +// Channel info +// + +const uint8_t num_channels = 128; +uint8_t values[num_channels]; + +// +// Setup +// + +void setup(void) +{ + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/scanner/\n\r"); + + // + // Setup and configure rf radio + // + + radio.begin(); + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + // Print out header, high then low digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n\r"); + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n\r"); +} + +// +// Loop +// + +const int num_reps = 100; + +void loop(void) +{ + // Clear measurement values + memset(values,0,sizeof(values)); + + // Scan all channels num_reps times + int rep_counter = num_reps; + while (rep_counter--) + { + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(225); + + + // Did we get a carrier? + if ( radio.testCarrier() ){ + ++values[i]; + } + radio.stopListening(); + } + } + + // Print out channel measurements, clamped to a single hex digit + int i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,values[i]&0xf)); + ++i; + } + printf("\n\r"); +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/libraries/RF24/examples/starping/Jamfile b/libraries/RF24/examples/starping/Jamfile new file mode 100644 index 0000000..de9b1f6 --- /dev/null +++ b/libraries/RF24/examples/starping/Jamfile @@ -0,0 +1,206 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = EEPROM SPI RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + diff --git a/libraries/RF24/examples/starping/printf.h b/libraries/RF24/examples/starping/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/examples/starping/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/examples/starping/starping.pde b/libraries/RF24/examples/starping/starping.pde new file mode 100644 index 0000000..4813a77 --- /dev/null +++ b/libraries/RF24/examples/starping/starping.pde @@ -0,0 +1,293 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * Example RF Radio Ping Star Group + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +#include +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 + +RF24 radio(9,10); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'pong' receiver. +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the nodes to communicate. Only ping nodes need +// dedicated pipes in this topology. Each ping node has a talking pipe +// that it will ping into, and a listening pipe that it will listen for +// the pong. The pong node listens on all the ping node talking pipes +// and sends the pong back on the sending node's specific listening pipe. + +const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; +const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_invalid = 0, role_ping_out, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +// +// Address management +// + +// Where in EEPROM is the address stored? +const uint8_t address_at_eeprom_location = 0; + +// What is our address (SRAM cache of the address from EEPROM) +// Note that zero is an INVALID address. The pong back unit takes address +// 1, and the rest are 2-6 +uint8_t node_address; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Address + // + + if ( role == role_pong_back ) + node_address = 1; + else + { + // Read the address from EEPROM + uint8_t reading = EEPROM.read(address_at_eeprom_location); + + // If it is in a valid range for node addresses, it is our + // address. + if ( reading >= 2 && reading <= 6 ) + node_address = reading; + + // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' + else + { + node_address = 0; + role = role_invalid; + } + } + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/starping/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("ADDRESS: %i\n\r",node_address); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // The pong node listens on all the ping node talking pipes + // and sends the pong back on the sending node's specific listening pipe. + if ( role == role_pong_back ) + { + radio.openReadingPipe(1,talking_pipes[0]); + radio.openReadingPipe(2,talking_pipes[1]); + radio.openReadingPipe(3,talking_pipes[2]); + radio.openReadingPipe(4,talking_pipes[3]); + radio.openReadingPipe(5,talking_pipes[4]); + } + + // Each ping node has a talking pipe that it will ping into, and a listening + // pipe that it will listen for the pong. + if ( role == role_ping_out ) + { + // Write on our talking pipe + radio.openWritingPipe(talking_pipes[node_address-2]); + // Listen on our listening pipe + radio.openReadingPipe(1,listening_pipes[node_address-2]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Prompt the user to assign a node address if we don't have one + // + + if ( role == role_invalid ) + { + printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); + } +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + uint8_t pipe_num; + if ( radio.available(&pipe_num) ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + bool done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu from node %i...",got_time,pipe_num+1); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Open the correct pipe for writing + radio.openWritingPipe(listening_pipes[pipe_num-1]); + + // Retain the low 2 bytes to identify the pipe for the spew + uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response to %04x.\n\r",pipe_id); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } + + // + // Listen for serial input, which is how we set the address + // + if (Serial.available()) + { + // If the character on serial input is in a valid range... + char c = Serial.read(); + if ( c >= '1' && c <= '6' ) + { + // It is our address + EEPROM.write(address_at_eeprom_location,c-'0'); + + // And we are done right now (no easy way to soft reset) + printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); + while(1) ; + } + } +} +// vim:ai:ci sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/keywords.txt b/libraries/RF24/keywords.txt new file mode 100644 index 0000000..d829ee9 --- /dev/null +++ b/libraries/RF24/keywords.txt @@ -0,0 +1,16 @@ + RF24 KEYWORD1 + begin KEYWORD2 + setChannel KEYWORD2 + setPayloadSize KEYWORD2 + getPayloadSize KEYWORD2 + print_details KEYWORD2 + startListening KEYWORD2 + stopListening KEYWORD2 + write KEYWORD2 + writeFast KEYWORD2 + writeBlocking KEYWORD2 + txStandBy KEYWORD2 + available KEYWORD2 + read KEYWORD2 + openWritingPipe KEYWORD2 + openReadingPipe KEYWORD2 \ No newline at end of file diff --git a/libraries/RF24/nRF24L01.h b/libraries/RF24/nRF24L01.h new file mode 100644 index 0000000..132de94 --- /dev/null +++ b/libraries/RF24/nRF24L01.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2007 Stefan Engelke + Portions Copyright (C) 2011 Greg Copeland + + 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. +*/ + +/* Memory Map */ +#define CONFIG 0x00 +#define EN_AA 0x01 +#define EN_RXADDR 0x02 +#define SETUP_AW 0x03 +#define SETUP_RETR 0x04 +#define RF_CH 0x05 +#define RF_SETUP 0x06 +#define STATUS 0x07 +#define OBSERVE_TX 0x08 +#define CD 0x09 +#define RX_ADDR_P0 0x0A +#define RX_ADDR_P1 0x0B +#define RX_ADDR_P2 0x0C +#define RX_ADDR_P3 0x0D +#define RX_ADDR_P4 0x0E +#define RX_ADDR_P5 0x0F +#define TX_ADDR 0x10 +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D + +/* Bit Mnemonics */ +#define MASK_RX_DR 6 +#define MASK_TX_DS 5 +#define MASK_MAX_RT 4 +#define EN_CRC 3 +#define CRCO 2 +#define PWR_UP 1 +#define PRIM_RX 0 +#define ENAA_P5 5 +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW 0 +#define ARD 4 +#define ARC 0 +#define PLL_LOCK 4 +#define RF_DR 3 +#define RF_PWR 6 +#define RX_DR 6 +#define TX_DS 5 +#define MAX_RT 4 +#define RX_P_NO 1 +#define TX_FULL 0 +#define PLOS_CNT 4 +#define ARC_CNT 0 +#define TX_REUSE 6 +#define FIFO_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 + +/* Instruction Mnemonics */ +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define NOP 0xFF + +/* Non-P omissions */ +#define LNA_HCURR 0 + +/* P model memory Map */ +#define RPD 0x09 +#define W_TX_PAYLOAD_NO_ACK 0xB0 + +/* P model bit Mnemonics */ +#define RF_DR_LOW 5 +#define RF_DR_HIGH 3 +#define RF_PWR_LOW 1 +#define RF_PWR_HIGH 2 diff --git a/libraries/RF24/tests/README b/libraries/RF24/tests/README new file mode 100644 index 0000000..43ceaf5 --- /dev/null +++ b/libraries/RF24/tests/README @@ -0,0 +1,7 @@ +The sketches in this directory are intended to be checkin tests. +No code should be pushed to github without these tests passing. + +See "runtests.sh" script inside each sketch dir. This script is fully compatible with +git bisest. + +Note that this requires python and py-serial diff --git a/libraries/RF24/tests/native/Jamfile b/libraries/RF24/tests/native/Jamfile new file mode 100644 index 0000000..10d0336 --- /dev/null +++ b/libraries/RF24/tests/native/Jamfile @@ -0,0 +1,300 @@ +PROJECT_NAME = $(PWD:B) ; +PROJECT_DIR = . ; +PROJECT_LIBS = RF24 ; + +OUT_DIR = ojam ; +F_CPU = 16000000 ; +MCU = atmega328p ; +PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ; + +UPLOAD_RATE = 57600 ; +AVRDUDE_PROTOCOL = stk500v1 ; +COM = 33 ; + +# Host-specific overrides for locations +if $(OS) = MACOSX +{ +ARDUINO_VERSION = 22 ; +OLD_DIR = /opt/arduino-0021 ; +AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ; +AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ; +ARDUINO_DIR = /opt/Arduino ; +ARDUINO_AVR = /usr/lib/avr/include ; +} + +# Where is everything? +ARDUINO_VERSION ?= 22 ; +SKETCH_DIR = $(HOME)/Source/Arduino ; +AVR_TOOLS_PATH ?= /usr/bin ; +ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ; +ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ; +AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(SKETCH_DIR)/libraries ; +AVR_AS = $(AVR_TOOLS_PATH)/avr-as ; +AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ; +AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ; +AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ; +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ; + +DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H HAL=1 ; +CTUNING = -ffunction-sections -fdata-sections ; +CXXTUNING = -fno-exceptions -fno-strict-aliasing ; +ASFLAGS = -mmcu=$(MCU) ; +CFLAGS = -Os -Wall -Wextra $(ASFLAGS) $(CTUNING) ; +CXXFLAGS = $(CFLAGS) $(CXXTUNING) ; +LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ; + +# Search everywhere for headers +HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ; +HDRS += [ GLOB $(HDRS) : utility ] ; + +# Grab everything from the core directory +CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# In addition to explicitly-specified program modules, pick up anything from the current +# dir. +PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ; + +# Shortcut for the out files +OUT = $(OUT_DIR)/$(PROJECT_NAME) ; + +# AvrDude setup +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ; + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +# GitVersion version.h ; + +rule AvrAsm +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrAsm +{ + $(AVR_AS) $(ASFLAGS) -o $(<) $(>) +} + +rule AvrCc +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrCc +{ + $(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>) +} + +rule AvrC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrC++ +{ + $(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule AvrAsmFromC++ +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +actions AvrAsmFromC++ +{ + $(AVR_CXX) -S -fverbose-asm -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>) +} + +rule Pde +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Clean clean : $(<) ; +} + +actions Pde +{ + echo "#include " > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule AvrPde +{ + local _CPP = $(OUT_DIR)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + AvrC++ $(<) : $(_CPP) ; +} + +rule AvrObject +{ + switch $(>:S) + { + case .S : AvrAsm $(<) : $(>) ; + case .c : AvrCc $(<) : $(>) ; + case .cpp : AvrC++ $(<) : $(>) ; + case .pde : AvrPde $(<) : $(>) ; + } +} + +rule AvrObjects +{ + for _I in $(<) + { + AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ; + } +} + +rule AvrMainFromObjects +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + MkDir $(<:D) ; + Depends all : $(<) ; + Clean clean : $(<) ; +} + +actions AvrMainFromObjects +{ + $(AVR_LD) $(LDFLAGS) -o $(<) $(>) +} + +rule AvrMain +{ + AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ; + AvrObjects $(>) ; +} + +rule AvrHex +{ + Depends $(<) : $(>) ; + Depends $(<) : $(<:D) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions AvrHex +{ + $(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule AvrUpload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + AvrUploadAction $(2) : $(3) ; +} + +actions AvrUploadAction +{ + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) +AvrHex $(OUT).hex : $(OUT).elf ; + +AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ; +AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ; +AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ; + +# +# Native +# + +OUT_DIR_NATIVE = out_native ; +OUT_NATIVE = $(OUT_DIR_NATIVE)/$(PROJECT_NAME) ; +NATIVE_CORE = $(SKETCH_DIR)/hardware/native ; +HDRS = $(NATIVE_CORE) $(HDRS) ; +NATIVE_CORE_MODULES = [ GLOB $(NATIVE_CORE) : *.c *.cpp ] ; +NATIVE_MODULES = ; +DEFINES += NATIVE ; + +rule NativePde +{ + local _CPP = $(OUT_DIR_NATIVE)/$(_I:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>) + { + case *.pde : NativePde $(<) : $(>) ; + } +} + +rule Objects +{ + for _I in $(<) + { + local _O = $(OUT_DIR_NATIVE)/$(_I:B).o ; + Object $(_O) : $(_I) ; + } +} + +rule Main +{ + MainFromObjects $(<) : $(OUT_DIR_NATIVE)/$(>:B).o ; + Objects $(>) ; +} + +actions C++ +{ + c++ -c -o $(<) $(CCHDRS) $(CCDEFS) $(>) +} + +actions Link +{ + c++ -o $(<) $(>) +} + + + +MkDir $(OUT_DIR_NATIVE) ; +Depends $(OUT_NATIVE) : $(OUT_DIR_NATIVE) ; +Main $(OUT_NATIVE) : $(NATIVE_CORE_MODULES) $(NATIVE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ; + +Depends native : $(OUT_NATIVE) ; + diff --git a/libraries/RF24/tests/native/pingpair_irq.pde b/libraries/RF24/tests/native/pingpair_irq.pde new file mode 100644 index 0000000..99c2cdf --- /dev/null +++ b/libraries/RF24/tests/native/pingpair_irq.pde @@ -0,0 +1,223 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Interrupt-driven test for native target + * + * This example is the friendliest for the native target because it doesn't do + * any polling. Made a slight change to call done() at the end of setup. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const short role_pin = 7; + +// +// Topology +// + +// Single radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_sender = 1, role_receiver } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; + +// The role of the current running sketch +role_e role; + +// Interrupt handler, check the radio because we got an IRQ +void check_radio(void); + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_sender; + else + role = role_receiver; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_irq/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // We will be using the Ack Payload feature, so please enable it + radio.enableAckPayload(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens a single pipe for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. + + if ( role == role_sender ) + { + radio.openWritingPipe(pipe); + } + else + { + radio.openReadingPipe(1,pipe); + } + + // + // Start listening + // + + if ( role == role_receiver ) + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Attach interrupt handler to interrupt #0 (using pin 2) + // on BOTH the sender and receiver + // + + attachInterrupt(0, check_radio, FALLING); + + // + // On the native target, this is as far as we get + // +#if NATIVE + done(); +#endif +} + +static uint32_t message_count = 0; + +void loop(void) +{ + // + // Sender role. Repeatedly send the current time + // + + if (role == role_sender) + { + // Take the time, and send it. + unsigned long time = millis(); + printf("Now sending %lu\n\r",time); + radio.startWrite( &time, sizeof(unsigned long) ); + + // Try again soon + delay(2000); + } + + // + // Receiver role: Does nothing! All the work is in IRQ + // + +} + +void check_radio(void) +{ + // What happened? + bool tx,fail,rx; + radio.whatHappened(tx,fail,rx); + + // Have we successfully transmitted? + if ( tx ) + { + if ( role == role_sender ) + printf("Send:OK\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Sent\n\r"); + } + + // Have we failed to transmit? + if ( fail ) + { + if ( role == role_sender ) + printf("Send:Failed\n\r"); + + if ( role == role_receiver ) + printf("Ack Payload:Failed\n\r"); + } + + // Transmitter can power down for now, because + // the transmission is done. + if ( ( tx || fail ) && ( role == role_sender ) ) + radio.powerDown(); + + // Did we receive a message? + if ( rx ) + { + // If we're the sender, we've received an ack payload + if ( role == role_sender ) + { + radio.read(&message_count,sizeof(message_count)); + printf("Ack:%lu\n\r",(unsigned long)message_count); + } + + // If we're the receiver, we've received a time message + if ( role == role_receiver ) + { + // Get this payload and dump it + static unsigned long got_time; + radio.read( &got_time, sizeof(got_time) ); + printf("Got payload %lu\n\r",got_time); + + // Add an ack packet for the next time around. This is a simple + // packet counter + radio.writeAckPayload( 1, &message_count, sizeof(message_count) ); + ++message_count; + } + } +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/libraries/RF24/tests/native/printf.h b/libraries/RF24/tests/native/printf.h new file mode 100644 index 0000000..df6c46a --- /dev/null +++ b/libraries/RF24/tests/native/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/tests/pingpair_blocking/Jamfile b/libraries/RF24/tests/pingpair_blocking/Jamfile new file mode 100644 index 0000000..18244ec --- /dev/null +++ b/libraries/RF24/tests/pingpair_blocking/Jamfile @@ -0,0 +1,219 @@ +# (1) Project Information + +PROJECT_LIBS = SPI RF24 ; +PROJECT_DIRS = $(PWD) ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= arduino ; +UPLOAD_SPEED ?= 115200 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. + +if $(OS) = MACOSX +{ + AVR_BIN ?= /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN ?= /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; +} + +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; + +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE ?= $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; + +# Search everywhere for headers + +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Library +{ + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +rule Arduino +{ + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : core libs ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } +} + +# +# Targets +# + +# Grab everything from the core directory +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde b/libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde new file mode 100644 index 0000000..004b421 --- /dev/null +++ b/libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde @@ -0,0 +1,273 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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 +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Test version of RF24, exposes some protected interface +// + +class RF24Test: public RF24 +{ +public: RF24Test(int a, int b): RF24(a,b) {} +}; + + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24Test radio(48,49); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const int role_pin = 5; + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +// +// Test state +// + +bool done; //*< Are we done with the test? */ +bool passed; //*< Have we passed the test? */ +bool notified; //*< Have we notified the user we're done? */ +const int num_needed = 10; //*< How many success/failures until we're done? */ +int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ +int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ +const int interval = 100; //*< ms to wait between sends */ + +char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ + +void one_ok(void) +{ + // Have we received enough yet? + if ( ! --receives_remaining ) + { + done = true; + passed = true; + } +} + +void one_failed(void) +{ + // Have we failed enough yet? + if ( ! --failures_remaining ) + { + done = true; + passed = false; + } +} + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/tests/pingpair_blocking/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // get test config + // + + printf("+READY press any key to start\n\r\n\r"); + + while (! Serial.available() ) {} + configuration = Serial.read(); + printf("Configuration\t = %c\n\r",configuration); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + if ( role == role_pong_back ) + printf("\n\r+OK "); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 200 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + one_failed(); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + one_ok(); + } + + // Try again later + delay(250); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + bool done = false; + while (radio.available()) + { + // Fetch the payload, and see if this was the last one. + radio.read( &got_time, sizeof(unsigned long) ); + } + // Delay just a little bit to let the other unit + // make the transition to receiver + //delay(20); + //} + + // First, stop listening so we can talk + radio.stopListening(); + + // Spew it + printf("Got payload %lu...",got_time); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + printf("Sent response.\n\r"); + + } + } + + // + // Stop the test if we're done and report results + // + if ( done && ! notified ) + { + notified = true; + + printf("\n\r+OK "); + if ( passed ) + printf("PASS\n\r\n\r"); + else + printf("FAIL\n\r\n\r"); + } +} +// vim:cin:ai:sts=2 sw=2 ft=cpp \ No newline at end of file diff --git a/libraries/RF24/tests/pingpair_blocking/printf.h b/libraries/RF24/tests/pingpair_blocking/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/tests/pingpair_blocking/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/tests/pingpair_blocking/runtest.py b/libraries/RF24/tests/pingpair_blocking/runtest.py new file mode 100755 index 0000000..0772f95 --- /dev/null +++ b/libraries/RF24/tests/pingpair_blocking/runtest.py @@ -0,0 +1,25 @@ +#!/usr/bin/python + +import sys,serial + +def read_until(token): + while 1: + line = ser.readline(None) + sys.stdout.write(line) + + if (line.startswith(token)): + break + return line + + +ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) + +read_until("+READY") +ser.write(sys.argv[2]) + +line = read_until("+OK") +ser.close() +if (line.find("PASS") != -1): + sys.exit(0) +else: + sys.exit(1) diff --git a/libraries/RF24/tests/pingpair_blocking/runtests.sh b/libraries/RF24/tests/pingpair_blocking/runtests.sh new file mode 100755 index 0000000..e106448 --- /dev/null +++ b/libraries/RF24/tests/pingpair_blocking/runtests.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# Connect u0 to receiver, u1 to sender + +jam u0 u1 && expect test.ex diff --git a/libraries/RF24/tests/pingpair_blocking/test.ex b/libraries/RF24/tests/pingpair_blocking/test.ex new file mode 100755 index 0000000..ea992ad --- /dev/null +++ b/libraries/RF24/tests/pingpair_blocking/test.ex @@ -0,0 +1,11 @@ +#/usr/bin/expect + +set timeout 100 +spawn picocom -b 57600 /dev/ttyUSB0 +expect "+READY" +send "1" +expect "+OK" +spawn picocom -b 57600 /dev/ttyUSB1 +expect "+READY" +send "1" +expect "+OK" diff --git a/libraries/RF24/tests/pingpair_test/Jamfile b/libraries/RF24/tests/pingpair_test/Jamfile new file mode 100644 index 0000000..18244ec --- /dev/null +++ b/libraries/RF24/tests/pingpair_test/Jamfile @@ -0,0 +1,219 @@ +# (1) Project Information + +PROJECT_LIBS = SPI RF24 ; +PROJECT_DIRS = $(PWD) ; + +# (2) Board Information + +UPLOAD_PROTOCOL ?= arduino ; +UPLOAD_SPEED ?= 115200 ; +MCU ?= atmega328p ; +F_CPU ?= 16000000 ; +CORE ?= arduino ; +VARIANT ?= standard ; +ARDUINO_VERSION ?= 100 ; + +# (3) USB Ports + +PORTS = p4 p6 p9 u0 u1 u2 ; +PORT_p6 = /dev/tty.usbserial-A600eHIs ; +PORT_p4 = /dev/tty.usbserial-A40081RP ; +PORT_p9 = /dev/tty.usbserial-A9007LmI ; +PORT_u0 = /dev/ttyUSB0 ; +PORT_u1 = /dev/ttyUSB1 ; +PORT_u2 = /dev/ttyUSB2 ; + +# (4) Location of AVR tools +# +# This configuration assumes using avr-tools that were obtained separate from the Arduino +# distribution. + +if $(OS) = MACOSX +{ + AVR_BIN ?= /usr/local/avrtools/bin ; + AVR_ETC = /usr/local/avrtools/etc ; + AVR_INCLUDE = /usr/local/avrtools/include ; +} +else +{ + AVR_BIN ?= /usr/bin ; + AVR_INCLUDE = /usr/lib/avr/include ; + AVR_ETC = /etc ; +} + +# (5) Directories where Arduino core and libraries are located + +ARDUINO_DIR ?= /opt/Arduino ; +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ; +ARDUINO_LIB = $(ARDUINO_DIR)/libraries ; +SKETCH_LIB = $(HOME)/Source/Arduino/libraries ; + +# +# -------------------------------------------------- +# Below this line usually never needs to be modified +# + +# Tool locations + +CC = $(AVR_BIN)/avr-gcc ; +C++ = $(AVR_BIN)/avr-g++ ; +LINK = $(AVR_BIN)/avr-gcc ; +AR = $(AVR_BIN)/avr-ar rcs ; +RANLIB = ; +OBJCOPY = $(AVR_BIN)/avr-objcopy ; +AVRDUDE ?= $(AVR_BIN)/avrdude ; + +# Flags + +DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ; +OPTIM = -Os ; +CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ; +C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ; +LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ; +AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ; + +# Search everywhere for headers + +HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ; + +# Output locations + +LOCATE_TARGET = $(F_CPU) ; +LOCATE_SOURCE = $(F_CPU) ; + +# +# Custom rules +# + +rule GitVersion +{ + Always $(<) ; + Depends all : $(<) ; +} + +actions GitVersion +{ + echo "const char program_version[] = \"\\" > $(<) + git log -1 --pretty=format:%h >> $(<) + echo "\";" >> $(<) +} + +GitVersion version.h ; + +rule Pde +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +if ( $(ARDUINO_VERSION) < 100 ) +{ + ARDUINO_H = WProgram.h ; +} +else +{ + ARDUINO_H = Arduino.h ; +} + +actions Pde +{ + echo "#include <$(ARDUINO_H)>" > $(<) + echo "#line 1 \"$(>)\"" >> $(<) + cat $(>) >> $(<) +} + +rule C++Pde +{ + local _CPP = $(>:B).cpp ; + Pde $(_CPP) : $(>) ; + C++ $(<) : $(_CPP) ; +} + +rule UserObject +{ + switch $(>:S) + { + case .ino : C++Pde $(<) : $(>) ; + case .pde : C++Pde $(<) : $(>) ; + } +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + local _b = $(_i:B)$(SUFOBJ) ; + local _o = $(_b:G=$(SOURCE_GRIST:E)) ; + Object $(_o) : $(_i) ; + Depends obj : $(_o) ; + } +} + +rule Library +{ + LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Main +{ + MainFromObjects $(<) : $(>:B)$(SUFOBJ) ; + Objects $(>) ; +} + +rule Hex +{ + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_TARGET) ; + Depends hex : $(<) ; + Clean clean : $(<) ; +} + +actions Hex +{ + $(OBJCOPY) -O ihex -R .eeprom $(>) $(<) +} + +rule Upload +{ + Depends $(1) : $(2) ; + Depends $(2) : $(3) ; + NotFile $(1) ; + Always $(1) ; + Always $(2) ; + UploadAction $(2) : $(3) ; +} + +actions UploadAction +{ + $(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i +} + +rule Arduino +{ + LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ; + Main $(<) : $(>) ; + LinkLibraries $(<) : core libs ; + Hex $(<:B).hex : $(<) ; + for _p in $(PORTS) + { + Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ; + } +} + +# +# Targets +# + +# Grab everything from the core directory +Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ; + +# Grab everything from libraries. To avoid this "grab everything" behaviour, you +# can specify specific modules to pick up in PROJECT_MODULES +Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ; + +# Main output executable +Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ; diff --git a/libraries/RF24/tests/pingpair_test/pingpair_test.pde b/libraries/RF24/tests/pingpair_test/pingpair_test.pde new file mode 100644 index 0000000..7685ca8 --- /dev/null +++ b/libraries/RF24/tests/pingpair_test/pingpair_test.pde @@ -0,0 +1,435 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + 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. + */ + +/** + * Full test on single RF pair + * + * This sketches uses as many RF24 methods as possible in a single test. + * + * To operate: + * Upload this sketch on two nodes, each with IRQ -> pin 2 + * One node needs pin 7 -> GND, the other NC. That's the receiving node + * Monitor the sending node's serial output + * Look for "+OK PASS" or "+OK FAIL" + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(7,8); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const short role_pin = 5; + +// +// Topology +// + +// Single radio pipe address for the 2 nodes to communicate. +const uint64_t pipe = 0xE8E8F0F0E1LL; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes in this +// system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_sender = 1, role_receiver } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Sender", "Receiver"}; + +// The role of the current running sketch +role_e role; + +// Interrupt handler, check the radio because we got an IRQ +void check_radio(void); + +// +// Payload +// + +const int min_payload_size = 4; +const int max_payload_size = 32; +int payload_size_increments_by = 2; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + +// +// Test state +// + +bool done; //*< Are we done with the test? */ +bool passed; //*< Have we passed the test? */ +bool notified; //*< Have we notified the user we're done? */ +const int num_needed = 10; //*< How many success/failures until we're done? */ +int receives_remaining = num_needed; //*< How many ack packets until we declare victory? */ +int failures_remaining = num_needed; //*< How many more failed sends until we declare failure? */ +const int interval = 100; //*< ms to wait between sends */ + +char configuration = '1'; //*< Configuration key, one char sent in by the test framework to tell us how to configure, this is the default */ + +uint8_t pipe_number = 1; // Which pipe to send on. + +void one_ok(void) +{ + // Have we received enough yet? + if ( ! --receives_remaining ) + { + done = true; + passed = true; + } +} + +void one_failed(void) +{ + // Have we failed enough yet? + if ( ! --failures_remaining ) + { + done = true; + passed = false; + } +} + +// +// Setup +// + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_sender; + else + role = role_receiver; + + // + // Print preamble + // + + Serial.begin(57600); + printf_begin(); + printf("\n\rRF24/tests/pingpair_test/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Read configuration from serial + // + // It would be a much better test if this program could accept configuration + // from the serial port. Then it would be possible to run the same test under + // lots of different circumstances. + // + // The idea is that we will print "+READY" at this point. The python script + // will wait for it, and then send down a configuration script that we + // execute here and then run with. + // + // The test controller will need to configure the receiver first, then go run + // the test on the sender. + // + + printf("+READY press any key to start\n\r\n\r"); + + while (! Serial.available() ) {} + configuration = Serial.read(); + printf("Configuration\t = %c\n\r",configuration); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // We will be using the Ack Payload feature, so please enable it + radio.enableAckPayload(); + + // Config 2 is special radio config + if (configuration=='2') + { + radio.setCRCLength(RF24_CRC_8); + radio.setDataRate(RF24_250KBPS); + radio.setChannel(10); + } + else + { + //Otherwise, default radio config + + // Optional: Increase CRC length for improved reliability + radio.setCRCLength(RF24_CRC_16); + + // Optional: Decrease data rate for improved reliability + radio.setDataRate(RF24_1MBPS); + + // Optional: Pick a high channel + radio.setChannel(90); + } + + // Config 3 is static payloads only + if (configuration == '3') + { + next_payload_size = 16; + payload_size_increments_by = 0; + radio.setPayloadSize(next_payload_size); + } + else + { + // enable dynamic payloads + radio.enableDynamicPayloads(); + } + + // Config 4 tests out a higher pipe ## + if (configuration == '4' && role == role_sender) + { + // Set top 4 bytes of the address in pipe 1 + radio.openReadingPipe(1,pipe & 0xFFFFFFFF00ULL); + + // indicate the pipe to use + pipe_number = 5; + } + else if ( role == role_sender ) + { + radio.openReadingPipe(5,0); + } + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens a single pipe for these two nodes to communicate + // back and forth. One listens on it, the other talks to it. + + if ( role == role_sender ) + { + radio.openWritingPipe(pipe); + } + else + { + radio.openReadingPipe(pipe_number,pipe); + } + + // + // Start listening + // + + if ( role == role_receiver ) + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Attach interrupt handler to interrupt #0 (using pin 2) + // on BOTH the sender and receiver + // + + attachInterrupt(0, check_radio, FALLING); + delay(50); + if ( role == role_receiver ) + printf("\n\r+OK "); +} + +// +// Print buffer +// +// Printing from the interrupt handler is a bad idea, so we print from there +// to this intermediate buffer +// + +char prbuf[1000]; +char *prbuf_end = prbuf + sizeof(prbuf); +char *prbuf_in = prbuf; +char *prbuf_out = prbuf; + +// +// Loop +// + +static uint32_t message_count = 0; +static uint32_t last_message_count = 0; + +void loop(void) +{ + // + // Sender role. Repeatedly send the current time + // + + if (role == role_sender && !done) + { + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Send it. This will block until complete + printf("\n\rNow sending length %i...",next_payload_size); + radio.startWrite( send_payload, next_payload_size,0 ); + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; + + // Try again soon + delay(interval); + + // Timeout if we have not received anything back ever + if ( ! last_message_count && millis() > interval * 100 ) + { + printf("No responses received. Are interrupts connected??\n\r"); + done = true; + } + } + + // + // Receiver role: Does nothing! All the work is in IRQ + // + + // + // Spew print buffer + // + + size_t write_length = prbuf_in - prbuf_out; + if ( write_length ) + { + Serial.write(reinterpret_cast(prbuf_out),write_length); + prbuf_out += write_length; + } + + // + // Stop the test if we're done and report results + // + if ( done && ! notified ) + { + notified = true; + + printf("\n\r+OK "); + if ( passed ) + printf("PASS\n\r\n\r"); + else + printf("FAIL\n\r\n\r"); + } + +} + +void check_radio(void) +{ + // What happened? + bool tx,fail,rx; + radio.whatHappened(tx,fail,rx); + + // Have we successfully transmitted? + if ( tx ) + { + if ( role == role_sender ) + prbuf_in += sprintf(prbuf_in,"Send:OK "); + + if ( role == role_receiver ) + prbuf_in += sprintf(prbuf_in,"Ack Payload:Sent\n\r"); + } + + // Have we failed to transmit? + if ( fail ) + { + if ( role == role_sender ) + { + prbuf_in += sprintf(prbuf_in,"Send:Failed "); + + // log status of this line + one_failed(); + } + + if ( role == role_receiver ) + prbuf_in += sprintf(prbuf_in,"Ack Payload:Failed\n\r"); + } + + // Not powering down since radio is in standby mode + //if ( ( tx || fail ) && ( role == role_sender ) ) + //radio.powerDown(); + + // Did we receive a message? + if ( rx ) + { + // If we're the sender, we've received an ack payload + if ( role == role_sender ) + { + radio.read(&message_count,sizeof(message_count)); + prbuf_in += sprintf(prbuf_in,"Ack:%lu ",message_count); + + // is this ack what we were expecting? to account + // for failures, we simply want to make sure we get a + // DIFFERENT ack every time. + if ( ( message_count != last_message_count ) || ( configuration=='3' && message_count == 16 ) ) + { + prbuf_in += sprintf(prbuf_in,"OK "); + one_ok(); + } + else + { + prbuf_in += sprintf(prbuf_in,"FAILED "); + one_failed(); + } + last_message_count = message_count; + } + + // If we're the receiver, we've received a time message + if ( role == role_receiver ) + { + // Get this payload and dump it + size_t len = max_payload_size; + memset(receive_payload,0,max_payload_size); + + if ( configuration == '3' ){ + len = next_payload_size; + }else{ + len = radio.getDynamicPayloadSize(); + } + + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + prbuf_in += sprintf(prbuf_in,"Recv size=%i val=%s len=%u\n\r",len,receive_payload,strlen(receive_payload)); + + // Add an ack packet for the next time around. + // Here we will report back how many bytes we got this time. + radio.writeAckPayload( pipe_number, &len, sizeof(len) ); + + ++message_count; + } + + } +} diff --git a/libraries/RF24/tests/pingpair_test/printf.h b/libraries/RF24/tests/pingpair_test/printf.h new file mode 100644 index 0000000..b2efd56 --- /dev/null +++ b/libraries/RF24/tests/pingpair_test/printf.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 J. Coliz + + 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. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#ifdef ARDUINO + +int serial_putc( char c, FILE * ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#else +#error This example is only for use on Arduino. +#endif // ARDUINO + +#endif // __PRINTF_H__ diff --git a/libraries/RF24/tests/pingpair_test/runtest.py b/libraries/RF24/tests/pingpair_test/runtest.py new file mode 100755 index 0000000..45fb65c --- /dev/null +++ b/libraries/RF24/tests/pingpair_test/runtest.py @@ -0,0 +1,25 @@ +#!/opt/local/bin/python + +import sys,serial + +def read_until(token): + while 1: + line = ser.readline(None,"\r") + sys.stdout.write(line) + + if (line.startswith(token)): + break + return line + + +ser = serial.Serial(sys.argv[1], 57600, timeout=5, dsrdtr=False, rtscts=False) + +read_until("+READY") +ser.write(sys.argv[2]) + +line = read_until("+OK") +ser.close() +if (line.find("PASS") != -1): + sys.exit(0) +else: + sys.exit(1) diff --git a/libraries/RF24/tests/pingpair_test/runtests.sh b/libraries/RF24/tests/pingpair_test/runtests.sh new file mode 100755 index 0000000..4d02310 --- /dev/null +++ b/libraries/RF24/tests/pingpair_test/runtests.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# Connect u0 to receiver, u0 to sender +# WARNING: Test config 2 only works with PLUS units. + +jam u0 u1 && expect test.ex 1 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 +expect test.ex 2 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 +expect test.ex 3 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB0 +sleep 1 +stty 57600 raw ignbrk hup < /dev/ttyUSB1 +expect test.ex 4 diff --git a/libraries/RF24/tests/pingpair_test/test.ex b/libraries/RF24/tests/pingpair_test/test.ex new file mode 100755 index 0000000..a14ffef --- /dev/null +++ b/libraries/RF24/tests/pingpair_test/test.ex @@ -0,0 +1,11 @@ +#/usr/bin/expect + +set timeout 100 +spawn picocom -b 57600 /dev/ttyUSB0 +expect "+READY" +send [lindex $argv 0] +expect "+OK" +spawn picocom -b 57600 /dev/ttyUSB1 +expect "+READY" +send [lindex $argv 0] +expect "+OK" diff --git a/libraries/RF24/wikidoc.xslt b/libraries/RF24/wikidoc.xslt new file mode 100644 index 0000000..b94d3ef --- /dev/null +++ b/libraries/RF24/wikidoc.xslt @@ -0,0 +1,41 @@ + + + + + + + + === === + + + + + '''' + +Parameters: + + + + * '''': + + + + + +Returns: + +* + + +Warning: + + + + <pre> </pre> + + + + + + + From a4aabb99fb234f371df15cb300c7ce2b09217b52 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 1 May 2014 16:45:01 +0200 Subject: [PATCH 235/326] Testing new TMRh20/RF24 library. Looks promising. No more soft-acks :) --- libraries/MySensors/Config.h | 2 +- libraries/MySensors/Sensor.cpp | 58 ++++++---------------------------- libraries/MySensors/Sensor.h | 6 +--- 3 files changed, 12 insertions(+), 54 deletions(-) diff --git a/libraries/MySensors/Config.h b/libraries/MySensors/Config.h index ab2a284..baafd5b 100644 --- a/libraries/MySensors/Config.h +++ b/libraries/MySensors/Config.h @@ -12,6 +12,6 @@ /*** * Enable/Disable debug logging */ -//#define DEBUG +#define DEBUG #endif diff --git a/libraries/MySensors/Sensor.cpp b/libraries/MySensors/Sensor.cpp index 5b74064..7802607 100644 --- a/libraries/MySensors/Sensor.cpp +++ b/libraries/MySensors/Sensor.cpp @@ -22,13 +22,14 @@ void Sensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e // Start up the radio library RF24::begin(); - RF24::enableDynamicPayloads(); - RF24::setAutoAck(false); - RF24::setRetries(15, 15); - RF24::setPALevel(paLevel); RF24::setChannel(channel); + RF24::setPALevel(paLevel); RF24::setDataRate(dataRate); + RF24::setAutoAck(true); + RF24::setRetries(2,15); RF24::setCRCLength(RF24_CRC_16); + RF24::enableDynamicPayloads(); + // All repeater nodes and gateway listen to broadcast pipe (for PING messages) if (isRelay) { @@ -217,51 +218,21 @@ boolean Sensor::send(message_s message, int length) { boolean Sensor::sendWrite(uint8_t dest, message_s message, int length) { - message.header.last = radioId; message.header.crc = crc8Message(message, length); debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d: %s\n"), message.header.from,message.header.to, message.header.last, dest, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data); - bool ok = true; bool broadcast = message.header.messageType == M_INTERNAL && message.header.type == I_PING; -// int retry = WRITE_RETRY; RF24::stopListening(); RF24::openWritingPipe(TO_ADDR(dest)); - RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length), broadcast); - RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit + bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length), broadcast); RF24::startListening(); - if (!broadcast) { - // ---------------- WAIT FOR ACK ------------------ - unsigned long startedWaiting = millis(); - bool timeout = false; - // Wait for ack message maximum 50 ms - while ( !RF24::available() && !timeout ) { - if (millis() - startedWaiting > ACK_MAX_WAIT ) { - timeout = true; - debug(PSTR("Ack: receive timeout\n")); - ok = false; - } - } - // Check payload size and content - if (!timeout) { - // Check payload size and content - if (RF24::getDynamicPayloadSize()==sizeof(uint8_t)) { - uint8_t idest; - RF24::read( &idest, sizeof(uint8_t)); - if (dest != idest) { - debug(PSTR("Ack: received ack from the wrong sensor\n")); - ok = false; - } else { - debug(PSTR("Ack: received OK\n")); - } - } else { - ok = false; - debug(PSTR("Ack: received none ack msg.\n")); - } - } - } + if (ok) + debug(PSTR("Sent successfully\n")); + else + debug(PSTR("Send failed\n")); return ok; @@ -456,15 +427,6 @@ boolean Sensor::readMessage() { uint8_t len = RF24::getDynamicPayloadSize(); RF24::read(&msg, len); - if (!(msg.header.messageType==M_INTERNAL && msg.header.type == I_PING)) { - delay(ACK_SEND_DELAY); // Small delay here to let other side switch to reading mode - RF24::stopListening(); - RF24::openWritingPipe(TO_ADDR(msg.header.last)); - RF24::write(&radioId, sizeof(uint8_t)); - RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit - RF24::startListening(); - debug(PSTR("Sent ack msg to %d\n"), msg.header.last); - } uint8_t valid = validate(len-sizeof(header_s)); boolean ok = valid == VALIDATE_OK; diff --git a/libraries/MySensors/Sensor.h b/libraries/MySensors/Sensor.h index 09452d7..54d5e12 100644 --- a/libraries/MySensors/Sensor.h +++ b/libraries/MySensors/Sensor.h @@ -50,11 +50,7 @@ #define CURRENT_NODE_PIPE ((uint8_t)1) #define BROADCAST_PIPE ((uint8_t)2) -#define ACK_MAX_WAIT 50 -#define ACK_SEND_DELAY 10 - -#define WRITE_RETRY 5 -#define FIND_RELAY_RETRIES 20 +#define FIND_RELAY_RETRIES 3 #define MAX_MESSAGE_LENGTH 32 From 18848a25a6e8db2cf132c3d3bc7219c8bc39e52b Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 1 May 2014 16:50:50 +0200 Subject: [PATCH 236/326] Renamed defines. --- libraries/MySensors/Sensor.cpp | 4 ++-- libraries/MySensors/Sensor.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/MySensors/Sensor.cpp b/libraries/MySensors/Sensor.cpp index 7802607..d7605ac 100644 --- a/libraries/MySensors/Sensor.cpp +++ b/libraries/MySensors/Sensor.cpp @@ -122,7 +122,7 @@ void Sensor::findRelay() { distance = 255; uint8_t oldRelayId = relayId; uint8_t retries=0; - while (distance == 255 && retries<3) { + while (distance == 255 && retries FIND_RELAY_RETRIES) { + if (failedTransmissions > FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { findRelay(); } failedTransmissions++; diff --git a/libraries/MySensors/Sensor.h b/libraries/MySensors/Sensor.h index 54d5e12..82149ea 100644 --- a/libraries/MySensors/Sensor.h +++ b/libraries/MySensors/Sensor.h @@ -50,7 +50,8 @@ #define CURRENT_NODE_PIPE ((uint8_t)1) #define BROADCAST_PIPE ((uint8_t)2) -#define FIND_RELAY_RETRIES 3 +#define FIND_RELAY_RETRIES 2 +#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 20 #define MAX_MESSAGE_LENGTH 32 From 8759712f127bef55a62e7748ff3c2c5daee566ac Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 1 May 2014 18:55:01 +0200 Subject: [PATCH 237/326] Added closeReadingPipe. --- RF24.cpp | 8 ++++++++ RF24.h | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index ebfbf81..000cfed 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -860,6 +860,14 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address) } } +/****************************************************************************/ + +void RF24::closeReadingPipe( uint8_t pipe ) +{ + write_register(EN_RXADDR,read_register(EN_RXADDR) & ~_BV(pgm_read_byte(&child_pipe_enable[pipe]))); +} + + /****************************************************************************/ void RF24::toggle_features(void) diff --git a/RF24.h b/RF24.h index 1571113..09390ed 100644 --- a/RF24.h +++ b/RF24.h @@ -208,6 +208,15 @@ public: */ void openReadingPipe(uint8_t number, uint64_t address); + + + /** + * Close a pipe after it has been previously opened. + * Can be safely called without having previously opened a pipe. + * @param pipe Which pipe # to close, 0-5. + */ + void closeReadingPipe( uint8_t pipe ) ; + /**@}*/ /** * @name Advanced Operation From 813f001f657c072214df0c26d8bf38e4f7fd8a7d Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 1 May 2014 20:51:23 +0200 Subject: [PATCH 238/326] Powerup radio if asleep when start transmitting. --- RF24.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RF24.cpp b/RF24.cpp index 000cfed..47b577a 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -677,6 +677,9 @@ void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //Allows the library to pass all tests void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){ + // Powerup radio if not already alive. + powerUp(); + // Send the payload //write_payload( buf, len ); From 960efcd6f707a09c14f4db4d917514794813655d Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 4 May 2014 23:47:46 +0200 Subject: [PATCH 239/326] Prepared for ack:ing between sensors. And between actuator and gateway. Now using hardware ack when sending messages and burst-mode which gives much more reliable communication. Use the corresponing development-branch for Vera plugin files. --- SerialGateway/SerialGateway.ino | 1 - libraries/MySensors/Config.h | 6 +- libraries/MySensors/Gateway.cpp | 3 +- libraries/MySensors/Gateway.h | 2 +- libraries/MySensors/Relay.cpp | 35 ++++++++--- libraries/MySensors/Relay.h | 2 +- libraries/MySensors/Sensor.cpp | 61 +++++++++++++------ libraries/MySensors/Sensor.h | 8 +-- .../examples/RelayActuator/RelayActuator.ino | 8 +-- 9 files changed, 83 insertions(+), 43 deletions(-) diff --git a/SerialGateway/SerialGateway.ino b/SerialGateway/SerialGateway.ino index 2d3d5a3..2ce8685 100644 --- a/SerialGateway/SerialGateway.ino +++ b/SerialGateway/SerialGateway.ino @@ -28,7 +28,6 @@ #include #include #include -#include #define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled #define INCLUSION_MODE_PIN 3 // Digital pin used for inclusion mode button diff --git a/libraries/MySensors/Config.h b/libraries/MySensors/Config.h index baafd5b..3b3b6e4 100644 --- a/libraries/MySensors/Config.h +++ b/libraries/MySensors/Config.h @@ -5,9 +5,9 @@ * Configure Sensor Network */ #define RF24_CHANNEL 76 //RF channel for the sensor net, 0-127 -#define RF24_DATARATE RF24_250KBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps -#define RF24_PA_LEVEL RF24_PA_MAX //Senor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_HIGH=0dBm -#define RF24_PA_LEVEL_GW RF24_PA_LEVEL //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. +#define RF24_DATARATE RF24_1MBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps +#define RF24_PA_LEVEL RF24_PA_MAX //Sensor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_HIGH=0dBm +#define RF24_PA_LEVEL_GW RF24_PA_LOW //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. /*** * Enable/Disable debug logging diff --git a/libraries/MySensors/Gateway.cpp b/libraries/MySensors/Gateway.cpp index 2470fe5..1b59d04 100644 --- a/libraries/MySensors/Gateway.cpp +++ b/libraries/MySensors/Gateway.cpp @@ -1,5 +1,5 @@ /* - The MySensors Arduino library adds a new layer on top of the RF24 library. + The MySensors library adds a new layer on top of the RF24 library. It handles radio network routing, relaying and ids. Created by Henrik Ekblad @@ -67,6 +67,7 @@ void Gateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e data // Start up the radio library setupRadio(paLevel, channel, dataRate); + RF24::openReadingPipe(WRITE_PIPE, BASE_RADIO_ID); RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID); RF24::startListening(); diff --git a/libraries/MySensors/Gateway.h b/libraries/MySensors/Gateway.h index 692997f..f23beb8 100644 --- a/libraries/MySensors/Gateway.h +++ b/libraries/MySensors/Gateway.h @@ -1,5 +1,5 @@ /* - The MySensors Arduino library adds a new layer on top of the RF24 library. + The MySensors library adds a new layer on top of the RF24 library. It handles radio network routing, relaying and ids. Created by Henrik Ekblad diff --git a/libraries/MySensors/Relay.cpp b/libraries/MySensors/Relay.cpp index d955c99..19f5b0d 100644 --- a/libraries/MySensors/Relay.cpp +++ b/libraries/MySensors/Relay.cpp @@ -1,5 +1,5 @@ /* - The MySensors Arduino library adds a new layer on top of the RF24 library. + The MySensors library adds a new layer on top of the RF24 library. It handles radio network routing, relaying and ids. Created by Henrik Ekblad @@ -86,8 +86,20 @@ boolean Relay::messageAvailable() { } if (available && pipe<7) { - uint8_t len = RF24::getDynamicPayloadSize()-sizeof(msg.header); - boolean ok = readMessage(); + uint8_t len = RF24::getDynamicPayloadSize(); + RF24::read(&msg, len); + RF24::writeAckPayload(pipe,&pipe, 1 ); + + uint8_t valid = validate(len-sizeof(header_s)); + boolean ok = valid == VALIDATE_OK; + + // Make sure string gets terminated ok for full sized messages. + msg.data[len - sizeof(header_s) ] = '\0'; + debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), + msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); + + + //boolean ok = readMessage(); if (ok) { if (msg.header.messageType == M_INTERNAL && msg.header.type == I_PING) { @@ -130,11 +142,14 @@ boolean Relay::messageAvailable() { } else { // If this is variable message from sensor net gateway. Send ack back. debug(PSTR("Message addressed for this node.\n")); - if (msg.header.from == GATEWAY_ADDRESS && - msg.header.messageType == M_SET_VARIABLE) { - // Send back ack message to sensor net gateway + + // Send set-message back to sender if sender wants this + if (msg.header.messageType == M_SET_WITH_ACK) { sendVariableAck(); + // The library user should not need to care about this ack request. Just treat it as a normal SET. + msg.header.messageType = M_SET_VARIABLE; } + // Return message to waiting sketch... if (msg.header.last != GATEWAY_ADDRESS) addChildRoute(msg.header.from, msg.header.last); @@ -143,7 +158,7 @@ boolean Relay::messageAvailable() { } } else { // We should probably try to relay this message - relayMessage(len, pipe); + relayMessage(len-sizeof(msg.header), pipe); } } } @@ -243,14 +258,14 @@ void Relay::clearChildRoutes() { void Relay::sendChildren() { // Send in which children that is using this node as an relay. // TODO: Fix this - debug(PSTR("Send child info to sensor gateway.\n")); + debug(PSTR("Dump beginning of routing table\n")); - for (int i=0;i< 10; i++) { + for (int i=0;i< 50; i++) { // Serial.println(childNodeTable[i]); debug(PSTR("rt:%d, %d\n"), i, getChildRoute(i) ); } - //sendInternal(I_CHILDREN, "not implemented"); + sendInternal(I_CHILDREN, "not implemented"); } diff --git a/libraries/MySensors/Relay.h b/libraries/MySensors/Relay.h index 6c17656..c5f3272 100644 --- a/libraries/MySensors/Relay.h +++ b/libraries/MySensors/Relay.h @@ -1,5 +1,5 @@ /* - The MySensors Arduino library adds a new layer on top of the RF24 library. + The MySensors library adds a new layer on top of the RF24 library. It handles radio network routing, relaying and ids. Created by Henrik Ekblad diff --git a/libraries/MySensors/Sensor.cpp b/libraries/MySensors/Sensor.cpp index aa8a634..72adc9f 100644 --- a/libraries/MySensors/Sensor.cpp +++ b/libraries/MySensors/Sensor.cpp @@ -22,15 +22,22 @@ void Sensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e // Start up the radio library RF24::begin(); + RF24::setAutoAck(1); + RF24::enableAckPayload(); + if (!RF24::isPVariant()) { + debug(PSTR("Sorry, you'll need to use the P version of NRF24L01.\n")); + while(1); + } + RF24::setChannel(channel); RF24::setPALevel(paLevel); RF24::setDataRate(dataRate); - RF24::setAutoAck(true); - RF24::setRetries(2,15); + RF24::setRetries(5,15); RF24::setCRCLength(RF24_CRC_16); RF24::enableDynamicPayloads(); + // All repeater nodes and gateway listen to broadcast pipe (for PING messages) if (isRelay) { RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); @@ -44,6 +51,7 @@ void Sensor::begin(uint8_t _radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf2 debug(PSTR("Started %s.\n"), isRelay?"relay":"sensor"); + setupRadio(paLevel, channel, dataRate); // Fetch relay from EEPROM @@ -58,7 +66,8 @@ void Sensor::begin(uint8_t _radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf2 initializeRadioId(); - // Open reading pipe for messages directed to this node + // Open reading pipe for messages directed to this node (set write pipe to same) + RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(radioId)); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId)); // Send presentation for this radio node @@ -224,11 +233,16 @@ boolean Sensor::sendWrite(uint8_t dest, message_s message, int length) { message.header.from,message.header.to, message.header.last, dest, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data); bool broadcast = message.header.messageType == M_INTERNAL && message.header.type == I_PING; + RF24::stopListening(); RF24::openWritingPipe(TO_ADDR(dest)); bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length), broadcast); RF24::startListening(); + + //RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit + //delay(50); + if (ok) debug(PSTR("Sent successfully\n")); else @@ -351,11 +365,11 @@ char* Sensor::get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t rec } char* Sensor::getStatus(uint8_t childId, uint8_t variableType) { - return get(GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, M_ACK_VARIABLE, variableType); + return get(GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, M_SET_VARIABLE, variableType); } char * Sensor::getStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) { - return get(nodeId, childId, M_REQ_VARIABLE, M_ACK_VARIABLE, variableType); + return get(nodeId, childId, M_REQ_VARIABLE, M_SET_VARIABLE, variableType); } @@ -385,22 +399,33 @@ void Sensor::requestIsMetricSystem() { boolean Sensor::messageAvailable() { uint8_t pipe; boolean available = RF24::available(&pipe); - - if (available) { - debug(PSTR("Message available on pipe %d\n"),pipe); - } - - if (available && pipe<7) { - boolean ok = readMessage(); + + uint8_t len = RF24::getDynamicPayloadSize(); + RF24::read(&msg, len); + RF24::writeAckPayload(pipe,&pipe, 1 ); + + if (available) { + debug(PSTR("Message available on pipe %d\n"),pipe); + } + + uint8_t valid = validate(len-sizeof(header_s)); + boolean ok = valid == VALIDATE_OK; + + // Make sure string gets terminated ok for full sized messages. + msg.data[len - sizeof(header_s) ] = '\0'; + debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), + msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); + + // boolean ok = readMessage(); if (ok && msg.header.to == radioId) { // This message is addressed to this node debug(PSTR("Message addressed for this node.\n")); - if (msg.header.from == GATEWAY_ADDRESS && - // If this is variable message from sensor net gateway. Send ack back. - msg.header.messageType == M_SET_VARIABLE) { - // Send back ack message to sensor net gateway + // Send set-message back to sender if sender wants this + if (msg.header.messageType == M_SET_WITH_ACK) { sendVariableAck(); + // The library user should not need to care about this ack request. Just treat it as a normal SET. + msg.header.messageType = M_SET_VARIABLE; } // Return message to waiting sketch... return true; @@ -423,7 +448,7 @@ message_s Sensor::getMessage() { } -boolean Sensor::readMessage() { +/*boolean Sensor::readMessage() { uint8_t len = RF24::getDynamicPayloadSize(); RF24::read(&msg, len); @@ -435,7 +460,7 @@ boolean Sensor::readMessage() { debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); return ok; -} +}*/ /* * calculate CRC8 on message_s data taking care of data structure and protocol version diff --git a/libraries/MySensors/Sensor.h b/libraries/MySensors/Sensor.h index e17a691..d661bc5 100644 --- a/libraries/MySensors/Sensor.h +++ b/libraries/MySensors/Sensor.h @@ -1,5 +1,5 @@ /* - The Sensor Net Arduino library adds a new layer on top of the RF24 library. + The MySensors library adds a new layer on top of the RF24 library. It handles radio network routing, relaying and ids. Created by Henrik Ekblad @@ -41,7 +41,7 @@ // This is the radioId for sensor net gateway receiver sketch (where all sensors should send their data). // This is also act as base value for sensor radioId -#define BASE_RADIO_ID ((uint64_t)0xABCDABC000LL) +#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) #define GATEWAY_ADDRESS ((uint8_t)0) #define BROADCAST_ADDRESS ((uint8_t)0xFF) #define TO_ADDR(x) (BASE_RADIO_ID + x) @@ -61,7 +61,7 @@ typedef enum { M_PRESENTATION = 0, M_SET_VARIABLE = 1, M_REQ_VARIABLE = 2, - M_ACK_VARIABLE = 3, + M_SET_WITH_ACK = 3, M_INTERNAL = 4 } messageType; @@ -278,7 +278,7 @@ class Sensor : public RF24 void findRelay(); boolean send(message_s message, int length); boolean sendWrite(uint8_t dest, message_s message, int length); - boolean readMessage(); + //boolean readMessage(); void buildMsg(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary); void sendInternal(uint8_t variableType, const char *value); boolean sendVariableAck(); diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index 8b995b6..47c73b7 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -6,11 +6,11 @@ #include #define RELAY_1 3 // Arduino Digital I/O pin number for first relay (second on pin+1 etc) -#define NUMBER_OF_RELAYS 1 -#define RELAY_ON 0 -#define RELAY_OFF 1 +#define NUMBER_OF_RELAYS 1 // Total number of attached relays +#define RELAY_ON 1 // GPIO value to write to turn on attached relay +#define RELAY_OFF 0 // GPIO value to write to turn off attached relay -Sensor gw; +Relay gw; void setup() { From 7856dfb20943aa66fa9159eab5a6b073281dae78 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 12 May 2014 23:50:44 +0200 Subject: [PATCH 240/326] Compiling. No tests what so ever. --- EthernetGateway/EthernetGateway.ino | 6 +- libraries/MySensors/{Config.h => MyConfig.h} | 6 +- .../MySensors/{Gateway.cpp => MyGateway.cpp} | 77 +-- .../MySensors/{Gateway.h => MyGateway.h} | 0 libraries/MySensors/MyMessage.cpp | 259 +++++++ libraries/MySensors/MyMessage.h | 184 +++++ libraries/MySensors/MySensor.cpp | 650 ++++++++++++++++++ libraries/MySensors/MySensor.h | 237 +++++++ libraries/MySensors/Relay.cpp | 271 -------- libraries/MySensors/Relay.h | 62 -- libraries/MySensors/Sensor.cpp | 555 --------------- libraries/MySensors/Sensor.h | 298 -------- .../examples/RelayActuator/RelayActuator.ino | 45 +- libraries/RF24/RF24.cpp | 3 - 14 files changed, 1396 insertions(+), 1257 deletions(-) rename libraries/MySensors/{Config.h => MyConfig.h} (91%) rename libraries/MySensors/{Gateway.cpp => MyGateway.cpp} (70%) rename libraries/MySensors/{Gateway.h => MyGateway.h} (100%) create mode 100644 libraries/MySensors/MyMessage.cpp create mode 100644 libraries/MySensors/MyMessage.h create mode 100644 libraries/MySensors/MySensor.cpp create mode 100644 libraries/MySensors/MySensor.h delete mode 100644 libraries/MySensors/Relay.cpp delete mode 100644 libraries/MySensors/Relay.h delete mode 100644 libraries/MySensors/Sensor.cpp delete mode 100644 libraries/MySensors/Sensor.h diff --git a/EthernetGateway/EthernetGateway.ino b/EthernetGateway/EthernetGateway.ino index 75fa991..7a4b363 100644 --- a/EthernetGateway/EthernetGateway.ino +++ b/EthernetGateway/EthernetGateway.ino @@ -52,9 +52,9 @@ #include #include #include -#include +#include #include -#include +//#include #include // Use this if you have attached a Ethernet ENC28J60 shields //#include // Use this fo WizNET module and Arduino Ethernet Shield @@ -81,7 +81,7 @@ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // DEAD BEEF FEED EthernetServer server = EthernetServer(IP_PORT); // No blink or button functionality. Use the vanilla constructor. -Gateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME); +MyGateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME); // Uncomment this constructor if you have leds and include button attached to your gateway //Gateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); diff --git a/libraries/MySensors/Config.h b/libraries/MySensors/MyConfig.h similarity index 91% rename from libraries/MySensors/Config.h rename to libraries/MySensors/MyConfig.h index 3b3b6e4..5d7956f 100644 --- a/libraries/MySensors/Config.h +++ b/libraries/MySensors/MyConfig.h @@ -1,5 +1,5 @@ -#ifndef Config_h -#define Config_h +#ifndef MyConfig_h +#define MyConfig_h /*** * Configure Sensor Network @@ -12,6 +12,6 @@ /*** * Enable/Disable debug logging */ -#define DEBUG +//#define DEBUG #endif diff --git a/libraries/MySensors/Gateway.cpp b/libraries/MySensors/MyGateway.cpp similarity index 70% rename from libraries/MySensors/Gateway.cpp rename to libraries/MySensors/MyGateway.cpp index 1b59d04..6d8fa89 100644 --- a/libraries/MySensors/Gateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -9,17 +9,15 @@ version 2 as published by the Free Software Foundation. */ -#include "Gateway.h" +#include "MyGateway.h" -Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : Relay(_cepin, _cspin) { +MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : MySensor(_cepin, _cspin) { ledMode = false; - isRelay = true; inclusionTime = _inclusion_time; } -Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er) : Relay(_cepin, _cspin) { +MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er) : MySensor(_cepin, _cspin) { ledMode = true; - isRelay = true; pinInclusion = _inclusion_pin; inclusionTime = _inclusion_time; pinRx = _rx; @@ -27,8 +25,10 @@ Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_ pinEr = _er; } -void Gateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) { +void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) { Serial.begin(BAUD_RATE); + relayMode = true; + setupRelayMode(); if (inDataCallback != NULL) { useWriteCallback = true; @@ -72,25 +72,25 @@ void Gateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e data RF24::startListening(); // Send startup log message on serial - serial(PSTR("0;0;%d;%d;Arduino startup complete.\n"), M_INTERNAL, I_LOG_MESSAGE); + serial(PSTR("0;0;%d;%d;Arduino startup complete.\n"), C_INTERNAL, I_LOG_MESSAGE); } -boolean Gateway::isLedMode() { +boolean MyGateway::isLedMode() { return ledMode; } -void Gateway::startInclusionInterrupt() { +void MyGateway::startInclusionInterrupt() { buttonTriggeredInclusion = true; } -void Gateway::checkButtonTriggeredInclusion() { +void MyGateway::checkButtonTriggeredInclusion() { if (buttonTriggeredInclusion) { // Ok, someone pressed the inclusion button on the gateway // start inclusion mode for 1 munute. - serial(PSTR("0;0;%d;%d;Inclusion started by button.\n"), M_INTERNAL, I_LOG_MESSAGE); + serial(PSTR("0;0;%d;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE); buttonTriggeredInclusion = false; setInclusionMode(true); @@ -101,7 +101,7 @@ void Gateway::checkButtonTriggeredInclusion() { } } -void Gateway::checkInclusionFinished() { +void MyGateway::checkInclusionFinished() { if (inclusionMode && millis()-inclusionStartTime>60000UL*inclusionTime) { // inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode setInclusionMode(false); @@ -109,13 +109,13 @@ void Gateway::checkInclusionFinished() { } -void Gateway::parseAndSend(char *commandBuffer) { +void MyGateway::parseAndSend(char *commandBuffer) { boolean ok = false; char *str, *p, *value=NULL; int i = 0; - uint16_t sensorRadioId = 0; - uint8_t childId = 0; - uint8_t messageType = 0; + uint16_t destination = 0; + uint8_t sensor = 0; + uint8_t command = 0; uint8_t type = 0; // Extract command data coming on serial line @@ -125,13 +125,13 @@ void Gateway::parseAndSend(char *commandBuffer) { ) { switch (i) { case 0: // Radioid (destination) - sensorRadioId = atoi(str); + destination = atoi(str); break; case 1: // Childid - childId = atoi(str); + sensor = atoi(str); break; case 2: // Message type - messageType = atoi(str); + command = atoi(str); break; case 3: // Data type type = atoi(str); @@ -143,19 +143,18 @@ void Gateway::parseAndSend(char *commandBuffer) { i++; } - if (sensorRadioId==GATEWAY_ADDRESS && messageType==M_INTERNAL) { + if (destination==GATEWAY_ADDRESS && command==C_INTERNAL) { // Handle messages directed to gateway if (type == I_VERSION) { // Request for version - serial(PSTR("0;0;%d;%d;%s\n"),M_INTERNAL, I_VERSION, LIBRARY_VERSION); + serial(PSTR("0;0;%d;%d;%s\n"),C_INTERNAL, I_VERSION, LIBRARY_VERSION); } else if (type == I_INCLUSION_MODE) { // Request to change inclusion mode setInclusionMode(atoi(value) == 1); } } else { txBlink(1); - - ok = sendData(GATEWAY_ADDRESS, sensorRadioId, childId, messageType, type, value, strlen(value), false); + ok = sendRoute(msg.build(GATEWAY_ADDRESS, destination, sensor, command, type).set(value)); if (!ok) { errBlink(1); } @@ -163,11 +162,11 @@ void Gateway::parseAndSend(char *commandBuffer) { } -void Gateway::setInclusionMode(boolean newMode) { +void MyGateway::setInclusionMode(boolean newMode) { if (newMode != inclusionMode) inclusionMode = newMode; // Send back mode change on serial line to ack command - serial(PSTR("0;0;%d;%d;%d\n"), M_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0); + serial(PSTR("0;0;%d;%d;%d\n"), C_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0); if (inclusionMode) { inclusionStartTime = millis(); @@ -176,32 +175,32 @@ void Gateway::setInclusionMode(boolean newMode) { // Override normal validate to add error blink if crc check fails -uint8_t Gateway::validate(uint8_t length) { - uint8_t res = Sensor::validate(length); +uint8_t MyGateway::validate(MyMessage message) { + uint8_t res = MySensor::validate(message); if (res == VALIDATE_BAD_CRC) { errBlink(1); } return res; } -void Gateway::processRadioMessage() { - if (messageAvailable()) { +void MyGateway::processRadioMessage() { + if (process()) { // A new message was received from one of the sensors - message_s msg = getMessage(); - if (msg.header.messageType == M_PRESENTATION && inclusionMode) { + MyMessage message = getLastMessage(); + if (message.getCommand() == C_PRESENTATION && inclusionMode) { rxBlink(3); } else { rxBlink(1); } // Pass along the message from sensors to serial line - serial(msg); + serial(message); } checkButtonTriggeredInclusion(); checkInclusionFinished(); } -void Gateway::serial(const char *fmt, ... ) { +void MyGateway::serial(const char *fmt, ... ) { va_list args; va_start (args, fmt ); vsnprintf_P(serialBuffer, MAX_SEND_LENGTH, fmt, args); @@ -213,12 +212,12 @@ void Gateway::serial(const char *fmt, ... ) { } } -void Gateway::serial(message_s msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg.header.from, msg.header.childId, msg.header.messageType, msg.header.type, msg.data); +void MyGateway::serial(MyMessage msg) { + serial(PSTR("%d;%d;%d;%d;%s\n"),msg.getSender(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getString()); } -void Gateway::ledTimersInterrupt() { +void MyGateway::ledTimersInterrupt() { if(countRx && countRx != 255) { // switch led on digitalWrite(pinRx, LOW); @@ -249,13 +248,13 @@ void Gateway::ledTimersInterrupt() { } -void Gateway::rxBlink(uint8_t cnt) { +void MyGateway::rxBlink(uint8_t cnt) { if(countRx == 255) { countRx = cnt; } } -void Gateway::txBlink(uint8_t cnt) { +void MyGateway::txBlink(uint8_t cnt) { if(countTx == 255 && !inclusionMode) { countTx = cnt; } } -void Gateway::errBlink(uint8_t cnt) { +void MyGateway::errBlink(uint8_t cnt) { if(countErr == 255) { countErr = cnt; } } diff --git a/libraries/MySensors/Gateway.h b/libraries/MySensors/MyGateway.h similarity index 100% rename from libraries/MySensors/Gateway.h rename to libraries/MySensors/MyGateway.h diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp new file mode 100644 index 0000000..3b31083 --- /dev/null +++ b/libraries/MySensors/MyMessage.cpp @@ -0,0 +1,259 @@ +#include "MyMessage.h" +#include +#include + + +MyMessage::MyMessage() { + setVersion(PROTOCOL_VERSION); +} + +MyMessage::MyMessage(uint8_t _sensor, uint8_t _type, uint8_t _destination) { + sensor = _sensor; + type = _type; + destination = _destination; + setVersion(PROTOCOL_VERSION); + setCommand(C_SET_VARIABLE); +} + +MyMessage MyMessage::build(uint8_t _sender, uint8_t _destination, uint8_t _sensor, uint8_t _command, uint8_t _type) { + sender = _sender; + destination = _destination; + sensor = _sensor; + type = _type; + setCommand(_command); + setLength(0); + setPayloadType(P_STRING); + return *this; +} + +/*MyMessage MyMessage::failed() { + return getCommand() == C_FAILED +}*/ + +// Header getters + +uint8_t MyMessage::getCRC() { + return crc; +} + +uint8_t MyMessage::getVersion() { + return BF_GET(version_length, 0, 3); +} + +uint8_t MyMessage::getLength() { + return BF_GET(version_length, 3, 5); +} + +uint8_t MyMessage::getCommand() { + return BF_GET(command_payload, 0, 4); +} + +uint8_t MyMessage::getPayloadType() { + return BF_GET(command_payload, 4, 4); +} + +uint8_t MyMessage::getType() { + return type; +} + +uint8_t MyMessage::getSensor() { + return sensor; +} + +uint8_t MyMessage::getSender() { + return sender; +} + +uint8_t MyMessage::getLast() { + return last; +} + +uint8_t MyMessage::getDestination() { + return destination; +} + + +/* Getters for payload converted to desired form */ +void* MyMessage::getCustom() { + return (void *)data; +} + +/* NOTE: Calling this method might affect the payload by converting it to its string representation */ +char* MyMessage::getString(char *buffer) { + uint8_t payloadType = getPayloadType(); + + if (payloadType == P_STRING) { + data[getLength()] = '\0'; + return data; + } else if (buffer != NULL) { + switch(payloadType) { + case P_BYTE: + itoa(bValue, buffer, MAX_PAYLOAD); + break; + case P_INT16: + itoa(iValue, data, MAX_PAYLOAD); + break; + case P_UINT16: + ltoa(uiValue, data, MAX_PAYLOAD); + break; + case P_LONG32: + ltoa(lValue, data, MAX_PAYLOAD); + break; + case P_ULONG32: + ltoa(ulValue, data, MAX_PAYLOAD); + break; + case P_CUSTOM: + // TODO: Ok, what do we do here? We should probably convert this to hex + // Mostly gateway interested in this so we do the special handling of this + // over there. + + break; + } + return buffer; + } else { + return NULL; + } +} + +uint8_t MyMessage::getByte() { + return data[0]; +} +bool MyMessage::getBool() { + return !(data[0]-'0'==0); +} + +double MyMessage::getDouble() { + return strtod(data, NULL); +} +long MyMessage::getLong() { + return lValue; +} +unsigned long MyMessage::getULong() { + return ulValue; +} +int MyMessage::getInt() { + return iValue; +} +unsigned int MyMessage::getUInt() { + return uiValue; +} + +// Header setters +MyMessage MyMessage::setCRC(uint8_t _crc) { + crc = _crc; + return *this; +} + +MyMessage MyMessage::setVersion(uint8_t version) { + BF_SET(version_length, version, 0, 3); + return *this; +} + +MyMessage MyMessage::setLength(uint8_t length) { + BF_SET(version_length, length, 3, 5); + return *this; +} + +MyMessage MyMessage::setCommand(uint8_t command) { + BF_SET(command_payload, command, 0, 4); + return *this; +} + +MyMessage MyMessage::setPayloadType(uint8_t pt) { + BF_SET(command_payload, pt, 4, 4); + return *this; +} + +MyMessage MyMessage::setType(uint8_t _type) { + type = _type; + return *this; +} + +MyMessage MyMessage::setSensor(uint8_t _sensor) { + sensor = _sensor; + return *this; +} + +MyMessage MyMessage::setSender(uint8_t _sender) { + sender = _sender; + return *this; +} + +MyMessage MyMessage::setLast(uint8_t _last) { + last=_last; + return *this; +} + +MyMessage MyMessage::setDestination(uint8_t _destination) { + destination = _destination; + return *this; +} + + +// Set payload +MyMessage MyMessage::set(void* value, uint8_t length) { + setPayloadType(P_CUSTOM); + setLength(length); + memcpy(data, value, min(length, MAX_PAYLOAD)); + return *this; +} + + +MyMessage MyMessage::set(const char* value) { + uint8_t length = strlen(value); + setLength(length); + setPayloadType(P_STRING); + strncpy(data, value, min(length, MAX_PAYLOAD)); + return *this; +} + +MyMessage MyMessage::set(uint8_t value) { + setLength(1); + setPayloadType(P_BYTE); + data[0] = value; + return *this; +} + +MyMessage MyMessage::set(bool value) { + setLength(1); + setPayloadType(P_STRING); + data[0] = value?'1':'0'; + return *this; +} + +MyMessage MyMessage::set(double value, uint8_t decimals) { + dtostrf(value,2,decimals,data); + setLength(strlen(data)); + setPayloadType(P_STRING); + return *this; +} + +MyMessage MyMessage::set(unsigned long value) { + setPayloadType(P_ULONG32); + setLength(4); + ulValue = value; + return *this; +} + +MyMessage MyMessage::set(long value) { + setPayloadType(P_LONG32); + setLength(4); + lValue = value; + return *this; +} + +MyMessage MyMessage::set(unsigned int value) { + setPayloadType(P_INT16); + setLength(2); + uiValue = value; + return *this; +} + +MyMessage MyMessage::set(int value) { + setPayloadType(P_UINT16); + setLength(2); + iValue = value; + return *this; +} + + diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h new file mode 100644 index 0000000..bd80b66 --- /dev/null +++ b/libraries/MySensors/MyMessage.h @@ -0,0 +1,184 @@ +/* + The MySensors library adds a new layer on top of the RF24 library. + It handles radio network routing, relaying and ids. + + Created by Henrik Ekblad + + 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 MyMessage_h +#define MyMessage_h + +#include + +#define PROTOCOL_VERSION 2 +#define MAX_MESSAGE_LENGTH 32 +#define HEADER_SIZE 8 +#define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) +#define GATEWAY_ADDRESS ((uint8_t)0) + +// Message types +typedef enum { + C_PRESENTATION = 0, + C_SET_VARIABLE = 1, + C_REQ_VARIABLE = 2, + C_SET_WITH_ACK = 3, + C_INTERNAL = 4, + C_STREAM = 5, // For Firmaware and other larger chunks of data that need to be divided into pieces. + C_FAILED = 6 +} command; + +// Type of sensor data (for set/req/ack messages) +typedef enum { + V_TEMP,V_HUM, V_LIGHT, V_DIMMER, 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_HEATER, V_HEATER_SW, 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 +} data; + +// Type of internal messages (for internal messages) +typedef enum { + I_BATTERY_LEVEL, I_TIME, I_VERSION, I_REQUEST_ID, + I_INCLUSION_MODE, I_RELAY_NODE, I_PING, I_PING_ACK, + I_LOG_MESSAGE, I_CHILDREN, I_UNIT, I_SKETCH_NAME, I_SKETCH_VERSION +} internal; + +// Type of sensor (for presentation message) +typedef enum { + S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, 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_RELAY, S_LOCK, S_IR, S_WATER +} sensor; + +// Type of data stream (for streamed message) +typedef enum { + ST_FIRMWARE, ST_SOUND, ST_IMAGE +} stream; + +typedef enum { + P_STRING, P_BYTE, P_INT16, P_UINT16, P_LONG32, P_ULONG32, P_CUSTOM +} payload; + +// Possible return values by validate() when doing crc check of received message. +typedef enum { + VALIDATE_OK, VALIDATE_BAD_CRC, VALIDATE_BAD_VERSION +} validationResult; + + + +#define BIT(n) ( 1<<(n) ) +// Create a bitmask of length len. +#define BIT_MASK(len) ( BIT(len)-1 ) +// Create a bitfield mask of length starting at bit 'start'. +#define BF_MASK(start, len) ( BIT_MASK(len)<<(start) ) +// Prepare a bitmask for insertion or combining. +#define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) ) +// Extract a bitfield of length len starting at bit 'start' from y. +#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) ) +// Insert a new bitfield value x into y. +#define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) + + + +class MyMessage +{ +public: + // Constructors + MyMessage(uint8_t sensor, uint8_t type, uint8_t destination=GATEWAY_ADDRESS); + MyMessage(); + + /* Check this after callback to see if request failed */ +// bool failed(); + + + // Getters for header + uint8_t getCRC(); + uint8_t getVersion(); + uint8_t getLength(); + uint8_t getCommand(); + uint8_t getPayloadType(); + uint8_t getType(); + uint8_t getSensor(); + uint8_t getSender(); + uint8_t getLast(); + uint8_t getDestination(); + + // Getters for payload + + + /** + * If payload is something else than P_STRING you can have payload the value converted + * to 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. + */ + char* getString(char *buffer = NULL); + + /** + * Returns payload untouched + */ + void* getCustom(); + + uint8_t getByte(); + bool getBool(); + double getDouble(); + long getLong(); + unsigned long getULong(); + int getInt(); + unsigned int getUInt(); + + // Builder helper + MyMessage build(uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type); + + // Setters for header + MyMessage setCRC(uint8_t crc); + MyMessage setVersion(uint8_t version); + MyMessage setLength(uint8_t length); + MyMessage setCommand(uint8_t command); + MyMessage setPayloadType(uint8_t pt); + MyMessage setType(uint8_t type); + MyMessage setSensor(uint8_t sensor); + MyMessage setSender(uint8_t sender); + MyMessage setLast(uint8_t last); + MyMessage setDestination(uint8_t destination); + + + // Setters for payload + MyMessage set(void* payload, uint8_t length); + MyMessage set(const char* value); + MyMessage set(uint8_t value); + MyMessage set(bool value); + MyMessage set(double value, uint8_t decimals); + MyMessage set(unsigned long value); + MyMessage set(long value); + MyMessage set(unsigned int value); + MyMessage set(int value); + + + uint8_t version_length; // 3 bit - Protocol version + 5 bit - Length of payload + uint8_t command_payload; // 4 bit - Command type + 4 bit - Payload data type + uint8_t type; // 8 bit - Type varies depending on command + uint8_t sensor; // 8 bit - Id of sensor that this message concerns. + uint8_t sender; // 8 bit - Id of sender node + uint8_t last; // 8 bit - Id of last node this message passed + uint8_t destination; // 8 bit - Id of destination node + uint8_t crc; // 8 bit - Message checksum + // Each message can transfer a payload. We add one extra byte for string + // terminator \0 to be "printable" this is not transferred OTA + // This union is used to simplify the construction of the binary transferred int/long values. + union { + uint8_t bValue; + unsigned long ulValue; + long lValue; + unsigned int uiValue; + int iValue; + char data[MAX_PAYLOAD + 1]; + }; + + +}; + +#endif diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp new file mode 100644 index 0000000..e4a3638 --- /dev/null +++ b/libraries/MySensors/MySensor.cpp @@ -0,0 +1,650 @@ + /* + The MySensors library adds a new layer on top of the RF24 library. + It handles radio network routing, relaying and ids. + + Created by Henrik Ekblad + + 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 "MySensor.h" + +MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { +} + + +void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { + failedTransmissions = 0; + + // Start up the radio library + RF24::begin(); + RF24::setAutoAck(1); + RF24::enableAckPayload(); + if (!RF24::isPVariant()) { + debug(PSTR("Radio wiring NOT ok or not using P version of NRF24L01.\n")); + while(1); + } + + RF24::setChannel(channel); + RF24::setPALevel(paLevel); + RF24::setDataRate(dataRate); + RF24::setRetries(5,15); + RF24::setCRCLength(RF24_CRC_16); + RF24::enableDynamicPayloads(); + + + + // All repeater nodes and gateway listen to broadcast pipe (for PING messages) + if (relayMode) { + RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); + } +} + +void MySensor::setupRelayMode(){ + childNodeTable = (uint8_t *) malloc(256); + // Read routing table from EEPROM + for (unsigned int i=0;i0 && route<255 && dest != GATEWAY_ADDRESS) { + debug(PSTR("Routing message to %d.\n"), route); + // Message destination is not gateway and is in routing table for this node. + // Send it downstream + return sendWrite(route, message); + } + } + + if (radioId != GATEWAY_ADDRESS) { + debug(PSTR("Relaying message back to gateway.\n")); + // Should be routed back to gateway. + bool ok = sendWrite(relayId, message); + + if (!ok) { + // Failure when sending to relay node. The relay node might be down and we + // need to find another route to gateway. Max 20 retries before giving up. + if (failedTransmissions > FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { + findRelay(); + } + failedTransmissions++; + } else { + failedTransmissions = 0; + } + return ok; + } + return false; +} + + + + + +boolean MySensor::sendWrite(uint8_t next, MyMessage message) { + uint8_t length = message.getLength(); + message.setLast(radioId); + message.setCRC(crc8Message(message)); + // Only print payload if string type + debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d:%s\n"), + message.getSender(),message.getDestination(), message.getLast(), next, message.getSensor(), message.getCommand(), message.getType(), message.getCRC(), message.getString()); + + bool broadcast = message.getCommand() == C_INTERNAL && message.getType() == I_PING; + + // Make sure radio has powered up + RF24::powerUp(); + RF24::stopListening(); + RF24::openWritingPipe(TO_ADDR(next)); + bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); + RF24::startListening(); + + if (ok) + debug(PSTR("Sent successfully\n")); + else + debug(PSTR("Send failed\n")); + + + return ok; +} + +void MySensor::sendInternal(uint8_t variableType, const char *value) { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType).set(value)); +} + + +bool MySensor::send(MyMessage message, bool enableAck, uint8_t retry) { + message.setSender(radioId); + if (enableAck) { + message.setCommand(C_SET_WITH_ACK); + return wait(message, C_SET_VARIABLE, retry).getCommand() != C_FAILED; + } else { + return sendRoute(message);; + } +} + + +void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType).set(LIBRARY_VERSION)); +} + +void MySensor::sketchInfo(const char *name, const char *version) { + if (name != NULL) + sendInternal(I_SKETCH_NAME, name); + if (version != NULL) + sendInternal(I_SKETCH_VERSION, version); +} + +MyMessage MySensor::get( uint8_t childSensorId, uint8_t variableType, uint8_t destination, uint8_t retry) { + msg.build(radioId, destination, childSensorId, C_REQ_VARIABLE, variableType); + return wait(msg, C_SET_VARIABLE, retry); +} + +void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { + process(); // Make sure to process any queued messages before sending new request + sendRoute(msg.build(radioId, destination, childSensorId, C_REQ_VARIABLE, variableType)); +} + + + +void MySensor::sendBatteryLevel(int value) { + sendInternal(I_BATTERY_LEVEL, ltoa(value, convBuffer, 10)); +} + +MyMessage MySensor::wait(MyMessage message, uint8_t expectedResponseCommand, uint8_t retry) { + while (retry>0) { + + uint8_t sensor = message.getSensor(); + uint8_t type = message.getType(); + sendRoute(message); + uint8_t i = 0; + while (i < 100) { // 1 seconds timeout before re-sending status request + while (process()) { + // Check that it is right type of message and not a routing message + if (msg.getCommand() == expectedResponseCommand && + msg.getType() == type && + msg.getSensor() == sensor) { + return msg; + } + } + delay(10); + i++; + } + retry--; + } + msg.setCommand(C_FAILED); + return msg; +} + + + + +MyMessage MySensor::getInternal(uint8_t variableType, uint8_t retry) { + msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType); + return wait(msg, C_INTERNAL, retry); +} + +/*unsigned long MySensor::getTime(uint8_t retry) { + MyMessage message = getInternal(I_TIME, retry); + return message == NULL ? 0UL : message.getULong(); +}*/ + +void MySensor::requestTime() { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME)); +} + + +/*bool MySensor::isMetricSystem(uint8_t retry) { + MyMessage message = getInternal(I_UNIT, retry); + return (message == NULL) + return true; + else + return message.getByte() == 'M'; +}*/ + +void MySensor::requestIsMetricSystem() { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_UNIT)); +} + + +boolean MySensor::process() { + uint8_t pipe; + boolean available = RF24::available(&pipe); + + if (available) { + debug(PSTR("Message available on pipe %d\n"),pipe); + } + + if (available && pipe<7) { + uint8_t len = RF24::getDynamicPayloadSize(); + RF24::read(&msg, len); + RF24::writeAckPayload(pipe,&pipe, 1 ); + + uint8_t valid = validate(msg); + boolean ok = valid == VALIDATE_OK; + + // Make sure string gets terminated ok for full string messages. + msg.data[len - HEADER_SIZE ] = '\0'; + debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), + msg.getSender(),msg.getDestination(), msg.getLast(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getCRC(), valid==0?"ok":valid==1?"ec":"ev", msg.getString()); + + if (ok) { + uint8_t command = msg.getCommand(); + uint8_t type = msg.getType(); + uint8_t sender = msg.getSender(); + uint8_t last = msg.getLast(); + uint8_t destination = msg.getDestination(); + + if (relayMode && command == C_INTERNAL && type == I_PING) { + // Relays should always answer ping messages + // Wait a random delay of 0-2 seconds to minimize collision + // between ping ack messages from other relaying nodes + randomSeed(millis()); + delay(random(2000)); + uint8_t replyTo = msg.getSender(); + msg.build(radioId, replyTo, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK).set(distance); + sendWrite(replyTo, msg); + } else if (sender == radioId) { + // This message is addressed to this node + if (relayMode && command == C_INTERNAL) { + // Handle all internal messages to this node + if (type == I_RELAY_NODE && destination != GATEWAY_ADDRESS) { + // Someone at sensor net gateway side wants this node to refresh its relay node + findRelay(); + // Send relay information back to sensor net gateway node device. + sendInternal(I_RELAY_NODE, ltoa(relayId, convBuffer, 10)); + return false; + } else if (type == I_CHILDREN && destination != GATEWAY_ADDRESS) { + debug(PSTR("Route command received")); + if (msg.getByte() == 'F') { + // Send in as many children we can fit in a message (binary) + sendChildren(); + } else if (msg.getByte() == 'C') { + // Clears child relay data for this node + clearChildRoutes(); + } + return false; + } + // Return rest of internal messages back to sketch... + if (last != GATEWAY_ADDRESS) + addChildRoute(sender, last); + + return true; + } else { + // If this is variable message from sensor net gateway. Send ack back. + debug(PSTR("Message addressed for this node.\n")); + + // Send set-message back to sender if sender wants this + if (command == C_SET_WITH_ACK) { + sendAck(msg); + // The library user should not need to care about this ack request. Just treat it as a normal SET. + msg.setCommand(C_SET_VARIABLE); + } + // Call incoming message callback if available + if (msgCallback != NULL) { + msgCallback(msg); + } + + if (last != GATEWAY_ADDRESS) + addChildRoute(sender, last); + + // Return true if message was addressed for this node... + return true; + } + } else if (relayMode) { + // We should try to relay this message to another node + + uint8_t route = getChildRoute(msg.getDestination()); + if (route>0 && route<255) { + debug(PSTR("Routing message to child node.\n")); + // This message should be forwarded to a child node. If we send message + // to this nodes pipe then all children will receive it because the are + // all listening to this nodes pipe. + // + // +----B + // -A + // +----C------D + // + // We're node C, Message comes from A and has destination D + // + // lookup route in table and send message there + sendWrite(route, msg); + } else if (pipe == CURRENT_NODE_PIPE) { + // A message comes from a child node and we have no + // route for it. + // + // +----B + // -A + // +----C------D <-- Message comes from D + // + // We're node C + // + // Message should be passed to node A (this nodes relay) + + debug(PSTR("Routing message to relay.\n")); + // This message should be routed back towards sensor net gateway + sendWrite(relayId, msg); + // Add this child to our "routing table" if it not already exist + addChildRoute(sender, last); + } else { + // We're snooped a message directed to gateway from another branch + // Make sure to remove the sender node from our routing table. + // + // +-----B <-- Message comes from here + // -A + // +-----C <-- We're here + // + // So the sender of message should never be in our + // routing table. + // + // We could also end up in this else-statement if message + // destination has no route. + // This can happen if a node never has sent any message to + // sensor net gateway (which will build up routing information in all nodes + // that gets passed). + if (radioId == GATEWAY_ADDRESS) { + // We should perhaps inform gateway about the no-route situation?! + debug(PSTR("Unknown route from GW\n")); + + } else { + debug(PSTR("Remove child node from routing table.\n")); + removeChildRoute(sender); + } + } + + } + } + } + return false; +} + + + + + +MyMessage MySensor::getLastMessage() { + return msg; +} + + +/* + * calculate CRC8 on MyMessage data taking care of data structure and protocol version + */ +uint8_t MySensor::crc8Message(MyMessage message) { + uint8_t len = message.getLength(); + uint8_t crc = 0x00; + uint8_t loop_count; + uint8_t bit_counter; + uint8_t data; + uint8_t feedback_bit; + uint8_t number_of_bytes_to_read = (uint8_t)sizeof(message); + + // Must set crc to a constant value. + message.setCRC(0); + + // fill unused space by zeroes for string data only + if(len>=0 && len < sizeof(message.data)-1) { + memset(&message.data[len], 0, sizeof(message.data) - 1 - len); + } + + + for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) + { + data = ((uint8_t*)&message)[loop_count]; + + bit_counter = 8; + do { + feedback_bit = (crc ^ data) & 0x01; + + if ( feedback_bit == 0x01 ) { + crc = crc ^ 0x18; //0X18 = X^8+X^5+X^4+X^0 + } + crc = (crc >> 1) & 0x7F; + if ( feedback_bit == 0x01 ) { + crc = crc | 0x80; + } + + data = data >> 1; + bit_counter--; + + } while (bit_counter > 0); + } + return crc; +} + + +uint8_t MySensor::validate(MyMessage message) { + uint8_t oldCrc = message.getCRC(); + uint8_t newCrc = crc8Message(message); + + if(!(message.getVersion() == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; + + if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; + return VALIDATE_OK; +} + + +void MySensor::addChildRoute(uint8_t childId, uint8_t route) { + if (childNodeTable[childId] != route) { + childNodeTable[childId] = route; + EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, route); + } +} + +void MySensor::removeChildRoute(uint8_t childId) { + if (childNodeTable[childId] != 0xff) { + childNodeTable[childId] = 0xff; + EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, 0xff); + } +} + +uint8_t MySensor::getChildRoute(uint8_t childId) { + return childNodeTable[childId]; +} + +void MySensor::clearChildRoutes() { + debug(PSTR("Clear child routing data\n")); + for (unsigned int i=0;i< sizeof(childNodeTable); i++) { + removeChildRoute(i); + } + sendInternal(I_CHILDREN, ""); +} + +void MySensor::sendChildren() { + // Send in which children that is using this node as an relay. + // TODO: Fix this + debug(PSTR("Dump beginning of routing table\n")); + + for (int i=0;i< 50; i++) { +// Serial.println(childNodeTable[i]); + debug(PSTR("rt:%d, %d\n"), i, getChildRoute(i) ); + } + + sendInternal(I_CHILDREN, "not implemented"); +} + + + + + +#ifdef DEBUG +void MySensor::debugPrint(const char *fmt, ... ) { + char fmtBuffer[300]; + if (radioId == GATEWAY_ADDRESS) { + // prepend debug message to be handled correctly by gw + Serial.print("0;0;4;11;"); + } + va_list args; + va_start (args, fmt ); + vsnprintf_P(fmtBuffer, 300, fmt, args); + va_end (args); + if (radioId == GATEWAY_ADDRESS) { + // Truncate message if this is gateway node + vsnprintf_P(fmtBuffer, 60, fmt, args); + fmtBuffer[59] = '\n'; + fmtBuffer[60] = '\0'; + } else { + vsnprintf_P(fmtBuffer, 299, fmt, args); + } + va_end (args); + Serial.print(fmtBuffer); + + + //Serial.print("0;0;4;11;Mem free:"); + //Serial.println(freeRam()); +} +#endif + + +#ifdef DEBUG +int MySensor::freeRam (void) { + extern int __heap_start, *__brkval; + int v; + return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} +#endif diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h new file mode 100644 index 0000000..1cc3537 --- /dev/null +++ b/libraries/MySensors/MySensor.h @@ -0,0 +1,237 @@ +/* + The MySensors library adds a new layer on top of the RF24 library. + It handles radio network routing, relaying and ids. + + Created by Henrik Ekblad + + 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 MySensor_h +#define MySensor_h + +#include "Version.h" // Auto generated by bot +#include "MyConfig.h" +#include "MyMessage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define debug(x,...) debugPrint(x, ##__VA_ARGS__) +#else +#define debug(x,...) +#endif + +#define BAUD_RATE 115200 + +#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of radioId. +#define NODE_CHILD_ID 0xFF // Node child id is always created for when a new sensor is detected +#define EEPROM_RADIO_ID_ADDRESS 0 // Where to store radio id in EEPROM +#define EEPROM_RELAY_ID_ADDRESS 1 // Where to store relay id in EEPROM +#define EEPROM_DISTANCE_ADDRESS 2 // Where to store distance to gateway in EEPROM +#define EEPROM_ROUTES_ADDRESS ((uint8_t)3) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. + + +// This is the radioId for sensor net gateway receiver sketch (where all sensors should send their data). +// This is also act as base value for sensor radioId +#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) +#define GATEWAY_ADDRESS ((uint8_t)0) +#define BROADCAST_ADDRESS ((uint8_t)0xFF) +#define TO_ADDR(x) (BASE_RADIO_ID + x) + +#define WRITE_PIPE ((uint8_t)0) +#define CURRENT_NODE_PIPE ((uint8_t)1) +#define BROADCAST_PIPE ((uint8_t)2) + +#define SEND_RETRIES 3 +#define FIND_RELAY_RETRIES 2 +#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 20 + + +class MySensor : public RF24 +{ + public: + /** + * Constructor + * + * Creates a new instance of Sensor class. + * + * @param _cepin The pin attached to RF24 Chip Enable on the RF module (defualt 9) + * @param _cspin The pin attached to RF24 Chip Select (default 10) + */ + MySensor(uint8_t _cepin=9, uint8_t _cspin=10); + + /** + * Begin operation of the MySensors library + * + * Call this in setup(), before calling any other sensor net library methods. + * @param incomingMessageCallback Callback function for incoming messages. Default is NULL + * @param relayMode Activate relay mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false + * @param radioId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. + * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX + * @param channel Radio channel. Default is channel 76 + * @param dataRate Radio transmission speed. Default RF24_1MBPS + */ + + void begin(void (* msgCallback)(MyMessage)=NULL, boolean relayMode=false, uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); + + /** + * Return the nodes radioId. + */ + uint8_t getRadioId(); + + /** + * 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 sensorChildId Sensor child id for the presented. Choose anything between 0-254. + * @param sensorType The sensor type. See sensor typedef in Message.h. + */ + void present(uint8_t sensorChildId, uint8_t sensorType); + + /** + * 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 + */ + void sketchInfo(const char *name, const char *version); + + /** + * 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. + * @param retry Number of attempts before giving up. Default is SEND_RETRIES + * @return true Retuns false if ack is enabled and no ack came back + */ + bool send(MyMessage msg, bool ack=false, uint8_t retry=SEND_RETRIES); + + /** + * Send this nodes battery level to gateway. + * @param level Level between 0-100(%) + */ + void sendBatteryLevel(int level); + + /** + * Fetches a value from gateway or some other sensor in the radio network. Resends request and waits for response. + * Note that if you have registered a callback in begin() it will also be called before message is returned + * by get() + * + * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. + * @param variableType The variableType to fetch + * @param destination The radioId of other node in radio network. Default is gateway + * @param number of attempts to fetch + * @return Message response + */ + MyMessage get(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS, uint8_t retry=SEND_RETRIES); + + /** + * Requests a value from gateway or some other sensor in the radio network. + * Make sure to add callback in begin() to handle request response. + * + * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. + * @param variableType The variableType to fetch + * @param callback for result. This will be called when a message reply is received or request fails (see status of second argument). + * @param destination The radioId of other node in radio network. Default is gateway + */ + /*void (* msgCallback)(MyMessage, bool)=NULL, */ + void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); + + + /** + * Fetches time from sensor net gateway. + * + * @return Seconds since 1970, 0 if failed. + */ +// unsigned long getTime(uint8_t retry=SEND_RETRIES); + void requestTime(); + + /** + * Fetches unit setting from sensor net gateway. Returns true if metric system has been selected which means + * that sensor should report it's information in: celsius, meter, cm, gram, km/h, m/s etc.. + * If false is returned the sensor should report data in imperial system which means + * fahrenheit, feet, gallon, mph etc... + */ + void requestIsMetricSystem(); +// bool isMetricSystem(uint8_t retry=SEND_RETRIES); + + + /** + * Processes incoming messages to this node. If this is a relaying node it will + * Returns true if there is a message addressed for this node just was received. + * Use callback to handle incoming messages. + */ + boolean process(void); + + /** + * Returns the last received message + */ + MyMessage getLastMessage(void); + + + + + + +#ifdef DEBUG + void debugPrint(const char *fmt, ... ); + int freeRam(); +#endif + + + protected: + MyMessage msg; // Buffer for incoming messages. + uint8_t failedTransmissions; + boolean relayMode; + uint8_t radioId; + uint8_t distance; // This nodes distance to sensor net gateway (number of hops) + uint8_t relayId; + char convBuffer[20]; + + void setupRelayMode(); + void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); + void findRelay(); + boolean sendRoute(MyMessage message); + boolean sendWrite(uint8_t dest, MyMessage message); + void sendInternal(uint8_t variableType, const char *value); + boolean sendAck(MyMessage message); + boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binaryMessage); + uint8_t validate(MyMessage message); + void sendChildren(); + + + private: + // Callbacks for reqeusts +/* struct callback { + bool active; // Deactivates callback after each reply + uint8_t command; // Command type + uint8_t sender; + uint8_t sensor; + uint8_t type; + void (*msgCallback)(MyMessage, bool); + };*/ + MyMessage ack; // Buffer for ack messages. + uint8_t *childNodeTable; // In memory buffer for routing to other nodes. Also stored in EEPROM + void (*msgCallback)(MyMessage); // Callback for undefined incoming messages + + void initializeRadioId(); + uint8_t crc8Message(MyMessage); + MyMessage wait(MyMessage msg, uint8_t expectedReceiveType, uint8_t retry); + MyMessage getInternal(uint8_t variableType, uint8_t retry=SEND_RETRIES); + + uint8_t getChildRoute(uint8_t childId); + void addChildRoute(uint8_t childId, uint8_t route); + void removeChildRoute(uint8_t childId); + void clearChildRoutes(); +}; + +#endif diff --git a/libraries/MySensors/Relay.cpp b/libraries/MySensors/Relay.cpp deleted file mode 100644 index 19f5b0d..0000000 --- a/libraries/MySensors/Relay.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 "Relay.h" - -Relay::Relay(uint8_t _cepin, uint8_t _cspin) : Sensor(_cepin, _cspin) { - isRelay = true; -} - - -void Relay::begin(uint8_t radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - Sensor::begin(radioId, paLevel, channel, dataRate); - // Read routing table from EEPROM - for (unsigned int i=0;i0) { - debug(PSTR("Found child in routing table. Sending to %d\n"),route); - buildMsg(from, to, childId, messageType, type, data, length, binary); - // Found node in route table - ok = sendWrite(route, msg, length); - } else if (radioId == GATEWAY_ADDRESS) { - // If we're GW (no parent...). As a last resort try sending message directly to node. - debug(PSTR("No route... try sending direct.\n")); - buildMsg(from, to, childId, messageType, type, data, length, binary); - // Found node in route table - ok = sendWrite(to, msg, length); - } else { - // We are probably a repeater node which should send message back to relay - ok = Sensor::sendData(from, to, childId, messageType, type, data, length, binary); - } - } else { - debug(PSTR("Message too large\n")); - } - return ok; -} - - - -boolean Relay::send(message_s message, int length) { - bool ok = true; - uint8_t route = getChildRoute(msg.header.to); - - if (route>0 && route<255 && message.header.to != GATEWAY_ADDRESS) { - debug(PSTR("Routing message to %d.\n"), route); - // Message destination is not gateway and is in routing table for this node. - // Send it downstream - ok = sendWrite(route, message, length); - - - } else if (radioId != GATEWAY_ADDRESS) { - debug(PSTR("Sending message back towards gw.\n")); - // Should be routed back to gateway. Sensor code knows how to do this. - ok = Sensor::send(message, length); - } - - if (!ok) { - debug(PSTR("No ack received.\n")); - } - return ok; -} - - - -boolean Relay::messageAvailable() { - uint8_t pipe; - boolean available = RF24::available(&pipe); - - if (available) { - debug(PSTR("Message available on pipe %d\n"),pipe); - } - - if (available && pipe<7) { - uint8_t len = RF24::getDynamicPayloadSize(); - RF24::read(&msg, len); - RF24::writeAckPayload(pipe,&pipe, 1 ); - - uint8_t valid = validate(len-sizeof(header_s)); - boolean ok = valid == VALIDATE_OK; - - // Make sure string gets terminated ok for full sized messages. - msg.data[len - sizeof(header_s) ] = '\0'; - debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), - msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); - - - //boolean ok = readMessage(); - if (ok) { - if (msg.header.messageType == M_INTERNAL && - msg.header.type == I_PING) { - // We always answer ping messages - // Wait a random delay of 0-2 seconds to minimize collision - // between ping ack messages from other relaying nodes - randomSeed(millis()); - delay(random(2000)); - ltoa(distance, convBuffer, 10); - uint8_t to = msg.header.from; - debug(PSTR("Answer ping message. %d\n"), strlen(convBuffer)); - buildMsg(radioId, to, NODE_CHILD_ID, M_INTERNAL, I_PING_ACK, convBuffer,strlen(convBuffer), false); - sendWrite(to, msg, strlen(convBuffer)); - } else if (msg.header.to == radioId) { - // This message is addressed to this node - if (msg.header.messageType == M_INTERNAL) { - // Handle all internal messages to this node - if (msg.header.type == I_RELAY_NODE && msg.header.to != GATEWAY_ADDRESS) { - // Someone at sensor net gateway side wants this node to refresh its relay node - findRelay(); - // Send relay information back to sensor net gateway node device. - sendInternal(I_RELAY_NODE, ltoa(relayId, convBuffer, 10)); - return false; - } else if (msg.header.type == I_CHILDREN && msg.header.to != GATEWAY_ADDRESS) { - debug(PSTR("Route command received")); - if (strncmp(msg.data,"F", 1) == 0) { - // Send in as many children we can fit in a message (binary) - sendChildren(); - } else if (strncmp(msg.data,"C", 1) == 0) { - // Clears child relay data for this node - clearChildRoutes(); - } - return false; - } - // Return rest of internal messages back to sketch... - if (msg.header.last != GATEWAY_ADDRESS) - addChildRoute(msg.header.from, msg.header.last); - - return true; - } else { - // If this is variable message from sensor net gateway. Send ack back. - debug(PSTR("Message addressed for this node.\n")); - - // Send set-message back to sender if sender wants this - if (msg.header.messageType == M_SET_WITH_ACK) { - sendVariableAck(); - // The library user should not need to care about this ack request. Just treat it as a normal SET. - msg.header.messageType = M_SET_VARIABLE; - } - - // Return message to waiting sketch... - if (msg.header.last != GATEWAY_ADDRESS) - addChildRoute(msg.header.from, msg.header.last); - - return true; - } - } else { - // We should probably try to relay this message - relayMessage(len-sizeof(msg.header), pipe); - } - } - } - return false; -} - - - - -void Relay::relayMessage(uint8_t length, uint8_t pipe) { - uint8_t route = getChildRoute(msg.header.to); - if (route>0 && route<255) { - debug(PSTR("Routing message to child node.\n")); - // This message should be forwarded to a child node. If we send message - // to this nodes pipe then all children will receive it because the are - // all listening to this nodes pipe. - // - // +----B - // -A - // +----C------D - // - // We're node C, Message comes from A and has destination D - // - // lookup route in table and send message there - sendWrite(route, msg, length); - } else if (pipe == CURRENT_NODE_PIPE) { - // A message comes from a child node and we have no - // route for it. - // - // +----B - // -A - // +----C------D <-- Message comes from D - // - // We're node C - // - // Message should be passed to node A (this nodes relay) - - debug(PSTR("Routing message to relay.\n")); - // This message should be routed back towards sensor net gateway - sendWrite(relayId, msg, length); - // Add this child to our "routing table" if it not already exist - addChildRoute(msg.header.from, msg.header.last); - } else { - // We're snooped a message directed to gateway from another branch - // Make sure to remove the sender node from our routing table. - // - // +-----B <-- Message comes from here - // -A - // +-----C <-- We're here - // - // So the sender of message should never be in our - // routing table. - // - // We could also end up in this else-statement if message - // destination has no route. - // This can happen if a node never has sent any message to - // sensor net gateway (which will build up routing information in all nodes - // that gets passed). - if (radioId == GATEWAY_ADDRESS) { - // We should perhaps inform gateway about the no-route situation?! - debug(PSTR("Unknown route from GW\n")); - - } else { - debug(PSTR("Remove child node from routing table.\n")); - removeChildRoute(msg.header.from); - } - } -} - - -void Relay::addChildRoute(uint8_t childId, uint8_t route) { - if (childNodeTable[childId] != route) { - childNodeTable[childId] = route; - EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, route); - } -} - -void Relay::removeChildRoute(uint8_t childId) { - if (childNodeTable[childId] != 0xff) { - childNodeTable[childId] = 0xff; - EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, 0xff); - } -} - -uint8_t Relay::getChildRoute(uint8_t childId) { - return childNodeTable[childId]; -} - -void Relay::clearChildRoutes() { - debug(PSTR("Clear child routing data\n")); - for (unsigned int i=0;i< sizeof(childNodeTable); i++) { - removeChildRoute(i); - } - sendInternal(I_CHILDREN, ""); -} - -void Relay::sendChildren() { - // Send in which children that is using this node as an relay. - // TODO: Fix this - debug(PSTR("Dump beginning of routing table\n")); - - for (int i=0;i< 50; i++) { -// Serial.println(childNodeTable[i]); - debug(PSTR("rt:%d, %d\n"), i, getChildRoute(i) ); - } - - sendInternal(I_CHILDREN, "not implemented"); -} - - diff --git a/libraries/MySensors/Relay.h b/libraries/MySensors/Relay.h deleted file mode 100644 index c5f3272..0000000 --- a/libraries/MySensors/Relay.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 Relay_h -#define Relay_h - -#include "Sensor.h" - -#ifdef DEBUG -#define debug(x,...) debugPrint(x, ##__VA_ARGS__) -#else -#define debug(x,...) -#endif - - - -#define EEPROM_ROUTES_ADDRESS ((uint8_t)3) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. - - -class Relay : public Sensor -{ - - public: - /** - * Constructor - * - * Creates a new instance of Relay class. - * - * @param _cepin The pin attached to RF24 Chip Enable on the RF module (default 9) - * @param _cspin The pin attached to RF24 Chip Select (default 10) - */ - Relay(uint8_t _cepin = 9, uint8_t _cspin = 10); - - void begin(uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); - boolean messageAvailable(); - boolean send(message_s message, int length); - - boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary); - - protected: - void sendChildren(); - - private: - uint8_t childNodeTable[256]; // Buffer to store child node information. Store this in EEPROM - - uint8_t getChildRoute(uint8_t childId); - void addChildRoute(uint8_t childId, uint8_t route); - void removeChildRoute(uint8_t childId); - void clearChildRoutes(); - void relayMessage(uint8_t length, uint8_t pipe); - -}; - -#endif diff --git a/libraries/MySensors/Sensor.cpp b/libraries/MySensors/Sensor.cpp deleted file mode 100644 index 72adc9f..0000000 --- a/libraries/MySensors/Sensor.cpp +++ /dev/null @@ -1,555 +0,0 @@ - /* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 "Sensor.h" - - -Sensor::Sensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { - isRelay = false; -} - - -void Sensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - failedTransmissions = 0; - - // Start up the radio library - RF24::begin(); - RF24::setAutoAck(1); - RF24::enableAckPayload(); - if (!RF24::isPVariant()) { - debug(PSTR("Sorry, you'll need to use the P version of NRF24L01.\n")); - while(1); - } - - RF24::setChannel(channel); - RF24::setPALevel(paLevel); - RF24::setDataRate(dataRate); - RF24::setRetries(5,15); - RF24::setCRCLength(RF24_CRC_16); - RF24::enableDynamicPayloads(); - - - - // All repeater nodes and gateway listen to broadcast pipe (for PING messages) - if (isRelay) { - RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); - } -} - -void Sensor::begin(uint8_t _radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - Serial.begin(BAUD_RATE); - - radioId = _radioId; - - debug(PSTR("Started %s.\n"), isRelay?"relay":"sensor"); - - - setupRadio(paLevel, channel, dataRate); - - // Fetch relay from EEPROM - relayId = EEPROM.read(EEPROM_RELAY_ID_ADDRESS); - // Fetch distance from eeprom - distance = EEPROM.read(EEPROM_DISTANCE_ADDRESS); - if (relayId == 0xff) { - // No relay previously fetched and stored in eeprom. Try to find one. - findRelay(); - } - debug(PSTR("Relay=%d, distance=%d\n"), relayId, distance); - - initializeRadioId(); - - // Open reading pipe for messages directed to this node (set write pipe to same) - RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(radioId)); - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId)); - - // Send presentation for this radio node - sendSensorPresentation(NODE_CHILD_ID, isRelay? S_ARDUINO_RELAY : S_ARDUINO_NODE); - - // Send relay information back to sensor net gateway. - sendInternal(I_RELAY_NODE, itoa(relayId, convBuffer, 10)); - - // Initialize ack message header - ack.header.version = PROTOCOL_VERSION; - ack.header.binary = 0; - ack.header.from = radioId; - ack.header.to = GATEWAY_ADDRESS; - ack.header.messageType = M_SET_VARIABLE; -} - - -uint8_t Sensor::getRadioId() { - return radioId; -} - -void Sensor::initializeRadioId() { - if (radioId == AUTO) { - radioId = EEPROM.read(EEPROM_RADIO_ID_ADDRESS); - if (radioId == 0xFF || radioId == 0) { - radioId = AUTO; - debug(PSTR("No radio id found in EEPROM fetching one from sensor net gateway\n")); - // No radio id has been fetched yet ant EEPROM is unwritten. - // Request new id from sensor net gateway. Use radioId 4095 temporarily - // to be able to receive my correct nodeId. - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId)); - radioId = atoi(getInternal(I_REQUEST_ID)); - // Write id to EEPROM - if (radioId == AUTO) { // sensor net gateway will return max id if all sensor id are taken - debug(PSTR("Sensor network is full! You already have the maximum of sensors!\n")); - while (1) {} // Wait here. Nothing else we can do... - } else { - debug(PSTR("Radio id received: %d\n"), radioId); - EEPROM.write(EEPROM_RADIO_ID_ADDRESS, radioId); - } - } - debug(PSTR("Radio id stored in EEPROM was: %d\n"), radioId); - } -} - - -void Sensor::findRelay() { - // This can be improved by using status = read_register(OBSERVE_TX,&observe_tx,1) on the - // sending side.. the problem is getting this information here without too much fuss. - // Try to find relay 3 times before giving up - if (radioId == GATEWAY_ADDRESS) - return; // Gateway has no business here! - - failedTransmissions = 0; - - // Open a reading pipe for current radioId (if it differs from broadcast) - if (radioId != BROADCAST_PIPE) { - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId)); - debug(PSTR("Open ping reading pipe: %d\n"), radioId); - } - distance = 255; - uint8_t oldRelayId = relayId; - uint8_t retries=0; - while (distance == 255 && retries FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { - findRelay(); - } - failedTransmissions++; - } else { - failedTransmissions = 0; - } - return ok; -} - - -boolean Sensor::sendWrite(uint8_t dest, message_s message, int length) { - message.header.last = radioId; - message.header.crc = crc8Message(message, length); - debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d: %s\n"), - message.header.from,message.header.to, message.header.last, dest, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data); - - bool broadcast = message.header.messageType == M_INTERNAL && message.header.type == I_PING; - - RF24::stopListening(); - RF24::openWritingPipe(TO_ADDR(dest)); - bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length), broadcast); - RF24::startListening(); - - - //RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit - //delay(50); - - if (ok) - debug(PSTR("Sent successfully\n")); - else - debug(PSTR("Send failed\n")); - - - return ok; -} - -void Sensor::sendInternal(uint8_t variableType, const char *value) { - sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, variableType, value, strlen(value), false); -} - -// This will be added in 1.4 -/*void Sensor::sendVariable(uint8_t childId, uint8_t variableType, - const char *value, length) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_SET_VARIABLE, variableType, value, length, true); -}*/ - - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, - const char *value) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_SET_VARIABLE, variableType, value, strlen(value), false); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, double value, int decimals) { - sendVariable(childId, variableType, dtostrf(value,2,decimals,convBuffer)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, int value) { - sendVariable(childId, variableType, itoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, long value) { - sendVariable(childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, unsigned long value) { - sendVariable(childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, unsigned int value) { - sendVariable(childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, - const char *value) { - sendData(radioId, nodeId, childId, M_SET_VARIABLE, variableType, value, strlen(value), false); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, double value, int decimals) { - sendVariable(nodeId, childId, variableType, dtostrf(value,2,decimals,convBuffer)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, int value) { - sendVariable(nodeId, childId, variableType, itoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, long value) { - sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, unsigned long value) { - sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, unsigned int value) { - sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendSensorPresentation(uint8_t childId, uint8_t sensorType) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_PRESENTATION, sensorType, LIBRARY_VERSION, strlen(LIBRARY_VERSION), false); -} - -void Sensor::requestStatus(uint8_t childId, uint8_t variableType) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, variableType, "", 0, false); -} - -void Sensor::requestStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) { - sendData(radioId, nodeId, childId, M_REQ_VARIABLE, variableType, "", 0, false); -} - -void Sensor::sendSketchInfo(const char *name, const char *version) { - sendSketchName(name); - sendSketchVersion(version); -} - -void Sensor::sendSketchName(const char *name) { - if (name != NULL) - sendInternal(I_SKETCH_NAME, name); -} - -void Sensor::sendSketchVersion(const char *version) { - if (version != NULL) - sendInternal(I_SKETCH_VERSION, version); -} - -void Sensor::sendBatteryLevel(int value) { - sendInternal(I_BATTERY_LEVEL, ltoa(value, convBuffer, 10)); -} - -char* Sensor::get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType) { - while (1) { - sendData(radioId, nodeId, childId, sendType, variableType, "", 0, false); - uint8_t i = 0; - while (i < 100) { // 5 seconds timeout before re-sending status request - while (messageAvailable()) { - // Check that it is right type of message and not a routing message - if (msg.header.messageType == receiveType && - msg.header.type == variableType && - msg.header.childId == childId) { - return msg.data; - } - } - delay(50); - i++; - } - } - return NULL; -} - -char* Sensor::getStatus(uint8_t childId, uint8_t variableType) { - return get(GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, M_SET_VARIABLE, variableType); -} - -char * Sensor::getStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) { - return get(nodeId, childId, M_REQ_VARIABLE, M_SET_VARIABLE, variableType); -} - - - -char* Sensor::getInternal(uint8_t variableType) { - return get(GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, M_INTERNAL, variableType); -} - -unsigned long Sensor::getTime() { - return atol(getInternal(I_TIME)); -} - -void Sensor::requestTime() { - sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_TIME, "", 0, false); -} - - -bool Sensor::isMetricSystem() { - return getInternal(I_UNIT)[0] == 'M'; -} - -void Sensor::requestIsMetricSystem() { - sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_UNIT, "", 0, false); -} - - -boolean Sensor::messageAvailable() { - uint8_t pipe; - boolean available = RF24::available(&pipe); - if (available && pipe<7) { - - uint8_t len = RF24::getDynamicPayloadSize(); - RF24::read(&msg, len); - RF24::writeAckPayload(pipe,&pipe, 1 ); - - if (available) { - debug(PSTR("Message available on pipe %d\n"),pipe); - } - - uint8_t valid = validate(len-sizeof(header_s)); - boolean ok = valid == VALIDATE_OK; - - // Make sure string gets terminated ok for full sized messages. - msg.data[len - sizeof(header_s) ] = '\0'; - debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), - msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); - - // boolean ok = readMessage(); - if (ok && msg.header.to == radioId) { - // This message is addressed to this node - debug(PSTR("Message addressed for this node.\n")); - // Send set-message back to sender if sender wants this - if (msg.header.messageType == M_SET_WITH_ACK) { - sendVariableAck(); - // The library user should not need to care about this ack request. Just treat it as a normal SET. - msg.header.messageType = M_SET_VARIABLE; - } - // Return message to waiting sketch... - return true; - } - } - return false; -} - - -message_s Sensor::waitForMessage() { - while (1) { - if (messageAvailable()) { - return msg; - } - } -} - -message_s Sensor::getMessage() { - return msg; -} - - -/*boolean Sensor::readMessage() { - uint8_t len = RF24::getDynamicPayloadSize(); - RF24::read(&msg, len); - - uint8_t valid = validate(len-sizeof(header_s)); - boolean ok = valid == VALIDATE_OK; - - // Make sure string gets terminated ok for full sized messages. - msg.data[len - sizeof(header_s) ] = '\0'; - debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), - msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); - return ok; -}*/ - -/* - * calculate CRC8 on message_s data taking care of data structure and protocol version - */ -uint8_t Sensor::crc8Message(message_s var_msg, uint8_t len) { - uint8_t crc = 0x00; - uint8_t loop_count; - uint8_t bit_counter; - uint8_t data; - uint8_t feedback_bit; - uint8_t number_of_bytes_to_read = (uint8_t)sizeof(var_msg); - - // Must set crc to a constant value. - var_msg.header.crc = 0; - - // fill unused space by zeroes for string data only - if(len>=0 && len < sizeof(var_msg.data)-1) { - memset(&var_msg.data[len], 0, sizeof(var_msg.data) - 1 - len); - } - - - for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) - { - data = ((uint8_t*)&var_msg)[loop_count]; - - bit_counter = 8; - do { - feedback_bit = (crc ^ data) & 0x01; - - if ( feedback_bit == 0x01 ) { - crc = crc ^ 0x18; //0X18 = X^8+X^5+X^4+X^0 - } - crc = (crc >> 1) & 0x7F; - if ( feedback_bit == 0x01 ) { - crc = crc | 0x80; - } - - data = data >> 1; - bit_counter--; - - } while (bit_counter > 0); - } - return crc; -} - - -uint8_t Sensor::validate(uint8_t length) { - uint8_t oldCrc = msg.header.crc; - uint8_t newCrc = crc8Message(msg, length); - - if(!(msg.header.version == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; - - if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; - return VALIDATE_OK; -} - -#ifdef DEBUG -void Sensor::debugPrint(const char *fmt, ... ) { - char fmtBuffer[300]; - if (radioId == GATEWAY_ADDRESS) { - // prepend debug message to be handled correctly by gw - Serial.print("0;0;4;11;"); - } - va_list args; - va_start (args, fmt ); - vsnprintf_P(fmtBuffer, 300, fmt, args); - va_end (args); - if (radioId == GATEWAY_ADDRESS) { - // Truncate message if this is gateway node - vsnprintf_P(fmtBuffer, 60, fmt, args); - fmtBuffer[59] = '\n'; - fmtBuffer[60] = '\0'; - } else { - vsnprintf_P(fmtBuffer, 299, fmt, args); - } - va_end (args); - Serial.print(fmtBuffer); - - - //Serial.print("0;0;4;11;Mem free:"); - //Serial.println(freeRam()); -} -#endif - - -#ifdef DEBUG -int Sensor::freeRam (void) { - extern int __heap_start, *__brkval; - int v; - return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); -} -#endif diff --git a/libraries/MySensors/Sensor.h b/libraries/MySensors/Sensor.h deleted file mode 100644 index d661bc5..0000000 --- a/libraries/MySensors/Sensor.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 Sensor_h -#define Sensor_h - -#include "Config.h" -#include "Version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef DEBUG -#define debug(x,...) debugPrint(x, ##__VA_ARGS__) -#else -#define debug(x,...) -#endif - -#define PROTOCOL_VERSION 1 -#define BAUD_RATE 115200 - -#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of radioId. -#define NODE_CHILD_ID 0xFF // Node child id is always created for when a new sensor is detected -#define EEPROM_RADIO_ID_ADDRESS 0 // Where to store radio id in EEPROM -#define EEPROM_RELAY_ID_ADDRESS 1 // Where to store relay id in EEPROM -#define EEPROM_DISTANCE_ADDRESS 2 // Where to store distance to gateway in EEPROM - -// This is the radioId for sensor net gateway receiver sketch (where all sensors should send their data). -// This is also act as base value for sensor radioId -#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) -#define GATEWAY_ADDRESS ((uint8_t)0) -#define BROADCAST_ADDRESS ((uint8_t)0xFF) -#define TO_ADDR(x) (BASE_RADIO_ID + x) - -#define WRITE_PIPE ((uint8_t)0) -#define CURRENT_NODE_PIPE ((uint8_t)1) -#define BROADCAST_PIPE ((uint8_t)2) - -#define FIND_RELAY_RETRIES 2 -#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 20 - - -#define MAX_MESSAGE_LENGTH 32 - -// Message types -typedef enum { - M_PRESENTATION = 0, - M_SET_VARIABLE = 1, - M_REQ_VARIABLE = 2, - M_SET_WITH_ACK = 3, - M_INTERNAL = 4 -} messageType; - -// Sensor variables that can be used in sketches -typedef enum { - V_TEMP,V_HUM, V_LIGHT, V_DIMMER, 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_HEATER, V_HEATER_SW, 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 -} variableType; - -// Internal messages -typedef enum { - I_BATTERY_LEVEL, I_BATTERY_DATE, I_LAST_TRIP, I_TIME, I_VERSION, I_REQUEST_ID, - I_INCLUSION_MODE, I_RELAY_NODE, I_LAST_UPDATE, I_PING, I_PING_ACK, - I_LOG_MESSAGE, I_CHILDREN, I_UNIT, I_SKETCH_NAME, I_SKETCH_VERSION -} internalMessageType; - -// Sensor types -typedef enum { - S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, 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_RELAY, S_LOCK, S_IR, S_WATER -} sensor; - -// Possible return values by validate() when doing crc check of received message. -enum { - VALIDATE_OK, VALIDATE_BAD_CRC, VALIDATE_BAD_VERSION -}; - -// The message structure -typedef struct { - uint8_t crc : 8; // 8 bits crc - uint8_t version : 3; // 3 bits protocol version - uint8_t binary : 1; // 1 bit. Data is binary and should be encoded when sent to sensor net gateway - uint8_t from : 8; // 8 bits. RadioId of sender node - uint8_t to : 8; // 8 bits. RadioId of destination node - uint8_t last : 8; // 8 bits. RadioId of last node this message passed - uint8_t childId : 8; // 1 byte. Up to MAX_CHILD_DEVICES child sensors per radioId - uint8_t messageType : 4; // 4 bits. Type of message. See messageType - - uint8_t type : 8; // 8 bits. variableType or deviceType depending on messageType -} header_s; - -typedef struct { - header_s header; - char data[MAX_MESSAGE_LENGTH - sizeof(header_s) + 1]; // Each message can transfer a payload. Add one extra byte for \0 -} message_s; - - -class Sensor : public RF24 -{ - public: - /** - * Constructor - * - * Creates a new instance of Sensor class. - * - * @param _cepin The pin attached to RF24 Chip Enable on the RF module (defualt 9) - * @param _cspin The pin attached to RF24 Chip Select (default 10) - */ - Sensor(uint8_t _cepin=9, uint8_t _cspin=10); - - /** - * Begin operation of the MySensors library - * - * Call this in setup(), before calling any other sensor net library methods. - * @param radioId The unique id (1-254) for this sensor. Default 255 (auto mode). - * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX - * @param channel Radio channel. Default is channel 76 - * @param dataRate Radio transmission speed. Default RF24_2MBPS - */ - void begin(uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); - - uint8_t getRadioId(); - - /** - * The arduino node must send a presentation of all the sensors connected before any - * variable changes will be registered on sensor net gateway side. - * Usually it's good to present all sensors when arduino starts up (setup). - * Note that gateway must be in include mode to register new devices. - * - * @param childId The unique child id for the different sensors connected to this arduino. 0-254. - * @param sensorType Sensor types to create. They will be numbered from 0 up to 127. - */ - void sendSensorPresentation(uint8_t childId, uint8_t sensorType); - - - /** - * Sends a variable change to sensor net gateway (or another sensor if radioId is specified) - * - * @param radioId The radioId of other node in radio network (used for communicating between sensors) - * @param childId The child id for which to update variable. Value can be 0-254. - * @param variableType The variableType to update - * @param value New value of the variable - */ - - void sendVariable(uint8_t childId, uint8_t variableType, const char *value); - void sendVariable(uint8_t childId, uint8_t variableType, double value, int decimals); - void sendVariable(uint8_t childId, uint8_t variableType, unsigned long value); - void sendVariable(uint8_t childId, uint8_t variableType, long value); - void sendVariable(uint8_t childId, uint8_t variableType, unsigned int value); - void sendVariable(uint8_t childId, uint8_t variableType, int value); - - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, const char *value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, double value, int decimals); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, unsigned long value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, long value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, unsigned int value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, int value); - - /** - * Sends sketch info to sensor net gateway for this radio node. - * @param name String containing a short Sketch name - * @param version String containing a short Sketch version - */ - void sendSketchInfo(const char *name, const char *version); - - /** - * Sends sketch name to sensor net gateway for this radio node. - * @param info String containing a short Sketch name and version - */ - void sendSketchName(const char *name); - - /** - * Sends sketch version to sensor net gateway for this radio node. - * @param info String containing a short Sketch name and version - */ - void sendSketchVersion(const char *version); - - /** - * Sends battery status to sensor net gateway for this radio node. - * @param value Set a value between 0-100(%) - */ - void sendBatteryLevel(int value); - - /** - * Requests a variable value from sensor net gateway (or another sensor). This method will not wait for an answer. - * You should use mesageAvailable/getMessage to pick up the response. - * - * @param radioId The radioId of other node in radio network (used when fetching from other node) - * @param childId The unique child id for the different sensors connected to this arduino. 0-254. - * @param variableType The variableType to fetch - */ - void requestStatus(uint8_t childId, uint8_t variableType); - void requestStatus(uint8_t radioId, int8_t childId, uint8_t variableType); - - /** - * Requests status for a sensor variable from sensor net gateway. Waits until message arrives. - * - * @param radioId The radioId of other node in radio network (used when fetching from other node) - * @param childId The unique child id for the different sensors connected to this arduino. 0-254. - * @param variableType The variableType to fetch - * @return The variable value - */ - char *getStatus(uint8_t childId, uint8_t variableType); - char *getStatus(uint8_t radioId, int8_t childId, uint8_t variableType); - - - /** - * Fetches time from sensor net gateway - */ - void requestTime(); - unsigned long getTime(); - - /** - * Fetches unit setting from sensor net gateway. Returns true if metric system has been selected which means - * that sensor should report it's information in: celsius, meter, cm, gram, km/h, m/s etc.. - * If false is returned the sensor should report data in imperial system which means - * fahrenheit, feet, gallon, mph etc... - */ - void requestIsMetricSystem(); - bool isMetricSystem(); - - - /** - * Busy waits until there is a message for this node available to be read - */ - message_s waitForMessage(void); - - /** - * Returns true if there is a message addressed for this node is available to be read - */ - boolean messageAvailable(void); - - /** - * Returns the last received message - */ - message_s getMessage(void); - - /** - * Validates consistency of the message including CRC and protocol version - */ - uint8_t validate(uint8_t length); - - -#ifdef DEBUG - void debugPrint(const char *fmt, ... ); - int freeRam(); -#endif - - - protected: - uint8_t failedTransmissions; - boolean isRelay; - uint8_t radioId; - uint8_t distance; // This nodes distance to sensor net gateway (number of hops) - uint8_t relayId; - message_s msg; // Buffer for incoming messages. - char convBuffer[20]; - - void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); - void findRelay(); - boolean send(message_s message, int length); - boolean sendWrite(uint8_t dest, message_s message, int length); - //boolean readMessage(); - void buildMsg(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary); - void sendInternal(uint8_t variableType, const char *value); - boolean sendVariableAck(); - boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binaryMessage); - - - - private: - message_s ack; // Buffer for ack messages. - - void initializeRadioId(); - uint8_t crc8Message(message_s, uint8_t length); - char* get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType); - char *getInternal(uint8_t variableType); -}; - -#endif diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index 47c73b7..e8a44c0 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -1,27 +1,31 @@ // Example sketch showing how to control physical relays. -#include +#include +#include +#include #include #include #include + #define RELAY_1 3 // Arduino Digital I/O pin number for first relay (second on pin+1 etc) #define NUMBER_OF_RELAYS 1 // Total number of attached relays #define RELAY_ON 1 // GPIO value to write to turn on attached relay #define RELAY_OFF 0 // GPIO value to write to turn off attached relay -Relay gw; +MySensor gw; void setup() -{ - gw.begin(); +{ + // Initialize library and add callback for incoming messages + gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("Relay", "1.0"); + gw.sketchInfo("Relay", "1.0"); // Register all sensors to gw (they will be created as child devices) for (int i=0; i Date: Mon, 12 May 2014 23:56:57 +0200 Subject: [PATCH 241/326] And myGateway... --- libraries/MySensors/MyGateway.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/MySensors/MyGateway.h b/libraries/MySensors/MyGateway.h index f23beb8..d3c6188 100644 --- a/libraries/MySensors/MyGateway.h +++ b/libraries/MySensors/MyGateway.h @@ -9,21 +9,21 @@ version 2 as published by the Free Software Foundation. */ -#ifndef Gateway_h -#define Gateway_h +#ifndef MyGateway_h +#define MyGateway_h -#include "Relay.h" +#include "MySensor.h" #define MAX_RECEIVE_LENGTH 100 // Max buffersize needed for messages coming from vera #define MAX_SEND_LENGTH 120 // Max buffersize needed for messages coming from vera -class Gateway : public Relay +class MyGateway : public MySensor { public: /** * Constructor * - * Creates a new instance of Gateway class. + * Creates a new instance of MyGateway class. * If you don't use status leds and/or inclusion mode button on your arduino gateway * you can disable this functionality by calling the 2 argument constructor. * @@ -36,8 +36,8 @@ class Gateway : public Relay * @param _er Digital pin for error led * */ - Gateway(uint8_t _cepin=9, uint8_t _cspin=10, uint8_t _inclusion_time = 1); - Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er); + MyGateway(uint8_t _cepin=9, uint8_t _cspin=10, uint8_t _inclusion_time = 1); + MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er); /* Use this and pass a function that should be called when you want to process commands that arrive from radio network */ void begin(rf24_pa_dbm_e paLevel=RF24_PA_LEVEL_GW, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE, void (*dataCallback)(char *)=NULL); @@ -68,8 +68,8 @@ class Gateway : public Relay uint8_t pinEr; void serial(const char *fmt, ... ); - uint8_t validate(uint8_t length); - void serial(message_s msg); + void serial(MyMessage msg); + uint8_t validate(MyMessage message); void interruptStartInclusion(); void checkButtonTriggeredInclusion(); void setInclusionMode(boolean newMode); From 26ef9c81c2264b377e0a237b0c4366cceb8dad52 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 14 May 2014 00:26:28 +0200 Subject: [PATCH 242/326] refactoring continues. --- SerialGateway/SerialGateway.ino | 4 +- libraries/MySensors/MyConfig.h | 2 +- libraries/MySensors/MyGateway.cpp | 4 +- libraries/MySensors/MyMessage.cpp | 31 ++- libraries/MySensors/MySensor.cpp | 368 ++++++++++++++---------------- libraries/MySensors/MySensor.h | 55 +++-- 6 files changed, 230 insertions(+), 234 deletions(-) diff --git a/SerialGateway/SerialGateway.ino b/SerialGateway/SerialGateway.ino index 2ce8685..c245b92 100644 --- a/SerialGateway/SerialGateway.ino +++ b/SerialGateway/SerialGateway.ino @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled @@ -37,7 +37,7 @@ //Gateway gw; // To start gateway with include button and led blinking functionality enabled use this instead! -Gateway gw(9, 10, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4); +MyGateway gw(9, 10, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4); char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 5d7956f..474b705 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -12,6 +12,6 @@ /*** * Enable/Disable debug logging */ -//#define DEBUG +#define DEBUG #endif diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index 6d8fa89..f6e56d0 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -37,8 +37,8 @@ void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e da useWriteCallback = false; } - radioId = 0; - distance = 0; + s.nodeId = 0; + s.distance = 0; inclusionMode = 0; buttonTriggeredInclusion = false; countRx = 0; diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 3b31083..759d72e 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -116,7 +116,11 @@ char* MyMessage::getString(char *buffer) { } uint8_t MyMessage::getByte() { - return data[0]; + if (getPayloadType() == P_BYTE) + return data[0]; + if (getPayloadType() == P_STRING) + return atoi(data); + return 0; } bool MyMessage::getBool() { return !(data[0]-'0'==0); @@ -125,17 +129,34 @@ bool MyMessage::getBool() { double MyMessage::getDouble() { return strtod(data, NULL); } + long MyMessage::getLong() { - return lValue; + if (getPayloadType() == P_LONG32) + return lValue; + if (getPayloadType() == P_STRING) + return atol(data); + return 0; } unsigned long MyMessage::getULong() { - return ulValue; + if (getPayloadType() == P_ULONG32) + return ulValue; + if (getPayloadType() == P_STRING) + return atol(data); + return 0; } int MyMessage::getInt() { - return iValue; + if (getPayloadType() == P_INT16) + return iValue; + if (getPayloadType() == P_STRING) + return atoi(data); + return 0; } unsigned int MyMessage::getUInt() { - return uiValue; + if (getPayloadType() == P_UINT16) + return uiValue; + if (getPayloadType() == P_STRING) + return atoi(data); + return 0; } // Header setters diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index e4a3638..bc58710 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -20,13 +20,13 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ // Start up the radio library RF24::begin(); - RF24::setAutoAck(1); - RF24::enableAckPayload(); + if (!RF24::isPVariant()) { - debug(PSTR("Radio wiring NOT ok or not using P version of NRF24L01.\n")); + debug(PSTR("err=check wire\n")); while(1); } - + RF24::setAutoAck(1); + RF24::enableAckPayload(); RF24::setChannel(channel); RF24::setPALevel(paLevel); RF24::setDataRate(dataRate); @@ -34,8 +34,6 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ RF24::setCRCLength(RF24_CRC_16); RF24::enableDynamicPayloads(); - - // All repeater nodes and gateway listen to broadcast pipe (for PING messages) if (relayMode) { RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); @@ -43,138 +41,85 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ } void MySensor::setupRelayMode(){ - childNodeTable = (uint8_t *) malloc(256); - // Read routing table from EEPROM - for (unsigned int i=0;i0 && route<255 && dest != GATEWAY_ADDRESS) { - debug(PSTR("Routing message to %d.\n"), route); + debug(PSTR("ro=%d.\n"), route); // Message destination is not gateway and is in routing table for this node. // Send it downstream return sendWrite(route, message); } } - if (radioId != GATEWAY_ADDRESS) { - debug(PSTR("Relaying message back to gateway.\n")); + if (s.nodeId != GATEWAY_ADDRESS) { + debug(PSTR("ro=parent\n")); // Should be routed back to gateway. - bool ok = sendWrite(relayId, message); + bool ok = sendWrite(s.parentNodeId, message); if (!ok) { - // Failure when sending to relay node. The relay node might be down and we - // need to find another route to gateway. Max 20 retries before giving up. + // Failure when sending to parent node. The parent node might be down and we + // need to find another route to gateway. Max 20 retries before giving up. if (failedTransmissions > FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { - findRelay(); + findParentNode(); } failedTransmissions++; } else { @@ -229,11 +183,11 @@ boolean MySensor::sendRoute(MyMessage message) { boolean MySensor::sendWrite(uint8_t next, MyMessage message) { uint8_t length = message.getLength(); - message.setLast(radioId); + message.setLast(s.nodeId); message.setCRC(crc8Message(message)); // Only print payload if string type - debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d:%s\n"), - message.getSender(),message.getDestination(), message.getLast(), next, message.getSensor(), message.getCommand(), message.getType(), message.getCRC(), message.getString()); + debug(PSTR("tx: %d-%d-%d-%d s=%d,c=%d,t=%d:%s\n"), + message.getSender(),message.getLast(), next, message.getDestination(), message.getSensor(), message.getCommand(), message.getType(), message.getString()); bool broadcast = message.getCommand() == C_INTERNAL && message.getType() == I_PING; @@ -245,21 +199,21 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage message) { RF24::startListening(); if (ok) - debug(PSTR("Sent successfully\n")); + debug(PSTR("s=ok\n")); else - debug(PSTR("Send failed\n")); + debug(PSTR("s=fail\n")); return ok; } void MySensor::sendInternal(uint8_t variableType, const char *value) { - sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType).set(value)); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType).set(value)); } bool MySensor::send(MyMessage message, bool enableAck, uint8_t retry) { - message.setSender(radioId); + message.setSender(s.nodeId); if (enableAck) { message.setCommand(C_SET_WITH_ACK); return wait(message, C_SET_VARIABLE, retry).getCommand() != C_FAILED; @@ -270,7 +224,7 @@ bool MySensor::send(MyMessage message, bool enableAck, uint8_t retry) { void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { - sendRoute(msg.build(radioId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType).set(LIBRARY_VERSION)); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType).set(LIBRARY_VERSION)); } void MySensor::sketchInfo(const char *name, const char *version) { @@ -281,13 +235,13 @@ void MySensor::sketchInfo(const char *name, const char *version) { } MyMessage MySensor::get( uint8_t childSensorId, uint8_t variableType, uint8_t destination, uint8_t retry) { - msg.build(radioId, destination, childSensorId, C_REQ_VARIABLE, variableType); + msg.build(s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType); return wait(msg, C_SET_VARIABLE, retry); } void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { process(); // Make sure to process any queued messages before sending new request - sendRoute(msg.build(radioId, destination, childSensorId, C_REQ_VARIABLE, variableType)); + sendRoute(msg.build(s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType)); } @@ -325,7 +279,7 @@ MyMessage MySensor::wait(MyMessage message, uint8_t expectedResponseCommand, uin MyMessage MySensor::getInternal(uint8_t variableType, uint8_t retry) { - msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType); + msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType); return wait(msg, C_INTERNAL, retry); } @@ -335,7 +289,7 @@ MyMessage MySensor::getInternal(uint8_t variableType, uint8_t retry) { }*/ void MySensor::requestTime() { - sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME)); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME)); } @@ -348,7 +302,7 @@ void MySensor::requestTime() { }*/ void MySensor::requestIsMetricSystem() { - sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_UNIT)); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_UNIT)); } @@ -356,9 +310,6 @@ boolean MySensor::process() { uint8_t pipe; boolean available = RF24::available(&pipe); - if (available) { - debug(PSTR("Message available on pipe %d\n"),pipe); - } if (available && pipe<7) { uint8_t len = RF24::getDynamicPayloadSize(); @@ -370,8 +321,8 @@ boolean MySensor::process() { // Make sure string gets terminated ok for full string messages. msg.data[len - HEADER_SIZE ] = '\0'; - debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), - msg.getSender(),msg.getDestination(), msg.getLast(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getCRC(), valid==0?"ok":valid==1?"ec":"ev", msg.getString()); + debug(PSTR("rx: %d-%d-%d s=%d,c=%d,t=%d,cr=%s: %s\n"), + msg.getSender(), msg.getLast(), msg.getDestination(), msg.getSensor(), msg.getCommand(), msg.getType(), valid==0?"ok":valid==1?"ec":"ev", msg.getString()); if (ok) { uint8_t command = msg.getCommand(); @@ -380,68 +331,85 @@ boolean MySensor::process() { uint8_t last = msg.getLast(); uint8_t destination = msg.getDestination(); - if (relayMode && command == C_INTERNAL && type == I_PING) { - // Relays should always answer ping messages + if (relayMode && command == C_INTERNAL && type == I_PING && s.nodeId != AUTO) { + // Relaying nodes should always answer ping messages // Wait a random delay of 0-2 seconds to minimize collision // between ping ack messages from other relaying nodes randomSeed(millis()); delay(random(2000)); uint8_t replyTo = msg.getSender(); - msg.build(radioId, replyTo, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK).set(distance); + msg.build(s.nodeId, replyTo, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK).set(s.distance); sendWrite(replyTo, msg); - } else if (sender == radioId) { + return false; + } else if (destination == s.nodeId) { // This message is addressed to this node - if (relayMode && command == C_INTERNAL) { - // Handle all internal messages to this node - if (type == I_RELAY_NODE && destination != GATEWAY_ADDRESS) { - // Someone at sensor net gateway side wants this node to refresh its relay node - findRelay(); - // Send relay information back to sensor net gateway node device. - sendInternal(I_RELAY_NODE, ltoa(relayId, convBuffer, 10)); - return false; - } else if (type == I_CHILDREN && destination != GATEWAY_ADDRESS) { - debug(PSTR("Route command received")); - if (msg.getByte() == 'F') { - // Send in as many children we can fit in a message (binary) - sendChildren(); - } else if (msg.getByte() == 'C') { - // Clears child relay data for this node - clearChildRoutes(); + + if (relayMode && last != s.parentNodeId) { + // Message is from one of the child nodes. Add it to routing table. + addChildRoute(sender, last); + } + + if (command == C_INTERNAL) { + if (type == I_PING_ACK) { + uint8_t distance = msg.getByte(); + if (distance0 && route<255) { - debug(PSTR("Routing message to child node.\n")); // This message should be forwarded to a child node. If we send message // to this nodes pipe then all children will receive it because the are // all listening to this nodes pipe. @@ -466,9 +434,8 @@ boolean MySensor::process() { // // Message should be passed to node A (this nodes relay) - debug(PSTR("Routing message to relay.\n")); // This message should be routed back towards sensor net gateway - sendWrite(relayId, msg); + sendWrite(s.parentNodeId, msg); // Add this child to our "routing table" if it not already exist addChildRoute(sender, last); } else { @@ -487,14 +454,17 @@ boolean MySensor::process() { // This can happen if a node never has sent any message to // sensor net gateway (which will build up routing information in all nodes // that gets passed). - if (radioId == GATEWAY_ADDRESS) { + + // THIS SHOULD NOT HAPPEN ANY MORE + + /*if (s.nodeId == GATEWAY_ADDRESS) { // We should perhaps inform gateway about the no-route situation?! - debug(PSTR("Unknown route from GW\n")); + debug(PSTR("ro=?\n")); } else { - debug(PSTR("Remove child node from routing table.\n")); + //debug(PSTR("Remove child node from routing table.\n")); removeChildRoute(sender); - } + }*/ } } @@ -570,42 +540,42 @@ uint8_t MySensor::validate(MyMessage message) { void MySensor::addChildRoute(uint8_t childId, uint8_t route) { - if (childNodeTable[childId] != route) { - childNodeTable[childId] = route; - EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, route); + if (s.childNodeTable[childId] != route) { + s.childNodeTable[childId] = route; + eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, route); } } void MySensor::removeChildRoute(uint8_t childId) { - if (childNodeTable[childId] != 0xff) { - childNodeTable[childId] = 0xff; - EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, 0xff); + if (s.childNodeTable[childId] != 0xff) { + s.childNodeTable[childId] = 0xff; + eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, 0xff); } } uint8_t MySensor::getChildRoute(uint8_t childId) { - return childNodeTable[childId]; + return s.childNodeTable[childId]; } void MySensor::clearChildRoutes() { - debug(PSTR("Clear child routing data\n")); - for (unsigned int i=0;i< sizeof(childNodeTable); i++) { + debug(PSTR("rd=clear\n")); + for (unsigned int i=0;i< sizeof(s.childNodeTable); i++) { removeChildRoute(i); } sendInternal(I_CHILDREN, ""); } void MySensor::sendChildren() { - // Send in which children that is using this node as an relay. + // Send in which children that is using this node as parent. // TODO: Fix this - debug(PSTR("Dump beginning of routing table\n")); + debug(PSTR("rd=dump\n")); for (int i=0;i< 50; i++) { -// Serial.println(childNodeTable[i]); - debug(PSTR("rt:%d, %d\n"), i, getChildRoute(i) ); +// Serial.println(s.childNodeTable[i]); + debug(PSTR("rd=%d, %d\n"), i, getChildRoute(i) ); } - sendInternal(I_CHILDREN, "not implemented"); + sendInternal(I_CHILDREN, "nit impl"); } @@ -615,7 +585,7 @@ void MySensor::sendChildren() { #ifdef DEBUG void MySensor::debugPrint(const char *fmt, ... ) { char fmtBuffer[300]; - if (radioId == GATEWAY_ADDRESS) { + if (s.nodeId == GATEWAY_ADDRESS) { // prepend debug message to be handled correctly by gw Serial.print("0;0;4;11;"); } @@ -623,7 +593,7 @@ void MySensor::debugPrint(const char *fmt, ... ) { va_start (args, fmt ); vsnprintf_P(fmtBuffer, 300, fmt, args); va_end (args); - if (radioId == GATEWAY_ADDRESS) { + if (s.nodeId == GATEWAY_ADDRESS) { // Truncate message if this is gateway node vsnprintf_P(fmtBuffer, 60, fmt, args); fmtBuffer[59] = '\n'; @@ -633,7 +603,7 @@ void MySensor::debugPrint(const char *fmt, ... ) { } va_end (args); Serial.print(fmtBuffer); - + Serial.flush(); //Serial.print("0;0;4;11;Mem free:"); //Serial.println(freeRam()); diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 1cc3537..f4090a1 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -33,16 +33,18 @@ #define BAUD_RATE 115200 -#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of radioId. +#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId. #define NODE_CHILD_ID 0xFF // Node child id is always created for when a new sensor is detected -#define EEPROM_RADIO_ID_ADDRESS 0 // Where to store radio id in EEPROM -#define EEPROM_RELAY_ID_ADDRESS 1 // Where to store relay id in EEPROM -#define EEPROM_DISTANCE_ADDRESS 2 // Where to store distance to gateway in EEPROM -#define EEPROM_ROUTES_ADDRESS ((uint8_t)3) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. + +#define EEPROM_START 0 +#define EEPROM_NODE_ID_ADDRESS EEPROM_START // Where to store radio id in EEPROM +#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1) // Where to store relay id in EEPROM +#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) // Where to store distance to gateway in EEPROM +#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. -// This is the radioId for sensor net gateway receiver sketch (where all sensors should send their data). -// This is also act as base value for sensor radioId +// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). +// This is also act as base value for sensor nodeId #define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) #define GATEWAY_ADDRESS ((uint8_t)0) #define BROADCAST_ADDRESS ((uint8_t)0xFF) @@ -53,8 +55,7 @@ #define BROADCAST_PIPE ((uint8_t)2) #define SEND_RETRIES 3 -#define FIND_RELAY_RETRIES 2 -#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 20 +#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 5 class MySensor : public RF24 @@ -76,27 +77,27 @@ class MySensor : public RF24 * Call this in setup(), before calling any other sensor net library methods. * @param incomingMessageCallback Callback function for incoming messages. Default is NULL * @param relayMode Activate relay mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false - * @param radioId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. + * @param nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX * @param channel Radio channel. Default is channel 76 * @param dataRate Radio transmission speed. Default RF24_1MBPS */ - void begin(void (* msgCallback)(MyMessage)=NULL, boolean relayMode=false, uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); + void begin(void (* msgCallback)(MyMessage)=NULL, boolean relayMode=false, uint8_t nodeId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); /** - * Return the nodes radioId. + * Return the nodes nodeId. */ - uint8_t getRadioId(); + uint8_t getNodeId(); /** * 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 sensorChildId Sensor child id for the presented. Choose anything between 0-254. + * @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 Message.h. */ - void present(uint8_t sensorChildId, uint8_t sensorType); + void present(uint8_t sensorId, uint8_t sensorType); /** * Sends sketch meta information to the gateway. Not mandatory but a nice thing to do. @@ -128,7 +129,7 @@ class MySensor : public RF24 * * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. * @param variableType The variableType to fetch - * @param destination The radioId of other node in radio network. Default is gateway + * @param destination The nodeId of other node in radio network. Default is gateway * @param number of attempts to fetch * @return Message response */ @@ -141,7 +142,7 @@ class MySensor : public RF24 * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. * @param variableType The variableType to fetch * @param callback for result. This will be called when a message reply is received or request fails (see status of second argument). - * @param destination The radioId of other node in radio network. Default is gateway + * @param destination The nodeId of other node in radio network. Default is gateway */ /*void (* msgCallback)(MyMessage, bool)=NULL, */ void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); @@ -187,19 +188,22 @@ class MySensor : public RF24 int freeRam(); #endif - protected: MyMessage msg; // Buffer for incoming messages. uint8_t failedTransmissions; + struct settings_t + { + uint8_t nodeId; // Current node id + uint8_t parentNodeId; // Where this node sends its messages + uint8_t distance; // This nodes distance to sensor net gateway (number of hops) + uint8_t *childNodeTable; // In memory buffer for routing to other nodes. also stored in EEPROM + } s; boolean relayMode; - uint8_t radioId; - uint8_t distance; // This nodes distance to sensor net gateway (number of hops) - uint8_t relayId; char convBuffer[20]; void setupRelayMode(); void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); - void findRelay(); + void findParentNode(); boolean sendRoute(MyMessage message); boolean sendWrite(uint8_t dest, MyMessage message); void sendInternal(uint8_t variableType, const char *value); @@ -219,11 +223,12 @@ class MySensor : public RF24 uint8_t type; void (*msgCallback)(MyMessage, bool); };*/ + + MyMessage ack; // Buffer for ack messages. - uint8_t *childNodeTable; // In memory buffer for routing to other nodes. Also stored in EEPROM void (*msgCallback)(MyMessage); // Callback for undefined incoming messages - void initializeRadioId(); + void requestNodeId(); uint8_t crc8Message(MyMessage); MyMessage wait(MyMessage msg, uint8_t expectedReceiveType, uint8_t retry); MyMessage getInternal(uint8_t variableType, uint8_t retry=SEND_RETRIES); From 4b18f9fbf4953914a30587119605c588d82e38b1 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 14 May 2014 00:26:52 +0200 Subject: [PATCH 243/326] Relay updated to new api --- .../MySensors/examples/RelayActuator/RelayActuator.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index e8a44c0..b43e6fc 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -18,8 +18,7 @@ MySensor gw; void setup() { // Initialize library and add callback for incoming messages - gw.begin(incomingMessage); - + gw.begin(incomingMessage, false); // Send the sketch version information to the gateway and Controller gw.sketchInfo("Relay", "1.0"); @@ -49,8 +48,10 @@ void loop() void incomingMessage(MyMessage message) { // We only expect one type of message from controller. But we better check anyway. + Serial.println("hej"); if (message.getType()==V_LIGHT) { bool status = message.getBool(); + // Change relay state digitalWrite(message.getSensor(), status?RELAY_ON:RELAY_OFF); @@ -61,3 +62,4 @@ void incomingMessage(MyMessage message) { Serial.println(status); } } + From 710d4458157de9594a0aad2b1bbdaa3746a60943 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 14 May 2014 10:15:01 +0200 Subject: [PATCH 244/326] Stripping all sync-functions --- libraries/MySensors/MyGateway.cpp | 2 +- libraries/MySensors/MyGateway.h | 1 + libraries/MySensors/MySensor.cpp | 81 ++++++++++++++----------------- libraries/MySensors/MySensor.h | 18 +++---- 4 files changed, 48 insertions(+), 54 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index f6e56d0..fbf148a 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -213,7 +213,7 @@ void MyGateway::serial(const char *fmt, ... ) { } void MyGateway::serial(MyMessage msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg.getSender(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getString()); + serial(PSTR("%d;%d;%d;%d;%s\n"),msg.getSender(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getString(convBuf)); } diff --git a/libraries/MySensors/MyGateway.h b/libraries/MySensors/MyGateway.h index d3c6188..6dd19c4 100644 --- a/libraries/MySensors/MyGateway.h +++ b/libraries/MySensors/MyGateway.h @@ -49,6 +49,7 @@ class MyGateway : public MySensor void startInclusionInterrupt(); private: + char convBuf[MAX_PAYLOAD*2+1]; char serialBuffer[MAX_SEND_LENGTH]; // Buffer for building string when sending data to vera unsigned long inclusionStartTime; boolean inclusionMode; // Keeps track on inclusion mode diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index bc58710..dcd7798 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -22,7 +22,7 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ RF24::begin(); if (!RF24::isPVariant()) { - debug(PSTR("err=check wire\n")); + debug(PSTR("check wires\n")); while(1); } RF24::setAutoAck(1); @@ -81,7 +81,7 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_ present(NODE_CHILD_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE); // Send parent information back to sensor net gateway. - sendInternal(I_RELAY_NODE, itoa(s.parentNodeId, convBuffer, 10)); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE).set(s.parentNodeId)); } @@ -91,7 +91,6 @@ uint8_t MySensor::getNodeId() { void MySensor::requestNodeId() { debug(PSTR("req node id\n")); - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_REQUEST_ID)); } @@ -125,7 +124,7 @@ void MySensor::findParentNode() { - +/* boolean MySensor::sendAck(MyMessage message) { // Build ack message ack = message; @@ -133,7 +132,7 @@ boolean MySensor::sendAck(MyMessage message) { ack.setSender(s.nodeId); ack.setDestination(message.getSender()); return sendRoute(ack); -} +}*/ boolean MySensor::sendRoute(MyMessage message) { @@ -150,7 +149,7 @@ boolean MySensor::sendRoute(MyMessage message) { uint8_t dest = message.getDestination(); uint8_t route = getChildRoute(dest); if (route>0 && route<255 && dest != GATEWAY_ADDRESS) { - debug(PSTR("ro=%d.\n"), route); + debug(PSTR("route %d.\n"), route); // Message destination is not gateway and is in routing table for this node. // Send it downstream return sendWrite(route, message); @@ -158,7 +157,7 @@ boolean MySensor::sendRoute(MyMessage message) { } if (s.nodeId != GATEWAY_ADDRESS) { - debug(PSTR("ro=parent\n")); + debug(PSTR("route parent\n")); // Should be routed back to gateway. bool ok = sendWrite(s.parentNodeId, message); @@ -185,9 +184,6 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage message) { uint8_t length = message.getLength(); message.setLast(s.nodeId); message.setCRC(crc8Message(message)); - // Only print payload if string type - debug(PSTR("tx: %d-%d-%d-%d s=%d,c=%d,t=%d:%s\n"), - message.getSender(),message.getLast(), next, message.getDestination(), message.getSensor(), message.getCommand(), message.getType(), message.getString()); bool broadcast = message.getCommand() == C_INTERNAL && message.getType() == I_PING; @@ -198,28 +194,25 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage message) { bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); RF24::startListening(); - if (ok) - debug(PSTR("s=ok\n")); - else - debug(PSTR("s=fail\n")); - + // Only debug print payload if it is of string type + debug(PSTR("tx: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), + message.getSender(),message.getLast(), next, message.getDestination(), message.getSensor(), message.getCommand(), message.getType(), ok?"ok":"fail", message.getString()); return ok; } -void MySensor::sendInternal(uint8_t variableType, const char *value) { - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType).set(value)); -} - -bool MySensor::send(MyMessage message, bool enableAck, uint8_t retry) { +bool MySensor::send(MyMessage message, bool enableAck) { message.setSender(s.nodeId); if (enableAck) { message.setCommand(C_SET_WITH_ACK); - return wait(message, C_SET_VARIABLE, retry).getCommand() != C_FAILED; - } else { - return sendRoute(message);; } + return sendRoute(message); + +/* return wait(message, C_SET_VARIABLE, retry).getCommand() != C_FAILED; + } else { + return ; + }*/ } @@ -229,27 +222,28 @@ void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { void MySensor::sketchInfo(const char *name, const char *version) { if (name != NULL) - sendInternal(I_SKETCH_NAME, name); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_NAME).set(name)); if (version != NULL) - sendInternal(I_SKETCH_VERSION, version); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_VERSION).set(version)); } +/* MyMessage MySensor::get( uint8_t childSensorId, uint8_t variableType, uint8_t destination, uint8_t retry) { msg.build(s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType); return wait(msg, C_SET_VARIABLE, retry); -} +}*/ void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - process(); // Make sure to process any queued messages before sending new request sendRoute(msg.build(s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType)); } void MySensor::sendBatteryLevel(int value) { - sendInternal(I_BATTERY_LEVEL, ltoa(value, convBuffer, 10)); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_BATTERY_LEVEL).set(value)); } +/* MyMessage MySensor::wait(MyMessage message, uint8_t expectedResponseCommand, uint8_t retry) { while (retry>0) { @@ -274,14 +268,14 @@ MyMessage MySensor::wait(MyMessage message, uint8_t expectedResponseCommand, uin msg.setCommand(C_FAILED); return msg; } +*/ - - +/* MyMessage MySensor::getInternal(uint8_t variableType, uint8_t retry) { msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType); return wait(msg, C_INTERNAL, retry); -} +}*/ /*unsigned long MySensor::getTime(uint8_t retry) { MyMessage message = getInternal(I_TIME, retry); @@ -366,12 +360,13 @@ boolean MySensor::process() { // Write id to EEPROM if (s.nodeId == AUTO) { // sensor net gateway will return max id if all sensor id are taken - debug(PSTR("full or no reply\n")); + debug(PSTR("full\n")); while (1); // Wait here. Nothing else we can do... } else { + RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, s.nodeId); } - debug(PSTR("new node=%d\n"), s.nodeId); + debug(PSTR("id=%d\n"), s.nodeId); } return false; } else if (relayMode && sender == GATEWAY_ADDRESS) { @@ -379,7 +374,7 @@ boolean MySensor::process() { // Someone at sensor net gateway side wants this node to refresh its relay node findParentNode(); // Send relay information back to sensor net gateway node device. - sendInternal(I_RELAY_NODE, ltoa(s.parentNodeId, convBuffer, 10)); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE).set(s.parentNodeId)); return false; } else if (type == I_CHILDREN) { if (msg.getByte() == 'F') { @@ -393,9 +388,13 @@ boolean MySensor::process() { } } } - // If this is variable message from sensor net gateway. Send ack back. + // Check if sender requests an ack back. if (command == C_SET_WITH_ACK) { - sendAck(msg); + ack = msg; + ack.setCommand(C_SET_VARIABLE); + ack.setSender(s.nodeId); + ack.setDestination(msg.getSender()); + sendRoute(ack); // The library user should not need to care about this ack request. Just treat it as a normal SET. msg.setCommand(C_SET_VARIABLE); } @@ -474,9 +473,6 @@ boolean MySensor::process() { } - - - MyMessage MySensor::getLastMessage() { return msg; } @@ -562,20 +558,17 @@ void MySensor::clearChildRoutes() { for (unsigned int i=0;i< sizeof(s.childNodeTable); i++) { removeChildRoute(i); } - sendInternal(I_CHILDREN, ""); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN).set("")); } void MySensor::sendChildren() { // Send in which children that is using this node as parent. // TODO: Fix this - debug(PSTR("rd=dump\n")); - for (int i=0;i< 50; i++) { // Serial.println(s.childNodeTable[i]); debug(PSTR("rd=%d, %d\n"), i, getChildRoute(i) ); } - - sendInternal(I_CHILDREN, "nit impl"); + sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN).set("n/a")); } diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index f4090a1..a01b155 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -114,7 +114,7 @@ class MySensor : public RF24 * @param retry Number of attempts before giving up. Default is SEND_RETRIES * @return true Retuns false if ack is enabled and no ack came back */ - bool send(MyMessage msg, bool ack=false, uint8_t retry=SEND_RETRIES); + bool send(MyMessage msg, bool ack=false/*, uint8_t retry=SEND_RETRIES*/); /** * Send this nodes battery level to gateway. @@ -133,7 +133,7 @@ class MySensor : public RF24 * @param number of attempts to fetch * @return Message response */ - MyMessage get(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS, uint8_t retry=SEND_RETRIES); +// MyMessage get(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS, uint8_t retry=SEND_RETRIES); /** * Requests a value from gateway or some other sensor in the radio network. @@ -189,8 +189,6 @@ class MySensor : public RF24 #endif protected: - MyMessage msg; // Buffer for incoming messages. - uint8_t failedTransmissions; struct settings_t { uint8_t nodeId; // Current node id @@ -198,16 +196,18 @@ class MySensor : public RF24 uint8_t distance; // This nodes distance to sensor net gateway (number of hops) uint8_t *childNodeTable; // In memory buffer for routing to other nodes. also stored in EEPROM } s; + MyMessage msg; // Buffer for incoming messages. + uint8_t failedTransmissions; boolean relayMode; - char convBuffer[20]; +// char convBuffer[20]; void setupRelayMode(); void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); void findParentNode(); boolean sendRoute(MyMessage message); boolean sendWrite(uint8_t dest, MyMessage message); - void sendInternal(uint8_t variableType, const char *value); - boolean sendAck(MyMessage message); +// void sendInternal(uint8_t variableType, const char *value); +// boolean sendAck(MyMessage message); boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binaryMessage); uint8_t validate(MyMessage message); void sendChildren(); @@ -230,8 +230,8 @@ class MySensor : public RF24 void requestNodeId(); uint8_t crc8Message(MyMessage); - MyMessage wait(MyMessage msg, uint8_t expectedReceiveType, uint8_t retry); - MyMessage getInternal(uint8_t variableType, uint8_t retry=SEND_RETRIES); +// MyMessage wait(MyMessage msg, uint8_t expectedReceiveType, uint8_t retry); +// MyMessage getInternal(uint8_t variableType, uint8_t retry=SEND_RETRIES); uint8_t getChildRoute(uint8_t childId); void addChildRoute(uint8_t childId, uint8_t route); From a31d9d1074abcb69b03676a552e99aa185ab4a08 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 15 May 2014 07:40:07 +0200 Subject: [PATCH 245/326] Reduce library size --- libraries/MySensors/MyConfig.h | 2 +- libraries/MySensors/MyGateway.cpp | 4 +- libraries/MySensors/MyMessage.cpp | 203 +++++----------- libraries/MySensors/MyMessage.h | 70 +++--- libraries/MySensors/MySensor.cpp | 219 +++++------------- libraries/MySensors/MySensor.h | 57 ++--- .../examples/RelayActuator/RelayActuator.ino | 16 +- 7 files changed, 176 insertions(+), 395 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 474b705..5d7956f 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -12,6 +12,6 @@ /*** * Enable/Disable debug logging */ -#define DEBUG +//#define DEBUG #endif diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index fbf148a..2abed16 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -187,7 +187,7 @@ void MyGateway::processRadioMessage() { if (process()) { // A new message was received from one of the sensors MyMessage message = getLastMessage(); - if (message.getCommand() == C_PRESENTATION && inclusionMode) { + if (mGetCommand(message) == C_PRESENTATION && inclusionMode) { rxBlink(3); } else { rxBlink(1); @@ -213,7 +213,7 @@ void MyGateway::serial(const char *fmt, ... ) { } void MyGateway::serial(MyMessage msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg.getSender(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getString(convBuf)); + serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); } diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 759d72e..9175489 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -4,73 +4,8 @@ MyMessage::MyMessage() { - setVersion(PROTOCOL_VERSION); } -MyMessage::MyMessage(uint8_t _sensor, uint8_t _type, uint8_t _destination) { - sensor = _sensor; - type = _type; - destination = _destination; - setVersion(PROTOCOL_VERSION); - setCommand(C_SET_VARIABLE); -} - -MyMessage MyMessage::build(uint8_t _sender, uint8_t _destination, uint8_t _sensor, uint8_t _command, uint8_t _type) { - sender = _sender; - destination = _destination; - sensor = _sensor; - type = _type; - setCommand(_command); - setLength(0); - setPayloadType(P_STRING); - return *this; -} - -/*MyMessage MyMessage::failed() { - return getCommand() == C_FAILED -}*/ - -// Header getters - -uint8_t MyMessage::getCRC() { - return crc; -} - -uint8_t MyMessage::getVersion() { - return BF_GET(version_length, 0, 3); -} - -uint8_t MyMessage::getLength() { - return BF_GET(version_length, 3, 5); -} - -uint8_t MyMessage::getCommand() { - return BF_GET(command_payload, 0, 4); -} - -uint8_t MyMessage::getPayloadType() { - return BF_GET(command_payload, 4, 4); -} - -uint8_t MyMessage::getType() { - return type; -} - -uint8_t MyMessage::getSensor() { - return sensor; -} - -uint8_t MyMessage::getSender() { - return sender; -} - -uint8_t MyMessage::getLast() { - return last; -} - -uint8_t MyMessage::getDestination() { - return destination; -} /* Getters for payload converted to desired form */ @@ -80,10 +15,10 @@ void* MyMessage::getCustom() { /* NOTE: Calling this method might affect the payload by converting it to its string representation */ char* MyMessage::getString(char *buffer) { - uint8_t payloadType = getPayloadType(); + uint8_t payloadType = miGetPayloadType(); if (payloadType == P_STRING) { - data[getLength()] = '\0'; + data[miGetLength()] = '\0'; return data; } else if (buffer != NULL) { switch(payloadType) { @@ -116,11 +51,14 @@ char* MyMessage::getString(char *buffer) { } uint8_t MyMessage::getByte() { - if (getPayloadType() == P_BYTE) + switch(miGetPayloadType()) { + case P_BYTE: return data[0]; - if (getPayloadType() == P_STRING) + case P_STRING: return atoi(data); - return 0; + default: + return 0; + } } bool MyMessage::getBool() { return !(data[0]-'0'==0); @@ -131,90 +69,55 @@ double MyMessage::getDouble() { } long MyMessage::getLong() { - if (getPayloadType() == P_LONG32) + switch(miGetPayloadType()) { + case P_LONG32: return lValue; - if (getPayloadType() == P_STRING) + case P_STRING: return atol(data); - return 0; + default: + return 0; + } } + unsigned long MyMessage::getULong() { - if (getPayloadType() == P_ULONG32) + switch(miGetPayloadType()) { + case P_ULONG32: return ulValue; - if (getPayloadType() == P_STRING) + case P_STRING: return atol(data); - return 0; + default: + return 0; + } } + int MyMessage::getInt() { - if (getPayloadType() == P_INT16) + switch(miGetPayloadType()) { + case P_INT16: return iValue; - if (getPayloadType() == P_STRING) + case P_STRING: return atoi(data); - return 0; + default: + return 0; + } } + unsigned int MyMessage::getUInt() { - if (getPayloadType() == P_UINT16) + switch(miGetPayloadType()) { + case P_UINT16: return uiValue; - if (getPayloadType() == P_STRING) + case P_STRING: return atoi(data); - return 0; -} + default: + return 0; + } -// Header setters -MyMessage MyMessage::setCRC(uint8_t _crc) { - crc = _crc; - return *this; -} - -MyMessage MyMessage::setVersion(uint8_t version) { - BF_SET(version_length, version, 0, 3); - return *this; -} - -MyMessage MyMessage::setLength(uint8_t length) { - BF_SET(version_length, length, 3, 5); - return *this; -} - -MyMessage MyMessage::setCommand(uint8_t command) { - BF_SET(command_payload, command, 0, 4); - return *this; -} - -MyMessage MyMessage::setPayloadType(uint8_t pt) { - BF_SET(command_payload, pt, 4, 4); - return *this; -} - -MyMessage MyMessage::setType(uint8_t _type) { - type = _type; - return *this; -} - -MyMessage MyMessage::setSensor(uint8_t _sensor) { - sensor = _sensor; - return *this; -} - -MyMessage MyMessage::setSender(uint8_t _sender) { - sender = _sender; - return *this; -} - -MyMessage MyMessage::setLast(uint8_t _last) { - last=_last; - return *this; -} - -MyMessage MyMessage::setDestination(uint8_t _destination) { - destination = _destination; - return *this; } // Set payload MyMessage MyMessage::set(void* value, uint8_t length) { - setPayloadType(P_CUSTOM); - setLength(length); + miSetPayloadType(P_CUSTOM); + miSetLength(length); memcpy(data, value, min(length, MAX_PAYLOAD)); return *this; } @@ -222,57 +125,57 @@ MyMessage MyMessage::set(void* value, uint8_t length) { MyMessage MyMessage::set(const char* value) { uint8_t length = strlen(value); - setLength(length); - setPayloadType(P_STRING); + miSetLength(length); + miSetPayloadType(P_STRING); strncpy(data, value, min(length, MAX_PAYLOAD)); return *this; } MyMessage MyMessage::set(uint8_t value) { - setLength(1); - setPayloadType(P_BYTE); + miSetLength(1); + miSetPayloadType(P_BYTE); data[0] = value; return *this; } MyMessage MyMessage::set(bool value) { - setLength(1); - setPayloadType(P_STRING); + miSetLength(1); + miSetPayloadType(P_STRING); data[0] = value?'1':'0'; return *this; } MyMessage MyMessage::set(double value, uint8_t decimals) { dtostrf(value,2,decimals,data); - setLength(strlen(data)); - setPayloadType(P_STRING); + miSetLength(strlen(data)); + miSetPayloadType(P_STRING); return *this; } MyMessage MyMessage::set(unsigned long value) { - setPayloadType(P_ULONG32); - setLength(4); + miSetPayloadType(P_ULONG32); + miSetLength(4); ulValue = value; return *this; } MyMessage MyMessage::set(long value) { - setPayloadType(P_LONG32); - setLength(4); + miSetPayloadType(P_LONG32); + miSetLength(4); lValue = value; return *this; } MyMessage MyMessage::set(unsigned int value) { - setPayloadType(P_INT16); - setLength(2); + miSetPayloadType(P_INT16); + miSetLength(2); uiValue = value; return *this; } MyMessage MyMessage::set(int value) { - setPayloadType(P_UINT16); - setLength(2); + miSetPayloadType(P_UINT16); + miSetLength(2); iValue = value; return *this; } diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index bd80b66..6d4f053 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -18,7 +18,6 @@ #define MAX_MESSAGE_LENGTH 32 #define HEADER_SIZE 8 #define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) -#define GATEWAY_ADDRESS ((uint8_t)0) // Message types typedef enum { @@ -27,8 +26,7 @@ typedef enum { C_REQ_VARIABLE = 2, C_SET_WITH_ACK = 3, C_INTERNAL = 4, - C_STREAM = 5, // For Firmaware and other larger chunks of data that need to be divided into pieces. - C_FAILED = 6 + C_STREAM = 5 // For Firmaware and other larger chunks of data that need to be divided into pieces. } command; // Type of sensor data (for set/req/ack messages) @@ -44,7 +42,7 @@ typedef enum { typedef enum { I_BATTERY_LEVEL, I_TIME, I_VERSION, I_REQUEST_ID, I_INCLUSION_MODE, I_RELAY_NODE, I_PING, I_PING_ACK, - I_LOG_MESSAGE, I_CHILDREN, I_UNIT, I_SKETCH_NAME, I_SKETCH_VERSION + I_LOG_MESSAGE, I_CHILDREN, I_CONFIG, I_SKETCH_NAME, I_SKETCH_VERSION } internal; // Type of sensor (for presentation message) @@ -82,46 +80,44 @@ typedef enum { // Insert a new bitfield value x into y. #define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) +// Getters/setters for special fields in header +#define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 3) +#define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) +#define mSetCommand(_msg,_command) BF_SET(_msg.command_payload, _command, 0, 4) +#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_payload, _pt, 4, 4) + +#define mGetVersion(_msg) BF_GET(_msg.version_length, 0, 3) +#define mGetLength(_msg) BF_GET(_msg.version_length, 3, 5) +#define mGetCommand(_msg) BF_GET(_msg.command_payload, 0, 4) +#define mGetPayloadType(_msg) BF_GET(_msg.command_payload, 4, 4) + +// internal access for special fileds +#define miGetPayloadType() BF_GET(command_payload, 4, 4) +#define miGetLength() BF_GET(version_length, 3, 5) + +#define miSetLength(_length) BF_SET(version_length, _length, 3, 5) +#define miSetPayloadType(_pt) BF_SET(command_payload, _pt, 4, 4) + + +// Check and fetch data from special messages +#define isTimeResponse(_msg) (mGetCommand(_msg)==C_INTERN && _msg.type==I_TIME) +#define isConfigResponse(_msg) (mGetCommand(_msg)==C_INTERN && _msg.type==I_CONFIG) +#define mIsMetric(_msg) (_msg.getByte() == 'M') class MyMessage { public: // Constructors - MyMessage(uint8_t sensor, uint8_t type, uint8_t destination=GATEWAY_ADDRESS); MyMessage(); - /* Check this after callback to see if request failed */ -// bool failed(); - - - // Getters for header - uint8_t getCRC(); - uint8_t getVersion(); - uint8_t getLength(); - uint8_t getCommand(); - uint8_t getPayloadType(); - uint8_t getType(); - uint8_t getSensor(); - uint8_t getSender(); - uint8_t getLast(); - uint8_t getDestination(); - - // Getters for payload - - /** * If payload is something else than P_STRING you can have payload the value converted * to 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. */ char* getString(char *buffer = NULL); - - /** - * Returns payload untouched - */ void* getCustom(); - uint8_t getByte(); bool getBool(); double getDouble(); @@ -130,21 +126,6 @@ public: int getInt(); unsigned int getUInt(); - // Builder helper - MyMessage build(uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type); - - // Setters for header - MyMessage setCRC(uint8_t crc); - MyMessage setVersion(uint8_t version); - MyMessage setLength(uint8_t length); - MyMessage setCommand(uint8_t command); - MyMessage setPayloadType(uint8_t pt); - MyMessage setType(uint8_t type); - MyMessage setSensor(uint8_t sensor); - MyMessage setSender(uint8_t sender); - MyMessage setLast(uint8_t last); - MyMessage setDestination(uint8_t destination); - // Setters for payload MyMessage set(void* payload, uint8_t length); @@ -166,6 +147,7 @@ public: uint8_t last; // 8 bit - Id of last node this message passed uint8_t destination; // 8 bit - Id of destination node uint8_t crc; // 8 bit - Message checksum + // Each message can transfer a payload. We add one extra byte for string // terminator \0 to be "printable" this is not transferred OTA // This union is used to simplify the construction of the binary transferred int/long values. diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index dcd7798..02c8240 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -14,7 +14,6 @@ MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } - void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { failedTransmissions = 0; @@ -41,7 +40,7 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ } void MySensor::setupRelayMode(){ - s.childNodeTable = (uint8_t *) malloc(256); + s.childNodeTable = new uint8_t[256]; eeprom_read_block((void*)s.childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); } @@ -81,7 +80,7 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_ present(NODE_CHILD_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE); // Send parent information back to sensor net gateway. - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE).set(s.parentNodeId)); + buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE, s.parentNodeId); } @@ -92,7 +91,7 @@ uint8_t MySensor::getNodeId() { void MySensor::requestNodeId() { debug(PSTR("req node id\n")); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_REQUEST_ID)); + buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_REQUEST_ID); } @@ -110,7 +109,8 @@ void MySensor::findParentNode() { s.distance = 255; // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - sendWrite(BROADCAST_ADDRESS, msg.build(s.nodeId, BROADCAST_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_PING)); + build(msg, s.nodeId, BROADCAST_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_PING) + sendWrite(BROADCAST_ADDRESS, msg, true); // Wait for replies for max 10 seconds (or when buffer for all parent nodes have been filled up) unsigned long enter = millis(); @@ -121,32 +121,18 @@ void MySensor::findParentNode() { } - - - -/* -boolean MySensor::sendAck(MyMessage message) { - // Build ack message - ack = message; - ack.setCommand(C_SET_VARIABLE); - ack.setSender(s.nodeId); - ack.setDestination(message.getSender()); - return sendRoute(ack); -}*/ - - boolean MySensor::sendRoute(MyMessage message) { // Make sure to process any incoming messages before sending (could this end up in recursive loop?) process(); // If we still don't have any node id, re-request and skip this message. - if (s.nodeId == AUTO && !(message.getCommand() == C_INTERNAL && message.getType() == I_REQUEST_ID)) { + if (s.nodeId == AUTO && !(mGetCommand(message) == C_INTERNAL && message.type == I_REQUEST_ID)) { requestNodeId(); return false; } if (relayMode) { - uint8_t dest = message.getDestination(); + uint8_t dest = message.destination; uint8_t route = getChildRoute(dest); if (route>0 && route<255 && dest != GATEWAY_ADDRESS) { debug(PSTR("route %d.\n"), route); @@ -176,16 +162,11 @@ boolean MySensor::sendRoute(MyMessage message) { return false; } - - - - -boolean MySensor::sendWrite(uint8_t next, MyMessage message) { - uint8_t length = message.getLength(); - message.setLast(s.nodeId); - message.setCRC(crc8Message(message)); - - bool broadcast = message.getCommand() == C_INTERNAL && message.getType() == I_PING; +boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) { + uint8_t length = mGetLength(message); + message.last = s.nodeId; + mSetVersion(message, PROTOCOL_VERSION); + message.crc = crc8Message(message); // Make sure radio has powered up RF24::powerUp(); @@ -196,115 +177,54 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage message) { // Only debug print payload if it is of string type debug(PSTR("tx: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), - message.getSender(),message.getLast(), next, message.getDestination(), message.getSensor(), message.getCommand(), message.getType(), ok?"ok":"fail", message.getString()); + message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, ok?"ok":"fail", message.getString()); return ok; } bool MySensor::send(MyMessage message, bool enableAck) { - message.setSender(s.nodeId); + message.sender = s.nodeId; if (enableAck) { - message.setCommand(C_SET_WITH_ACK); + mSetCommand(message,C_SET_WITH_ACK); } return sendRoute(message); - -/* return wait(message, C_SET_VARIABLE, retry).getCommand() != C_FAILED; - } else { - return ; - }*/ } +void MySensor::sendBatteryLevel(uint8_t value) { + buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_BATTERY_LEVEL, value); +} void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType).set(LIBRARY_VERSION)); + buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, LIBRARY_VERSION); } void MySensor::sketchInfo(const char *name, const char *version) { - if (name != NULL) - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_NAME).set(name)); - if (version != NULL) - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_VERSION).set(version)); + if (name != NULL) { + buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_NAME, name); + } + if (version != NULL) { + buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_VERSION, version); + } } -/* -MyMessage MySensor::get( uint8_t childSensorId, uint8_t variableType, uint8_t destination, uint8_t retry) { - msg.build(s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType); - return wait(msg, C_SET_VARIABLE, retry); -}*/ - void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - sendRoute(msg.build(s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType)); + buildSend(msg, s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType); } - - -void MySensor::sendBatteryLevel(int value) { - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_BATTERY_LEVEL).set(value)); -} - -/* -MyMessage MySensor::wait(MyMessage message, uint8_t expectedResponseCommand, uint8_t retry) { - while (retry>0) { - - uint8_t sensor = message.getSensor(); - uint8_t type = message.getType(); - sendRoute(message); - uint8_t i = 0; - while (i < 100) { // 1 seconds timeout before re-sending status request - while (process()) { - // Check that it is right type of message and not a routing message - if (msg.getCommand() == expectedResponseCommand && - msg.getType() == type && - msg.getSensor() == sensor) { - return msg; - } - } - delay(10); - i++; - } - retry--; - } - msg.setCommand(C_FAILED); - return msg; -} -*/ - - -/* -MyMessage MySensor::getInternal(uint8_t variableType, uint8_t retry) { - msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType); - return wait(msg, C_INTERNAL, retry); -}*/ - -/*unsigned long MySensor::getTime(uint8_t retry) { - MyMessage message = getInternal(I_TIME, retry); - return message == NULL ? 0UL : message.getULong(); -}*/ - void MySensor::requestTime() { - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME)); + buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME); } -/*bool MySensor::isMetricSystem(uint8_t retry) { - MyMessage message = getInternal(I_UNIT, retry); - return (message == NULL) - return true; - else - return message.getByte() == 'M'; -}*/ - -void MySensor::requestIsMetricSystem() { - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_UNIT)); +void MySensor::requestConfiguration() { + buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CONFIG); } - boolean MySensor::process() { uint8_t pipe; boolean available = RF24::available(&pipe); - if (available && pipe<7) { uint8_t len = RF24::getDynamicPayloadSize(); RF24::read(&msg, len); @@ -316,14 +236,14 @@ boolean MySensor::process() { // Make sure string gets terminated ok for full string messages. msg.data[len - HEADER_SIZE ] = '\0'; debug(PSTR("rx: %d-%d-%d s=%d,c=%d,t=%d,cr=%s: %s\n"), - msg.getSender(), msg.getLast(), msg.getDestination(), msg.getSensor(), msg.getCommand(), msg.getType(), valid==0?"ok":valid==1?"ec":"ev", msg.getString()); + msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, valid==0?"ok":valid==1?"ec":"ev", msg.getString()); if (ok) { - uint8_t command = msg.getCommand(); - uint8_t type = msg.getType(); - uint8_t sender = msg.getSender(); - uint8_t last = msg.getLast(); - uint8_t destination = msg.getDestination(); + uint8_t command = mGetCommand(msg); + uint8_t type = msg.type; + uint8_t sender = msg.sender; //getSender(); + uint8_t last = msg.last; + uint8_t destination = msg.destination; if (relayMode && command == C_INTERNAL && type == I_PING && s.nodeId != AUTO) { // Relaying nodes should always answer ping messages @@ -331,8 +251,9 @@ boolean MySensor::process() { // between ping ack messages from other relaying nodes randomSeed(millis()); delay(random(2000)); - uint8_t replyTo = msg.getSender(); - msg.build(s.nodeId, replyTo, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK).set(s.distance); + uint8_t replyTo = msg.sender; + build(msg, s.nodeId, replyTo, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK); + msg.set(s.distance); sendWrite(replyTo, msg); return false; } else if (destination == s.nodeId) { @@ -349,7 +270,7 @@ boolean MySensor::process() { if (distance0 && route<255) { // This message should be forwarded to a child node. If we send message // to this nodes pipe then all children will receive it because the are @@ -482,7 +411,7 @@ MyMessage MySensor::getLastMessage() { * calculate CRC8 on MyMessage data taking care of data structure and protocol version */ uint8_t MySensor::crc8Message(MyMessage message) { - uint8_t len = message.getLength(); + uint8_t len = mGetLength(message); uint8_t crc = 0x00; uint8_t loop_count; uint8_t bit_counter; @@ -491,7 +420,7 @@ uint8_t MySensor::crc8Message(MyMessage message) { uint8_t number_of_bytes_to_read = (uint8_t)sizeof(message); // Must set crc to a constant value. - message.setCRC(0); + message.crc = 0; // fill unused space by zeroes for string data only if(len>=0 && len < sizeof(message.data)-1) { @@ -525,16 +454,17 @@ uint8_t MySensor::crc8Message(MyMessage message) { uint8_t MySensor::validate(MyMessage message) { - uint8_t oldCrc = message.getCRC(); + uint8_t oldCrc = message.crc; uint8_t newCrc = crc8Message(message); - if(!(message.getVersion() == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; + if(!(mGetVersion(message) == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; return VALIDATE_OK; } + void MySensor::addChildRoute(uint8_t childId, uint8_t route) { if (s.childNodeTable[childId] != route) { s.childNodeTable[childId] = route; @@ -553,34 +483,13 @@ uint8_t MySensor::getChildRoute(uint8_t childId) { return s.childNodeTable[childId]; } -void MySensor::clearChildRoutes() { - debug(PSTR("rd=clear\n")); - for (unsigned int i=0;i< sizeof(s.childNodeTable); i++) { - removeChildRoute(i); - } - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN).set("")); -} - -void MySensor::sendChildren() { - // Send in which children that is using this node as parent. - // TODO: Fix this - for (int i=0;i< 50; i++) { -// Serial.println(s.childNodeTable[i]); - debug(PSTR("rd=%d, %d\n"), i, getChildRoute(i) ); - } - sendRoute(msg.build(s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN).set("n/a")); -} - - - - #ifdef DEBUG void MySensor::debugPrint(const char *fmt, ... ) { char fmtBuffer[300]; if (s.nodeId == GATEWAY_ADDRESS) { // prepend debug message to be handled correctly by gw - Serial.print("0;0;4;11;"); + Serial.write("0;0;4;11;"); } va_list args; va_start (args, fmt ); @@ -598,8 +507,8 @@ void MySensor::debugPrint(const char *fmt, ... ) { Serial.print(fmtBuffer); Serial.flush(); - //Serial.print("0;0;4;11;Mem free:"); - //Serial.println(freeRam()); + //Serial.write("0;0;4;11;Mem free:"); + //Serial.write(freeRam()); } #endif diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index a01b155..1b0c794 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -57,6 +57,22 @@ #define SEND_RETRIES 3 #define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 5 +#define build(_msg, _sender, _destination, _sensor, _cmd, _type) \ + _msg.sender = _sender; \ + _msg.destination = _destination;\ + _msg.sensor = _sensor; \ + _msg.type = _type; \ + mSetCommand(_msg,_cmd); \ + +#define buildSend(_msg, _sender, _destination, _sensor, _cmd, _type) \ + build(_msg, _sender, _destination, _sensor, _cmd, _type) \ + sendRoute(_msg); + +#define buildSendPayload(_msg, _sender, _destination, _sensor, _cmd, _type, _value) \ + build(_msg, _sender, _destination, _sensor, _cmd, _type) \ + _msg.set(_value); \ + sendRoute(_msg); + class MySensor : public RF24 { @@ -120,7 +136,7 @@ class MySensor : public RF24 * Send this nodes battery level to gateway. * @param level Level between 0-100(%) */ - void sendBatteryLevel(int level); + void sendBatteryLevel(uint8_t level); /** * Fetches a value from gateway or some other sensor in the radio network. Resends request and waits for response. @@ -144,26 +160,24 @@ class MySensor : public RF24 * @param callback for result. This will be called when a message reply is received or request fails (see status of second argument). * @param destination The nodeId of other node in radio network. Default is gateway */ - /*void (* msgCallback)(MyMessage, bool)=NULL, */ void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); /** - * Fetches time from sensor net gateway. + * Requests time from controller. Pick up response in callback. * - * @return Seconds since 1970, 0 if failed. + * @return Seconds since 1970. */ -// unsigned long getTime(uint8_t retry=SEND_RETRIES); void requestTime(); /** - * Fetches unit setting from sensor net gateway. Returns true if metric system has been selected which means - * that sensor should report it's information in: celsius, meter, cm, gram, km/h, m/s etc.. + * Fetches configuration from controller. Returns true if metric system has been selected which means + * that sensor should report it's information in: + * celsius, meter, cm, gram, km/h, m/s etc.. * If false is returned the sensor should report data in imperial system which means * fahrenheit, feet, gallon, mph etc... */ - void requestIsMetricSystem(); -// bool isMetricSystem(uint8_t retry=SEND_RETRIES); + void requestConfiguration(); /** @@ -180,9 +194,6 @@ class MySensor : public RF24 - - - #ifdef DEBUG void debugPrint(const char *fmt, ... ); int freeRam(); @@ -199,44 +210,24 @@ class MySensor : public RF24 MyMessage msg; // Buffer for incoming messages. uint8_t failedTransmissions; boolean relayMode; -// char convBuffer[20]; void setupRelayMode(); void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); void findParentNode(); boolean sendRoute(MyMessage message); - boolean sendWrite(uint8_t dest, MyMessage message); -// void sendInternal(uint8_t variableType, const char *value); -// boolean sendAck(MyMessage message); - boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binaryMessage); + boolean sendWrite(uint8_t dest, MyMessage message, bool broadcast=false); uint8_t validate(MyMessage message); - void sendChildren(); - private: - // Callbacks for reqeusts -/* struct callback { - bool active; // Deactivates callback after each reply - uint8_t command; // Command type - uint8_t sender; - uint8_t sensor; - uint8_t type; - void (*msgCallback)(MyMessage, bool); - };*/ - - MyMessage ack; // Buffer for ack messages. void (*msgCallback)(MyMessage); // Callback for undefined incoming messages void requestNodeId(); uint8_t crc8Message(MyMessage); -// MyMessage wait(MyMessage msg, uint8_t expectedReceiveType, uint8_t retry); -// MyMessage getInternal(uint8_t variableType, uint8_t retry=SEND_RETRIES); uint8_t getChildRoute(uint8_t childId); void addChildRoute(uint8_t childId, uint8_t route); void removeChildRoute(uint8_t childId); - void clearChildRoutes(); }; #endif diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index b43e6fc..f4375d1 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -4,7 +4,6 @@ #include #include #include -#include #include @@ -48,18 +47,15 @@ void loop() void incomingMessage(MyMessage message) { // We only expect one type of message from controller. But we better check anyway. - Serial.println("hej"); - if (message.getType()==V_LIGHT) { - bool status = message.getBool(); - + if (message.type==V_LIGHT) { // Change relay state - digitalWrite(message.getSensor(), status?RELAY_ON:RELAY_OFF); + digitalWrite(message.sensor, message.getBool()?RELAY_ON:RELAY_OFF); // Write some debug info - Serial.print("Incoming change for relay on pin:"); - Serial.print(message.getSensor()); + Serial.print("Incoming change for sensor:"); + Serial.print(message.sensor); Serial.print(", New status: "); - Serial.println(status); - } + Serial.println(message.getBool()); + } } From 7080520a3f0c793e79070aa0f878d809bc856bd4 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 16 May 2014 07:24:24 +0200 Subject: [PATCH 246/326] Fix comment. Pseudo random. --- libraries/MySensors/MyMessage.cpp | 8 +- libraries/MySensors/MyMessage.h | 4 +- libraries/MySensors/MySensor.cpp | 343 +++++++++++++++--------------- libraries/MySensors/MySensor.h | 24 +-- 4 files changed, 184 insertions(+), 195 deletions(-) diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 9175489..e1a0db2 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -26,16 +26,16 @@ char* MyMessage::getString(char *buffer) { itoa(bValue, buffer, MAX_PAYLOAD); break; case P_INT16: - itoa(iValue, data, MAX_PAYLOAD); + itoa(iValue, buffer, MAX_PAYLOAD); break; case P_UINT16: - ltoa(uiValue, data, MAX_PAYLOAD); + ltoa(uiValue, buffer, MAX_PAYLOAD); break; case P_LONG32: - ltoa(lValue, data, MAX_PAYLOAD); + ltoa(lValue, buffer, MAX_PAYLOAD); break; case P_ULONG32: - ltoa(ulValue, data, MAX_PAYLOAD); + ltoa(ulValue, buffer, MAX_PAYLOAD); break; case P_CUSTOM: // TODO: Ok, what do we do here? We should probably convert this to hex diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 6d4f053..af9f294 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -22,8 +22,8 @@ // Message types typedef enum { C_PRESENTATION = 0, - C_SET_VARIABLE = 1, - C_REQ_VARIABLE = 2, + C_SET = 1, + C_REQ = 2, C_SET_WITH_ACK = 3, C_INTERNAL = 4, C_STREAM = 5 // For Firmaware and other larger chunks of data that need to be divided into pieces. diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 02c8240..e3dfc4f 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -111,11 +111,10 @@ void MySensor::findParentNode() { // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) build(msg, s.nodeId, BROADCAST_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_PING) sendWrite(BROADCAST_ADDRESS, msg, true); - // Wait for replies for max 10 seconds (or when buffer for all parent nodes have been filled up) unsigned long enter = millis(); - // Wait for ack responses 5 seconds - while (millis() - enter < 5000) { + // Wait for ack responses slightly more than 1 second + while (millis() - enter < 1200) { process(); } @@ -187,6 +186,8 @@ bool MySensor::send(MyMessage message, bool enableAck) { message.sender = s.nodeId; if (enableAck) { mSetCommand(message,C_SET_WITH_ACK); + } else { + mSetCommand(message,C_SET); } return sendRoute(message); } @@ -209,15 +210,14 @@ void MySensor::sketchInfo(const char *name, const char *version) { } void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - buildSend(msg, s.nodeId, destination, childSensorId, C_REQ_VARIABLE, variableType); + buildSend(msg, s.nodeId, destination, childSensorId, C_REQ, variableType); } void MySensor::requestTime() { buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME); } - -void MySensor::requestConfiguration() { +void MySensor::requestConfig() { buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CONFIG); } @@ -225,179 +225,184 @@ boolean MySensor::process() { uint8_t pipe; boolean available = RF24::available(&pipe); - if (available && pipe<7) { - uint8_t len = RF24::getDynamicPayloadSize(); - RF24::read(&msg, len); - RF24::writeAckPayload(pipe,&pipe, 1 ); + if (!available || pipe>6) + return false; - uint8_t valid = validate(msg); - boolean ok = valid == VALIDATE_OK; + uint8_t len = RF24::getDynamicPayloadSize(); + RF24::read(&msg, len); + RF24::writeAckPayload(pipe,&pipe, 1 ); - // Make sure string gets terminated ok for full string messages. - msg.data[len - HEADER_SIZE ] = '\0'; - debug(PSTR("rx: %d-%d-%d s=%d,c=%d,t=%d,cr=%s: %s\n"), - msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, valid==0?"ok":valid==1?"ec":"ev", msg.getString()); + uint8_t valid = validate(msg); + boolean ok = valid == VALIDATE_OK; - if (ok) { - uint8_t command = mGetCommand(msg); - uint8_t type = msg.type; - uint8_t sender = msg.sender; //getSender(); - uint8_t last = msg.last; - uint8_t destination = msg.destination; + // Make sure string gets terminated ok for full string messages. + msg.data[len - HEADER_SIZE ] = '\0'; + debug(PSTR("rx: %d-%d-%d s=%d,c=%d,t=%d,cr=%s: %s\n"), + msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, valid==0?"ok":valid==1?"ec":"ev", msg.getString()); - if (relayMode && command == C_INTERNAL && type == I_PING && s.nodeId != AUTO) { - // Relaying nodes should always answer ping messages - // Wait a random delay of 0-2 seconds to minimize collision - // between ping ack messages from other relaying nodes - randomSeed(millis()); - delay(random(2000)); - uint8_t replyTo = msg.sender; - build(msg, s.nodeId, replyTo, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK); - msg.set(s.distance); - sendWrite(replyTo, msg); + if (!ok) + return false; + + uint8_t command = mGetCommand(msg); + uint8_t type = msg.type; + uint8_t sender = msg.sender; //getSender(); + uint8_t last = msg.last; + uint8_t destination = msg.destination; + + if (relayMode && command == C_INTERNAL && type == I_PING) { + // Relaying nodes should always answer ping messages + // Wait a random delay of 0-2 seconds to minimize collision + // between ping ack messages from other relaying nodes + delay(millis() & 0x3ff); + + build(msg, s.nodeId, sender, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK); + msg.set(s.distance); + sendWrite(sender, msg); + return false; + } else if (destination == s.nodeId) { + // This message is addressed to this node + + if (relayMode && last != s.parentNodeId) { + // Message is from one of the child nodes. Add it to routing table. + addChildRoute(sender, last); + } + + if (command == C_INTERNAL) { + if (type == I_PING_ACK) { + // We've received a reply to our PING message looking for parent. + uint8_t distance = msg.getByte(); + if (distance0 && route<255) { - // This message should be forwarded to a child node. If we send message - // to this nodes pipe then all children will receive it because the are - // all listening to this nodes pipe. - // - // +----B - // -A - // +----C------D - // - // We're node C, Message comes from A and has destination D - // - // lookup route in table and send message there - sendWrite(route, msg); - } else if (pipe == CURRENT_NODE_PIPE) { - // A message comes from a child node and we have no - // route for it. - // - // +----B - // -A - // +----C------D <-- Message comes from D - // - // We're node C - // - // Message should be passed to node A (this nodes relay) - - // This message should be routed back towards sensor net gateway - sendWrite(s.parentNodeId, msg); - // Add this child to our "routing table" if it not already exist - addChildRoute(sender, last); - } else { - // We're snooped a message directed to gateway from another branch - // Make sure to remove the sender node from our routing table. - // - // +-----B <-- Message comes from here - // -A - // +-----C <-- We're here - // - // So the sender of message should never be in our - // routing table. - // - // We could also end up in this else-statement if message - // destination has no route. - // This can happen if a node never has sent any message to - // sensor net gateway (which will build up routing information in all nodes - // that gets passed). - - // THIS SHOULD NOT HAPPEN ANY MORE - - /*if (s.nodeId == GATEWAY_ADDRESS) { - // We should perhaps inform gateway about the no-route situation?! - debug(PSTR("ro=?\n")); - + } else if (type == I_REQUEST_ID && sender == GATEWAY_ADDRESS) { + if (s.nodeId == AUTO) { + s.nodeId = msg.getByte(); + // Write id to EEPROM + if (s.nodeId == AUTO) { + // sensor net gateway will return max id if all sensor id are taken + debug(PSTR("full\n")); + while (1); // Wait here. Nothing else we can do... } else { - //debug(PSTR("Remove child node from routing table.\n")); - removeChildRoute(sender); - }*/ + RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); + eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, s.nodeId); + } + debug(PSTR("id=%d\n"), s.nodeId); + } + return false; + } else if (relayMode && sender == GATEWAY_ADDRESS) { + if (type == I_RELAY_NODE && destination != GATEWAY_ADDRESS) { + // Gatewa/controller wants this node to refresh its parent node + findParentNode(); + // Send relay information back to sensor net gateway node device. + buildSendPayload(msg,s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE,s.parentNodeId); + return false; + } else if (type == I_CHILDREN) { + if (msg.getByte() == 'F') { + // TODO: We should send in as many children we can fit in a message (binary) + // For now we only print routing table for this node. + for (uint8_t i=0;i< 50; i++) { + debug(PSTR("rd=%d, %d\n"), i, getChildRoute(i) ); + } + buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN, "n/a"); + } else if (msg.getByte() == 'C') { + // Clears child relay data for this node + debug(PSTR("rd=clear\n")); + for (uint8_t i=0;i< sizeof(s.childNodeTable); i++) { + removeChildRoute(i); + } + buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN,""); + } + return false; } - } } + // Check if sender requests an ack back. + if (command == C_SET_WITH_ACK) { + ack = msg; + mSetCommand(ack,C_SET); + ack.sender = s.nodeId; + ack.destination = msg.sender; + sendRoute(ack); + // The library user should not need to care about this ack request. Just treat it as a normal SET. + mSetCommand(msg,C_SET); + } + // Call incoming message callback if available + if (msgCallback != NULL) { + msgCallback(msg); + } + // Return true if message was addressed for this node... + return true; + } else if (relayMode) { + // We should try to relay this message to another node + + uint8_t route = getChildRoute(msg.destination); + if (route>0 && route<255) { + // This message should be forwarded to a child node. If we send message + // to this nodes pipe then all children will receive it because the are + // all listening to this nodes pipe. + // + // +----B + // -A + // +----C------D + // + // We're node C, Message comes from A and has destination D + // + // lookup route in table and send message there + sendWrite(route, msg); + } else if (pipe == CURRENT_NODE_PIPE) { + // A message comes from a child node and we have no + // route for it. + // + // +----B + // -A + // +----C------D <-- Message comes from D + // + // We're node C + // + // Message should be passed to node A (this nodes relay) + + // This message should be routed back towards sensor net gateway + sendWrite(s.parentNodeId, msg); + // Add this child to our "routing table" if it not already exist + addChildRoute(sender, last); + } else { + // We're snooped a message directed to gateway from another branch + // Make sure to remove the sender node from our routing table. + // + // +-----B <-- Message comes from here + // -A + // +-----C <-- We're here + // + // So the sender of message should never be in our + // routing table. + // + // We could also end up in this else-statement if message + // destination has no route. + // This can happen if a node never has sent any message to + // sensor net gateway (which will build up routing information in all nodes + // that gets passed). + + // THIS SHOULD NOT HAPPEN ANY MORE + + /*if (s.nodeId == GATEWAY_ADDRESS) { + // We should perhaps inform gateway about the no-route situation?! + debug(PSTR("ro=?\n")); + + } else { + //debug(PSTR("Remove child node from routing table.\n")); + removeChildRoute(sender); + }*/ + } + } + + return false; } diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 1b0c794..38b3ac6 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -54,7 +54,6 @@ #define CURRENT_NODE_PIPE ((uint8_t)1) #define BROADCAST_PIPE ((uint8_t)2) -#define SEND_RETRIES 3 #define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 5 #define build(_msg, _sender, _destination, _sensor, _cmd, _type) \ @@ -127,10 +126,9 @@ class MySensor : public RF24 * * @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. - * @param retry Number of attempts before giving up. Default is SEND_RETRIES - * @return true Retuns false if ack is enabled and no ack came back + * @return true Returns true if message reached the first stop on its way to destination. */ - bool send(MyMessage msg, bool ack=false/*, uint8_t retry=SEND_RETRIES*/); + bool send(MyMessage msg, bool ack=false); /** * Send this nodes battery level to gateway. @@ -138,19 +136,6 @@ class MySensor : public RF24 */ void sendBatteryLevel(uint8_t level); - /** - * Fetches a value from gateway or some other sensor in the radio network. Resends request and waits for response. - * Note that if you have registered a callback in begin() it will also be called before message is returned - * by get() - * - * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. - * @param variableType The variableType to fetch - * @param destination The nodeId of other node in radio network. Default is gateway - * @param number of attempts to fetch - * @return Message response - */ -// MyMessage get(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS, uint8_t retry=SEND_RETRIES); - /** * Requests a value from gateway or some other sensor in the radio network. * Make sure to add callback in begin() to handle request response. @@ -162,7 +147,6 @@ class MySensor : public RF24 */ void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); - /** * Requests time from controller. Pick up response in callback. * @@ -171,13 +155,13 @@ class MySensor : public RF24 void requestTime(); /** - * Fetches configuration from controller. Returns true if metric system has been selected which means + * Requests configuration from controller. Returns true if metric system has been selected which means * that sensor should report it's information in: * celsius, meter, cm, gram, km/h, m/s etc.. * If false is returned the sensor should report data in imperial system which means * fahrenheit, feet, gallon, mph etc... */ - void requestConfiguration(); + void requestConfig(); /** From 9291a97d452050d4fa329ed15c67ba676865bcd1 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 16 May 2014 10:35:26 +0200 Subject: [PATCH 247/326] added some message builder helpers. Optimized libray a bit using inline functions and macros. Converted Dallas example to use new library api. --- libraries/MySensors/MyMessage.cpp | 17 ++++ libraries/MySensors/MyMessage.h | 8 +- libraries/MySensors/MySensor.cpp | 23 ++++- libraries/MySensors/MySensor.h | 16 ---- .../DallasTemperatureSensor.ino | 86 +++++++++++++------ .../examples/RelayActuator/RelayActuator.ino | 2 - 6 files changed, 104 insertions(+), 48 deletions(-) diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index e1a0db2..4834b46 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -4,6 +4,7 @@ MyMessage::MyMessage() { + destination = 0; // Gateway is default destination } @@ -60,6 +61,7 @@ uint8_t MyMessage::getByte() { return 0; } } + bool MyMessage::getBool() { return !(data[0]-'0'==0); } @@ -114,6 +116,21 @@ unsigned int MyMessage::getUInt() { } +MyMessage MyMessage::setType(uint8_t _type) { + type = _type; + return *this; +} + +MyMessage MyMessage::setSensor(uint8_t _sensor) { + sensor = _sensor; + return *this; +} + +MyMessage MyMessage::setDestination(uint8_t _destination) { + destination = _destination; + return *this; +} + // Set payload MyMessage MyMessage::set(void* value, uint8_t length) { miSetPayloadType(P_CUSTOM); diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index af9f294..27b16a2 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -100,8 +100,8 @@ typedef enum { // Check and fetch data from special messages -#define isTimeResponse(_msg) (mGetCommand(_msg)==C_INTERN && _msg.type==I_TIME) -#define isConfigResponse(_msg) (mGetCommand(_msg)==C_INTERN && _msg.type==I_CONFIG) +#define isTimeResponse(_msg) (mGetCommand(_msg)==C_INTERNAL && _msg.type==I_TIME) +#define isConfigResponse(_msg) (mGetCommand(_msg)==C_INTERNAL && _msg.type==I_CONFIG) #define mIsMetric(_msg) (_msg.getByte() == 'M') @@ -126,6 +126,10 @@ public: int getInt(); unsigned int getUInt(); + // Setters for building message "on the fly" + MyMessage setType(uint8_t type); + MyMessage setSensor(uint8_t sensor); + MyMessage setDestination(uint8_t destination); // Setters for payload MyMessage set(void* payload, uint8_t length); diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index e3dfc4f..41ae49d 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -11,6 +11,27 @@ #include "MySensor.h" +// Inline and macros +inline MyMessage build (MyMessage msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) { + msg.sender = sender; + msg.destination = destination; + msg.sensor = sensor; + msg.type = type; + mSetCommand(msg,command); + return msg; +} + +// Created macros for these, compiler seems to do a bad job when doing sendRoute(build(...)) +#define buildSend(_msg, _sender, _destination, _sensor, _cmd, _type) \ + build(_msg, _sender, _destination, _sensor, _cmd, _type);\ + sendRoute(_msg); + +#define buildSendPayload(_msg, _sender, _destination, _sensor, _cmd, _type, _value) \ + build(_msg, _sender, _destination, _sensor, _cmd, _type).set(_value);\ + _msg.set(_value);\ + sendRoute(_msg); + + MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } @@ -109,7 +130,7 @@ void MySensor::findParentNode() { s.distance = 255; // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - build(msg, s.nodeId, BROADCAST_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_PING) + build(msg, s.nodeId, BROADCAST_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_PING); sendWrite(BROADCAST_ADDRESS, msg, true); unsigned long enter = millis(); diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 38b3ac6..0b22504 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -56,22 +56,6 @@ #define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 5 -#define build(_msg, _sender, _destination, _sensor, _cmd, _type) \ - _msg.sender = _sender; \ - _msg.destination = _destination;\ - _msg.sensor = _sensor; \ - _msg.type = _type; \ - mSetCommand(_msg,_cmd); \ - -#define buildSend(_msg, _sender, _destination, _sensor, _cmd, _type) \ - build(_msg, _sender, _destination, _sensor, _cmd, _type) \ - sendRoute(_msg); - -#define buildSendPayload(_msg, _sender, _destination, _sensor, _cmd, _type, _value) \ - build(_msg, _sender, _destination, _sensor, _cmd, _type) \ - _msg.set(_value); \ - sendRoute(_msg); - class MySensor : public RF24 { diff --git a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino index e4fe953..ddeeacc 100644 --- a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino +++ b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino @@ -1,61 +1,93 @@ +// Example sketch showing how to send in OneWire temperature readings +#include #include #include -#include #include #include #include -#include #define ONE_WIRE_BUS 3 // Pin where dallase sensor is connected #define MAX_ATTACHED_DS18B20 16 unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) - OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); -Sensor gw; +MySensor gw; Sleep sleep; float lastTemperature[MAX_ATTACHED_DS18B20]; int numSensors=0; +boolean receivedConfig = false; boolean metric = true; +MyMessage out; void setup() { + // Startup OneWire sensors.begin(); - gw.begin(); + + // Startup and initialize MySensors library. Set callback for incoming messages. + gw.begin(response); // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("Temperature Sensor", "1.0"); + gw.sketchInfo("Temperature Sensor", "1.0"); - // Fetch the number of attached sensors + // Fetch the number of attached temperature sensors numSensors = sensors.getDeviceCount(); - // Register all sensors to gw (they will be created as child devices) + + // Present all sensors to controller for (int i=0; i(static_cast((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; - // Only send data if temperature has changed and no error - if (lastTemperature[i] != temperature && temperature != -127.00) { - gw.powerUp(); // Powerup introduces a small delay (which is missing in radio.write powerup) - // Send variable (using registered shortcut) to gw - gw.sendVariable(i, V_TEMP, temperature, 1); - lastTemperature[i]=temperature; + // Process incoming messages + gw.process(); + + if (receivedConfig) { + // Fetch temperatures from Dallas sensors + sensors.requestTemperatures(); + + // Read temperatures and send them to controller + for (int i=0; i(static_cast((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; + + // Only send data if temperature has changed and no error + if (lastTemperature[i] != temperature && temperature != -127.00) { + + // Send in the new temperature + gw.send(out.setSensor(i).set(temperature,1)); + lastTemperature[i]=temperature; + } } + // Power down the radio and sleep Arduino to save batteries. + delay(500); + gw.powerDown(); + sleep.pwrDownMode(); //set sleep mode + sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime + } else { + // We're still missing the configuration. Wait a little bit and request it again. + delay(1000); + gw.requestConfig(); + } +} + +void response(MyMessage msg) { + // Check that received message is a configuration + if (isConfigResponse(msg)) { + // Read config from message + metric = mIsMetric(msg); + receivedConfig = true; } - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - delay(500); - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime } diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index f4375d1..0d57c94 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -1,7 +1,5 @@ // Example sketch showing how to control physical relays. -#include -#include #include #include #include From 891c826a0c574e858727e67b128584aeb632e44c Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 16 May 2014 14:48:18 +0200 Subject: [PATCH 248/326] * Converted time example. Callback for time request was the only clean solution. Now global callback for incoming messages only needs to care about SET commands coming from other nodes or gateway. * New api for storing states. RelayActator now stores state in eeprom, making it more standalone. * Controller config is now fetched in the background without needing any special tratment in sketch. Last received config is stored in eeprom: --- libraries/MySensors/MyMessage.h | 10 +- libraries/MySensors/MySensor.cpp | 211 ++++++++++-------- libraries/MySensors/MySensor.h | 99 +++++--- .../DallasTemperatureSensor.ino | 59 ++--- .../examples/RelayActuator/RelayActuator.ino | 25 +-- .../TimeAndVariableSensor.ino | 66 ------ .../TimeAwareSensor/TimeAwareSensor.ino | 56 +++++ 7 files changed, 270 insertions(+), 256 deletions(-) delete mode 100644 libraries/MySensors/examples/TimeAndVariableSensor/TimeAndVariableSensor.ino create mode 100644 libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 27b16a2..db71049 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -41,8 +41,8 @@ typedef enum { // Type of internal messages (for internal messages) typedef enum { I_BATTERY_LEVEL, I_TIME, I_VERSION, I_REQUEST_ID, - I_INCLUSION_MODE, I_RELAY_NODE, I_PING, I_PING_ACK, - I_LOG_MESSAGE, I_CHILDREN, I_CONFIG, I_SKETCH_NAME, I_SKETCH_VERSION + I_INCLUSION_MODE, I_CONFIG, I_PING, I_PING_ACK, + I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION } internal; // Type of sensor (for presentation message) @@ -99,12 +99,6 @@ typedef enum { #define miSetPayloadType(_pt) BF_SET(command_payload, _pt, 4, 4) -// Check and fetch data from special messages -#define isTimeResponse(_msg) (mGetCommand(_msg)==C_INTERNAL && _msg.type==I_TIME) -#define isConfigResponse(_msg) (mGetCommand(_msg)==C_INTERNAL && _msg.type==I_CONFIG) -#define mIsMetric(_msg) (_msg.getByte() == 'M') - - class MyMessage { public: diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 41ae49d..76e2510 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -11,7 +11,7 @@ #include "MySensor.h" -// Inline and macros +// Inline function and macros inline MyMessage build (MyMessage msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) { msg.sender = sender; msg.destination = destination; @@ -61,8 +61,8 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ } void MySensor::setupRelayMode(){ - s.childNodeTable = new uint8_t[256]; - eeprom_read_block((void*)s.childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); + childNodeTable = new uint8_t[256]; + eeprom_read_block((void*)childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); } void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_t _nodeId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { @@ -76,61 +76,72 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_ setupRadio(paLevel, channel, dataRate); // Read settings from EEPROM - eeprom_read_block((void*)&s, (void*)EEPROM_START, 3*sizeof(uint8_t)); + eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); + // Read latest received controller configuration from EEPROM + eeprom_read_block((void*)&cc, (void*)EEPROM_LOCAL_CONFIG_ADDRESS, sizeof(ControllerConfig)); + + if (_nodeId != AUTO) { // Set static id - s.nodeId = _nodeId; + nc.nodeId = _nodeId; } // If no parent was found. Try to find one. - if (s.parentNodeId == 0xff) { + if (nc.parentNodeId == 0xff) { findParentNode(); } // Try to fetch node-id from gateway - if (s.nodeId == AUTO) { + if (nc.nodeId == AUTO) { requestNodeId(); } + debug(PSTR("%s started\n"), relayMode?"relay":"sensor"); // Open reading pipe for messages directed to this node (set write pipe to same) - RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(s.nodeId)); - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); + RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId)); + RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); - // Send presentation for this radio node - present(NODE_CHILD_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE); + // Send presentation for this radio node (attach + present(NODE_SENSOR_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE); - // Send parent information back to sensor net gateway. - buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE, s.parentNodeId); + // Send a configuration exchange request to controller + // Node sends parent node. Controller answers with latest node configuration + // which is picked up in process() + buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, nc.parentNodeId); } uint8_t MySensor::getNodeId() { - return s.nodeId; + return nc.nodeId; +} + +ControllerConfig MySensor::getConfig() { + return cc; } void MySensor::requestNodeId() { debug(PSTR("req node id\n")); - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); - buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_REQUEST_ID); + RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); + buildSend(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_ID); } void MySensor::findParentNode() { - if (s.nodeId == GATEWAY_ADDRESS) + if (nc.nodeId == GATEWAY_ADDRESS) return; // Gateway has no business here! failedTransmissions = 0; // Open a reading pipe for current nodeId (if it differs from broadcast) - if (s.nodeId != BROADCAST_PIPE) { - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); + if (nc.nodeId != BROADCAST_PIPE) { + RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); } // Set distance to max - s.distance = 255; + nc.distance = 255; // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - build(msg, s.nodeId, BROADCAST_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_PING); + build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING); sendWrite(BROADCAST_ADDRESS, msg, true); unsigned long enter = millis(); @@ -146,7 +157,7 @@ boolean MySensor::sendRoute(MyMessage message) { process(); // If we still don't have any node id, re-request and skip this message. - if (s.nodeId == AUTO && !(mGetCommand(message) == C_INTERNAL && message.type == I_REQUEST_ID)) { + if (nc.nodeId == AUTO && !(mGetCommand(message) == C_INTERNAL && message.type == I_REQUEST_ID)) { requestNodeId(); return false; } @@ -162,15 +173,15 @@ boolean MySensor::sendRoute(MyMessage message) { } } - if (s.nodeId != GATEWAY_ADDRESS) { + if (nc.nodeId != GATEWAY_ADDRESS) { debug(PSTR("route parent\n")); // Should be routed back to gateway. - bool ok = sendWrite(s.parentNodeId, message); + bool ok = sendWrite(nc.parentNodeId, message); if (!ok) { // Failure when sending to parent node. The parent node might be down and we - // need to find another route to gateway. Max 20 retries before giving up. - if (failedTransmissions > FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { + // need to find another route to gateway. + if (failedTransmissions > SEARCH_FAILURES) { findParentNode(); } failedTransmissions++; @@ -184,7 +195,7 @@ boolean MySensor::sendRoute(MyMessage message) { boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) { uint8_t length = mGetLength(message); - message.last = s.nodeId; + message.last = nc.nodeId; mSetVersion(message, PROTOCOL_VERSION); message.crc = crc8Message(message); @@ -204,7 +215,7 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) { bool MySensor::send(MyMessage message, bool enableAck) { - message.sender = s.nodeId; + message.sender = nc.nodeId; if (enableAck) { mSetCommand(message,C_SET_WITH_ACK); } else { @@ -214,33 +225,31 @@ bool MySensor::send(MyMessage message, bool enableAck) { } void MySensor::sendBatteryLevel(uint8_t value) { - buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_BATTERY_LEVEL, value); + buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, value); } void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { - buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, LIBRARY_VERSION); + buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, LIBRARY_VERSION); } -void MySensor::sketchInfo(const char *name, const char *version) { +void MySensor::sendSketchInfo(const char *name, const char *version) { if (name != NULL) { - buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_NAME, name); + buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, name); } if (version != NULL) { - buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_VERSION, version); + buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, version); } } void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - buildSend(msg, s.nodeId, destination, childSensorId, C_REQ, variableType); + buildSend(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType); } -void MySensor::requestTime() { - buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME); +void MySensor::requestTime(void (* _timeCallback)(unsigned long)) { + timeCallback = _timeCallback; + buildSend(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME); } -void MySensor::requestConfig() { - buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CONFIG); -} boolean MySensor::process() { uint8_t pipe; @@ -266,7 +275,7 @@ boolean MySensor::process() { uint8_t command = mGetCommand(msg); uint8_t type = msg.type; - uint8_t sender = msg.sender; //getSender(); + uint8_t sender = msg.sender; uint8_t last = msg.last; uint8_t destination = msg.destination; @@ -275,15 +284,13 @@ boolean MySensor::process() { // Wait a random delay of 0-2 seconds to minimize collision // between ping ack messages from other relaying nodes delay(millis() & 0x3ff); - - build(msg, s.nodeId, sender, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK); - msg.set(s.distance); + build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK); + msg.set(nc.distance); sendWrite(sender, msg); return false; - } else if (destination == s.nodeId) { + } else if (destination == nc.nodeId) { // This message is addressed to this node - - if (relayMode && last != s.parentNodeId) { + if (relayMode && last != nc.parentNodeId) { // Message is from one of the child nodes. Add it to routing table. addChildRoute(sender, last); } @@ -292,62 +299,68 @@ boolean MySensor::process() { if (type == I_PING_ACK) { // We've received a reply to our PING message looking for parent. uint8_t distance = msg.getByte(); - if (distance(static_cast((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; + // Fetch and round temperature to one decimal + float temperature = static_cast(static_cast((gw.getConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; - // Only send data if temperature has changed and no error - if (lastTemperature[i] != temperature && temperature != -127.00) { + // Only send data if temperature has changed and no error + if (lastTemperature[i] != temperature && temperature != -127.00) { - // Send in the new temperature - gw.send(out.setSensor(i).set(temperature,1)); - lastTemperature[i]=temperature; - } + // Send in the new temperature + gw.send(out.setSensor(i).set(temperature,1)); + lastTemperature[i]=temperature; } - // Power down the radio and sleep Arduino to save batteries. - delay(500); - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime - } else { - // We're still missing the configuration. Wait a little bit and request it again. - delay(1000); - gw.requestConfig(); - } -} - -void response(MyMessage msg) { - // Check that received message is a configuration - if (isConfigResponse(msg)) { - // Read config from message - metric = mIsMetric(msg); - receivedConfig = true; } + // Power down the radio and sleep Arduino to save batteries. + delay(500); + gw.powerDown(); + sleep.pwrDownMode(); //set sleep mode + sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime } + diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index 0d57c94..f9800fb 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -1,4 +1,5 @@ // Example sketch showing how to control physical relays. +// This example will remember relay state even after power failure. #include #include @@ -17,29 +18,23 @@ void setup() // Initialize library and add callback for incoming messages gw.begin(incomingMessage, false); // Send the sketch version information to the gateway and Controller - gw.sketchInfo("Relay", "1.0"); + gw.sendSketchInfo("Relay", "1.0"); - // Register all sensors to gw (they will be created as child devices) - for (int i=0; i -#include -#include -#include -#include - -#define CHILD_ID 0 // Id of the sensor child - -Sensor gw; - - -void setup() -{ - gw.begin(); - - // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("Clock", "1.0"); - - // Register any sensortype. This example we just create a motion sensor. - gw.sendSensorPresentation(CHILD_ID, S_MOTION); - - setSyncProvider(getTime); - setSyncInterval(3600); // get time from gw every hour -} - -unsigned long getTime() { - return gw.getTime(); // Vera is time provider -} - -void loop() -{ -// long time = gw.getTime(); -// setTime(time); - - Serial.print(hour()); - printDigits(minute()); - printDigits(second()); - Serial.print(" "); - Serial.print(day()); - Serial.print(" "); - Serial.print(month()); - Serial.print(" "); - Serial.print(year()); - Serial.println(); - - // Ok now send time back to gw and store in in Variable1 - // of the motion sensor device - gw.sendVariable(CHILD_ID, V_VAR1, minute()); - - // wait 10 seconds... - delay(10000); -} - -void printDigits(int digits){ - // utility function for digital clock display: prints preceding colon and leading 0 - Serial.print(":"); - if(digits < 10) - Serial.print('0'); - Serial.print(digits); -} - - diff --git a/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino b/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino new file mode 100644 index 0000000..f7197ba --- /dev/null +++ b/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino @@ -0,0 +1,56 @@ + +// Example sketch showing how to request time from controller + +#include +#include +#include +#include + +MySensor gw; +boolean timeReceived = false; + +void setup() +{ + gw.begin(); + + // Send the sketch version information to the gateway and Controller + gw.sendSketchInfo("Clock", "1.0"); + + // Request time from controller. + gw.requestTime(receiveTime); +} + +// This is called when a new time value was received +void receiveTime(unsigned long time) { + // Ok, set incoming time + setTime(time); + timeReceived = true; +} + +void loop() +{ + gw.process(); + // Print time + if (timeReceived) { + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(day()); + Serial.print(" "); + Serial.print(month()); + Serial.print(" "); + Serial.print(year()); + Serial.println(); + } +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + + From 8e07382d749fbbd9407e5e666a77934b7965231f Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 16 May 2014 15:13:56 +0200 Subject: [PATCH 249/326] Gateway.cpp now compiles, Ethernet gateway fits in memory with debug enabled. :) --- libraries/MySensors/MyConfig.h | 2 +- libraries/MySensors/MyGateway.cpp | 18 ++++++++++-------- libraries/MySensors/MySensor.cpp | 2 +- libraries/MySensors/MySensor.h | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 5d7956f..474b705 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -12,6 +12,6 @@ /*** * Enable/Disable debug logging */ -//#define DEBUG +#define DEBUG #endif diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index 2abed16..843c90e 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -11,6 +11,7 @@ #include "MyGateway.h" + MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : MySensor(_cepin, _cspin) { ledMode = false; inclusionTime = _inclusion_time; @@ -37,8 +38,8 @@ void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e da useWriteCallback = false; } - s.nodeId = 0; - s.distance = 0; + nc.nodeId = 0; + nc.distance = 0; inclusionMode = 0; buttonTriggeredInclusion = false; countRx = 0; @@ -93,11 +94,6 @@ void MyGateway::checkButtonTriggeredInclusion() { serial(PSTR("0;0;%d;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE); buttonTriggeredInclusion = false; setInclusionMode(true); - -#ifdef DEBUG - sendChildren(); -#endif - } } @@ -154,7 +150,13 @@ void MyGateway::parseAndSend(char *commandBuffer) { } } else { txBlink(1); - ok = sendRoute(msg.build(GATEWAY_ADDRESS, destination, sensor, command, type).set(value)); + msg.sender = GATEWAY_ADDRESS; + msg.destination = destination; + msg.sensor = sensor; + msg.type = type; + mSetCommand(msg,command); + msg.set(value); + ok = sendRoute(msg); if (!ok) { errBlink(1); } diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 76e2510..55ac350 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -154,7 +154,7 @@ void MySensor::findParentNode() { boolean MySensor::sendRoute(MyMessage message) { // Make sure to process any incoming messages before sending (could this end up in recursive loop?) - process(); + // process(); // If we still don't have any node id, re-request and skip this message. if (nc.nodeId == AUTO && !(mGetCommand(message) == C_INTERNAL && message.type == I_REQUEST_ID)) { diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index f5042d6..c1d43df 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -204,6 +204,8 @@ class MySensor : public RF24 NodeConfig nc; // Essential settings for node to work ControllerConfig cc; // Configuration coming from controller boolean relayMode; + MyMessage msg; // Buffer for incoming messages. + MyMessage ack; // Buffer for ack messages. void setupRelayMode(); void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); @@ -214,8 +216,6 @@ class MySensor : public RF24 private: uint8_t failedTransmissions; uint8_t *childNodeTable; // In memory buffer for routing information to other nodes. also stored in EEPROM - MyMessage msg; // Buffer for incoming messages. - MyMessage ack; // Buffer for ack messages. void (*timeCallback)(unsigned long); // Callback for requested time messages void (*msgCallback)(MyMessage); // Callback for incoming messages from other nodes and gateway. From 9e043f3379d5daac81b5f729c007d8c486c490fe Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sat, 17 May 2014 00:46:04 +0200 Subject: [PATCH 250/326] Use pointers inernally instead of passing objects to save space. --- libraries/MySensors/MyGateway.cpp | 12 +- libraries/MySensors/MyGateway.h | 4 +- libraries/MySensors/MyMessage.cpp | 59 ++++++---- libraries/MySensors/MyMessage.h | 41 ++++--- libraries/MySensors/MySensor.cpp | 111 ++++++++---------- libraries/MySensors/MySensor.h | 12 +- .../DallasTemperatureSensor.ino | 2 +- .../examples/RelayActuator/RelayActuator.ino | 2 +- 8 files changed, 127 insertions(+), 116 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index 843c90e..f2d0cd1 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -156,7 +156,7 @@ void MyGateway::parseAndSend(char *commandBuffer) { msg.type = type; mSetCommand(msg,command); msg.set(value); - ok = sendRoute(msg); + ok = sendRoute(&msg); if (!ok) { errBlink(1); } @@ -177,7 +177,7 @@ void MyGateway::setInclusionMode(boolean newMode) { // Override normal validate to add error blink if crc check fails -uint8_t MyGateway::validate(MyMessage message) { +uint8_t MyGateway::validate(MyMessage *message) { uint8_t res = MySensor::validate(message); if (res == VALIDATE_BAD_CRC) { errBlink(1); @@ -188,8 +188,8 @@ uint8_t MyGateway::validate(MyMessage message) { void MyGateway::processRadioMessage() { if (process()) { // A new message was received from one of the sensors - MyMessage message = getLastMessage(); - if (mGetCommand(message) == C_PRESENTATION && inclusionMode) { + MyMessage *message = getLastMessage(); + if (mGetCommandP(message) == C_PRESENTATION && inclusionMode) { rxBlink(3); } else { rxBlink(1); @@ -214,8 +214,8 @@ void MyGateway::serial(const char *fmt, ... ) { } } -void MyGateway::serial(MyMessage msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); +void MyGateway::serial(MyMessage *msg) { + serial(PSTR("%d;%d;%d;%d;%s\n"),msg->sender, msg->sensor, mGetCommandP(msg), msg->type, msg->getString(convBuf)); } diff --git a/libraries/MySensors/MyGateway.h b/libraries/MySensors/MyGateway.h index 6dd19c4..e02b8e2 100644 --- a/libraries/MySensors/MyGateway.h +++ b/libraries/MySensors/MyGateway.h @@ -69,8 +69,8 @@ class MyGateway : public MySensor uint8_t pinEr; void serial(const char *fmt, ... ); - void serial(MyMessage msg); - uint8_t validate(MyMessage message); + void serial(MyMessage *msg); + uint8_t validate(MyMessage *message); void interruptStartInclusion(); void checkButtonTriggeredInclusion(); void setInclusionMode(boolean newMode); diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 4834b46..941dc06 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -14,7 +14,16 @@ void* MyMessage::getCustom() { return (void *)data; } -/* NOTE: Calling this method might affect the payload by converting it to its string representation */ +char* MyMessage::getString() { + uint8_t payloadType = miGetPayloadType(); + if (payloadType == P_STRING) { + data[miGetLength()] = '\0'; + return data; + } else { + return NULL; + } +} + char* MyMessage::getString(char *buffer) { uint8_t payloadType = miGetPayloadType(); @@ -116,85 +125,85 @@ unsigned int MyMessage::getUInt() { } -MyMessage MyMessage::setType(uint8_t _type) { +MyMessage* MyMessage::setType(uint8_t _type) { type = _type; - return *this; + return this; } -MyMessage MyMessage::setSensor(uint8_t _sensor) { +MyMessage* MyMessage::setSensor(uint8_t _sensor) { sensor = _sensor; - return *this; + return this; } -MyMessage MyMessage::setDestination(uint8_t _destination) { +MyMessage* MyMessage::setDestination(uint8_t _destination) { destination = _destination; - return *this; + return this; } // Set payload -MyMessage MyMessage::set(void* value, uint8_t length) { +MyMessage* MyMessage::set(void* value, uint8_t length) { miSetPayloadType(P_CUSTOM); miSetLength(length); memcpy(data, value, min(length, MAX_PAYLOAD)); - return *this; + return this; } -MyMessage MyMessage::set(const char* value) { +MyMessage* MyMessage::set(const char* value) { uint8_t length = strlen(value); miSetLength(length); miSetPayloadType(P_STRING); strncpy(data, value, min(length, MAX_PAYLOAD)); - return *this; + return this; } -MyMessage MyMessage::set(uint8_t value) { +MyMessage* MyMessage::set(uint8_t value) { miSetLength(1); miSetPayloadType(P_BYTE); data[0] = value; - return *this; + return this; } -MyMessage MyMessage::set(bool value) { +MyMessage* MyMessage::set(bool value) { miSetLength(1); miSetPayloadType(P_STRING); data[0] = value?'1':'0'; - return *this; + return this; } -MyMessage MyMessage::set(double value, uint8_t decimals) { +MyMessage* MyMessage::set(double value, uint8_t decimals) { dtostrf(value,2,decimals,data); miSetLength(strlen(data)); miSetPayloadType(P_STRING); - return *this; + return this; } -MyMessage MyMessage::set(unsigned long value) { +MyMessage* MyMessage::set(unsigned long value) { miSetPayloadType(P_ULONG32); miSetLength(4); ulValue = value; - return *this; + return this; } -MyMessage MyMessage::set(long value) { +MyMessage* MyMessage::set(long value) { miSetPayloadType(P_LONG32); miSetLength(4); lValue = value; - return *this; + return this; } -MyMessage MyMessage::set(unsigned int value) { +MyMessage* MyMessage::set(unsigned int value) { miSetPayloadType(P_INT16); miSetLength(2); uiValue = value; - return *this; + return this; } -MyMessage MyMessage::set(int value) { +MyMessage* MyMessage::set(int value) { miSetPayloadType(P_UINT16); miSetLength(2); iValue = value; - return *this; + return this; } diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index db71049..0039b0d 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -85,11 +85,20 @@ typedef enum { #define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) #define mSetCommand(_msg,_command) BF_SET(_msg.command_payload, _command, 0, 4) #define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_payload, _pt, 4, 4) +#define mSetVersionP(_msg,_version) BF_SET(_msg->version_length, _version, 0, 3) +#define mSetLengthP(_msg,_length) BF_SET(_msg->version_length, _length, 3, 5) +#define mSetCommandP(_msg,_command) BF_SET(_msg->command_payload, _command, 0, 4) +#define mSetPayloadTypeP(_msg, _pt) BF_SET(_msg->command_payload, _pt, 4, 4) #define mGetVersion(_msg) BF_GET(_msg.version_length, 0, 3) #define mGetLength(_msg) BF_GET(_msg.version_length, 3, 5) #define mGetCommand(_msg) BF_GET(_msg.command_payload, 0, 4) #define mGetPayloadType(_msg) BF_GET(_msg.command_payload, 4, 4) +#define mGetVersionP(_msg) BF_GET(_msg->version_length, 0, 3) +#define mGetLengthP(_msg) BF_GET(_msg->version_length, 3, 5) +#define mGetCommandP(_msg) BF_GET(_msg->command_payload, 0, 4) +#define mGetPayloadTypeP(_msg) BF_GET(_msg->command_payload, 4, 4) + // internal access for special fileds #define miGetPayloadType() BF_GET(command_payload, 4, 4) @@ -105,12 +114,14 @@ public: // Constructors MyMessage(); + + char* getString(); /** - * If payload is something else than P_STRING you can have payload the value converted - * to string representation by supplying a buffer with the minimum size of + * 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. */ - char* getString(char *buffer = NULL); + char* getString(char *buffer); void* getCustom(); uint8_t getByte(); bool getBool(); @@ -121,20 +132,20 @@ public: unsigned int getUInt(); // Setters for building message "on the fly" - MyMessage setType(uint8_t type); - MyMessage setSensor(uint8_t sensor); - MyMessage setDestination(uint8_t destination); + MyMessage* setType(uint8_t type); + MyMessage* setSensor(uint8_t sensor); + MyMessage* setDestination(uint8_t destination); // Setters for payload - MyMessage set(void* payload, uint8_t length); - MyMessage set(const char* value); - MyMessage set(uint8_t value); - MyMessage set(bool value); - MyMessage set(double value, uint8_t decimals); - MyMessage set(unsigned long value); - MyMessage set(long value); - MyMessage set(unsigned int value); - MyMessage set(int value); + MyMessage* set(void* payload, uint8_t length); + MyMessage* set(const char* value); + MyMessage* set(uint8_t value); + MyMessage* set(bool value); + MyMessage* set(double value, uint8_t decimals); + MyMessage* set(unsigned long value); + MyMessage* set(long value); + MyMessage* set(unsigned int value); + MyMessage* set(int value); uint8_t version_length; // 3 bit - Protocol version + 5 bit - Length of payload diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 55ac350..6921fcd 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -12,25 +12,15 @@ #include "MySensor.h" // Inline function and macros -inline MyMessage build (MyMessage msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) { - msg.sender = sender; - msg.destination = destination; - msg.sensor = sensor; - msg.type = type; - mSetCommand(msg,command); +inline MyMessage* build (MyMessage* msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) { + msg->sender = sender; + msg->destination = destination; + msg->sensor = sensor; + msg->type = type; + mSetCommandP(msg,command); return msg; } -// Created macros for these, compiler seems to do a bad job when doing sendRoute(build(...)) -#define buildSend(_msg, _sender, _destination, _sensor, _cmd, _type) \ - build(_msg, _sender, _destination, _sensor, _cmd, _type);\ - sendRoute(_msg); - -#define buildSendPayload(_msg, _sender, _destination, _sensor, _cmd, _type, _value) \ - build(_msg, _sender, _destination, _sensor, _cmd, _type).set(_value);\ - _msg.set(_value);\ - sendRoute(_msg); - MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } @@ -108,7 +98,9 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_ // Send a configuration exchange request to controller // Node sends parent node. Controller answers with latest node configuration // which is picked up in process() - buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, nc.parentNodeId); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG)->set(nc.parentNodeId)); + Serial.print("XX"); + Serial.println(nc.parentNodeId); } @@ -123,7 +115,7 @@ ControllerConfig MySensor::getConfig() { void MySensor::requestNodeId() { debug(PSTR("req node id\n")); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); - buildSend(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_ID); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_ID)); } @@ -141,8 +133,8 @@ void MySensor::findParentNode() { nc.distance = 255; // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING); - sendWrite(BROADCAST_ADDRESS, msg, true); + build(&msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING); + sendWrite(BROADCAST_ADDRESS, &msg, true); unsigned long enter = millis(); // Wait for ack responses slightly more than 1 second @@ -152,18 +144,18 @@ void MySensor::findParentNode() { } -boolean MySensor::sendRoute(MyMessage message) { +boolean MySensor::sendRoute(MyMessage *message) { // Make sure to process any incoming messages before sending (could this end up in recursive loop?) // process(); // If we still don't have any node id, re-request and skip this message. - if (nc.nodeId == AUTO && !(mGetCommand(message) == C_INTERNAL && message.type == I_REQUEST_ID)) { + if (nc.nodeId == AUTO && !(mGetCommandP(message) == C_INTERNAL && message->type == I_REQUEST_ID)) { requestNodeId(); return false; } if (relayMode) { - uint8_t dest = message.destination; + uint8_t dest = message->destination; uint8_t route = getChildRoute(dest); if (route>0 && route<255 && dest != GATEWAY_ADDRESS) { debug(PSTR("route %d.\n"), route); @@ -193,61 +185,61 @@ boolean MySensor::sendRoute(MyMessage message) { return false; } -boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) { - uint8_t length = mGetLength(message); - message.last = nc.nodeId; - mSetVersion(message, PROTOCOL_VERSION); - message.crc = crc8Message(message); +boolean MySensor::sendWrite(uint8_t next, MyMessage *message, bool broadcast) { + uint8_t length = mGetLengthP(message); + message->last = nc.nodeId; + mSetVersionP(message, PROTOCOL_VERSION); + message->crc = crc8Message(message); // Make sure radio has powered up RF24::powerUp(); RF24::stopListening(); RF24::openWritingPipe(TO_ADDR(next)); - bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); + bool ok = RF24::write(message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); RF24::startListening(); // Only debug print payload if it is of string type debug(PSTR("tx: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), - message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, ok?"ok":"fail", message.getString()); + message->sender,message->last, next, message->destination, message->sensor, mGetCommandP(message), message->type, ok?"ok":"fail", message->getString()); return ok; } -bool MySensor::send(MyMessage message, bool enableAck) { - message.sender = nc.nodeId; +bool MySensor::send(MyMessage *message, bool enableAck) { + message->sender = nc.nodeId; if (enableAck) { - mSetCommand(message,C_SET_WITH_ACK); + mSetCommandP(message,C_SET_WITH_ACK); } else { - mSetCommand(message,C_SET); + mSetCommandP(message,C_SET); } return sendRoute(message); } void MySensor::sendBatteryLevel(uint8_t value) { - buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, value); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL)->set(value)); } void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { - buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, LIBRARY_VERSION); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType)->set(LIBRARY_VERSION)); } void MySensor::sendSketchInfo(const char *name, const char *version) { if (name != NULL) { - buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, name); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME)->set(name)); } if (version != NULL) { - buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, version); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION)->set(version)); } } void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - buildSend(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType); + sendRoute(build(&msg, nc.nodeId, destination, childSensorId, C_REQ, variableType)); } void MySensor::requestTime(void (* _timeCallback)(unsigned long)) { timeCallback = _timeCallback; - buildSend(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME)); } @@ -262,7 +254,7 @@ boolean MySensor::process() { RF24::read(&msg, len); RF24::writeAckPayload(pipe,&pipe, 1 ); - uint8_t valid = validate(msg); + uint8_t valid = validate(&msg); boolean ok = valid == VALIDATE_OK; // Make sure string gets terminated ok for full string messages. @@ -284,9 +276,7 @@ boolean MySensor::process() { // Wait a random delay of 0-2 seconds to minimize collision // between ping ack messages from other relaying nodes delay(millis() & 0x3ff); - build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK); - msg.set(nc.distance); - sendWrite(sender, msg); + sendWrite(sender, build(&msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK)->set(nc.distance)); return false; } else if (destination == nc.nodeId) { // This message is addressed to this node @@ -344,7 +334,7 @@ boolean MySensor::process() { for (uint8_t i=0;i< sizeof(childNodeTable); i++) { removeChildRoute(i); } - buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,""); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN)->set("")); } break; case I_TIME: @@ -358,11 +348,12 @@ boolean MySensor::process() { } // Check if sender requests an ack back. if (command == C_SET_WITH_ACK) { + // Copy message ack = msg; mSetCommand(ack,C_SET); ack.sender = nc.nodeId; ack.destination = msg.sender; - sendRoute(ack); + sendRoute(&ack); // The library user should not need to care about this ack request. Just treat it as a normal SET. mSetCommand(msg,C_SET); } @@ -388,7 +379,7 @@ boolean MySensor::process() { // We're node C, Message comes from A and has destination D // // lookup route in table and send message there - sendWrite(route, msg); + sendWrite(route, &msg); } else if (pipe == CURRENT_NODE_PIPE) { // A message comes from a child node and we have no // route for it. @@ -402,7 +393,7 @@ boolean MySensor::process() { // Message should be passed to node A (this nodes relay) // This message should be routed back towards sensor net gateway - sendWrite(nc.parentNodeId, msg); + sendWrite(nc.parentNodeId, &msg); // Add this child to our "routing table" if it not already exist addChildRoute(sender, last); } else { @@ -441,35 +432,35 @@ boolean MySensor::process() { } -MyMessage MySensor::getLastMessage() { - return msg; +MyMessage* MySensor::getLastMessage() { + return &msg; } /* * calculate CRC8 on MyMessage data taking care of data structure and protocol version */ -uint8_t MySensor::crc8Message(MyMessage message) { - uint8_t len = mGetLength(message); +uint8_t MySensor::crc8Message(MyMessage *message) { + uint8_t len = mGetLengthP(message); uint8_t crc = 0x00; uint8_t loop_count; uint8_t bit_counter; uint8_t data; uint8_t feedback_bit; - uint8_t number_of_bytes_to_read = (uint8_t)sizeof(message); + uint8_t number_of_bytes_to_read = (uint8_t)sizeof(MyMessage); // Must set crc to a constant value. - message.crc = 0; + message->crc = 0; // fill unused space by zeroes for string data only - if(len>=0 && len < sizeof(message.data)-1) { - memset(&message.data[len], 0, sizeof(message.data) - 1 - len); + if(len>=0 && len < sizeof(message->data)-1) { + memset(&message->data[len], 0, sizeof(message->data) - 1 - len); } for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) { - data = ((uint8_t*)&message)[loop_count]; + data = ((uint8_t*)message)[loop_count]; bit_counter = 8; do { @@ -492,11 +483,11 @@ uint8_t MySensor::crc8Message(MyMessage message) { } -uint8_t MySensor::validate(MyMessage message) { - uint8_t oldCrc = message.crc; +uint8_t MySensor::validate(MyMessage *message) { + uint8_t oldCrc = message->crc; uint8_t newCrc = crc8Message(message); - if(!(mGetVersion(message) == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; + if(!(mGetVersionP(message) == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; return VALIDATE_OK; diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index c1d43df..d7ed065 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -130,7 +130,7 @@ class MySensor : public RF24 * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. * @return true Returns true if message reached the first stop on its way to destination. */ - bool send(MyMessage msg, bool ack=false); + bool send(MyMessage *msg, bool ack=false); /** * Send this nodes battery level to gateway. @@ -191,7 +191,7 @@ class MySensor : public RF24 /** * Returns the last received message */ - MyMessage getLastMessage(void); + MyMessage* getLastMessage(void); @@ -209,9 +209,9 @@ class MySensor : public RF24 void setupRelayMode(); void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); - boolean sendRoute(MyMessage message); - boolean sendWrite(uint8_t dest, MyMessage message, bool broadcast=false); - uint8_t validate(MyMessage message); + boolean sendRoute(MyMessage *message); + boolean sendWrite(uint8_t dest, MyMessage *message, bool broadcast=false); + uint8_t validate(MyMessage *message); private: uint8_t failedTransmissions; @@ -221,7 +221,7 @@ class MySensor : public RF24 void requestNodeId(); void findParentNode(); - uint8_t crc8Message(MyMessage); + uint8_t crc8Message(MyMessage *message); uint8_t getChildRoute(uint8_t childId); void addChildRoute(uint8_t childId, uint8_t route); void removeChildRoute(uint8_t childId); diff --git a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino index fec654b..681d2de 100644 --- a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino +++ b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino @@ -61,7 +61,7 @@ void loop() if (lastTemperature[i] != temperature && temperature != -127.00) { // Send in the new temperature - gw.send(out.setSensor(i).set(temperature,1)); + gw.send(out.setSensor(i)->set(temperature,1)); lastTemperature[i]=temperature; } } diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index f9800fb..e57ad78 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -21,7 +21,7 @@ void setup() gw.sendSketchInfo("Relay", "1.0"); // Fetch relay status - for (int sensor=0, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) { + for (int sensor=1, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) { // Register all sensors to gw (they will be created as child devices) gw.present(sensor, S_LIGHT); // Then set relay pins in output mode From 67f4ca1fb4149cfebeaf4d73c7527d4a8d8a51d9 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 19 May 2014 20:08:12 +0200 Subject: [PATCH 251/326] latest test sketches and working request for id --- EthernetGateway/EthernetGateway.ino | 1 - libraries/MySensors/MyMessage.cpp | 5 + libraries/MySensors/MyMessage.h | 3 +- libraries/MySensors/MySensor.cpp | 174 +++++++++--------- libraries/MySensors/MySensor.h | 20 +- .../DallasTemperatureSensor.ino | 8 +- .../examples/MotionSensor/MotionSensor.ino | 21 ++- .../examples/RelayActuator/RelayActuator.ino | 5 +- libraries/RF24/RF24.cpp | 4 +- 9 files changed, 129 insertions(+), 112 deletions(-) diff --git a/EthernetGateway/EthernetGateway.ino b/EthernetGateway/EthernetGateway.ino index 7a4b363..f6c2481 100644 --- a/EthernetGateway/EthernetGateway.ino +++ b/EthernetGateway/EthernetGateway.ino @@ -54,7 +54,6 @@ #include #include #include -//#include #include // Use this if you have attached a Ethernet ENC28J60 shields //#include // Use this fo WizNET module and Arduino Ethernet Shield diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 941dc06..7d34eb2 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -7,6 +7,11 @@ MyMessage::MyMessage() { destination = 0; // Gateway is default destination } +MyMessage::MyMessage(uint8_t _sensor, uint8_t _type) { + destination = 0; // Gateway is default destination + sensor = _sensor; + type = _type; +} /* Getters for payload converted to desired form */ diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 0039b0d..ab6da6b 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -40,7 +40,7 @@ typedef enum { // Type of internal messages (for internal messages) typedef enum { - I_BATTERY_LEVEL, I_TIME, I_VERSION, I_REQUEST_ID, + I_BATTERY_LEVEL, I_TIME, I_VERSION, I_ID_REQUEST, I_ID_RESPONSE, I_INCLUSION_MODE, I_CONFIG, I_PING, I_PING_ACK, I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION } internal; @@ -114,6 +114,7 @@ public: // Constructors MyMessage(); + MyMessage(uint8_t sensor, uint8_t type); char* getString(); /** diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 6921fcd..5174777 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -25,37 +25,8 @@ inline MyMessage* build (MyMessage* msg, uint8_t sender, uint8_t destination, ui MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } -void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - failedTransmissions = 0; - // Start up the radio library - RF24::begin(); - - if (!RF24::isPVariant()) { - debug(PSTR("check wires\n")); - while(1); - } - RF24::setAutoAck(1); - RF24::enableAckPayload(); - RF24::setChannel(channel); - RF24::setPALevel(paLevel); - RF24::setDataRate(dataRate); - RF24::setRetries(5,15); - RF24::setCRCLength(RF24_CRC_16); - RF24::enableDynamicPayloads(); - - // All repeater nodes and gateway listen to broadcast pipe (for PING messages) - if (relayMode) { - RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); - } -} - -void MySensor::setupRelayMode(){ - childNodeTable = new uint8_t[256]; - eeprom_read_block((void*)childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); -} - -void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_t _nodeId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { +void MySensor::begin(void (*_msgCallback)(MyMessage), uint8_t _nodeId, boolean _relayMode, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { Serial.begin(BAUD_RATE); relayMode = _relayMode; msgCallback = _msgCallback; @@ -86,7 +57,7 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_ requestNodeId(); } - debug(PSTR("%s started\n"), relayMode?"relay":"sensor"); + debug(PSTR("%s started, id %d\n"), relayMode?"relay":"sensor", nc.nodeId); // Open reading pipe for messages directed to this node (set write pipe to same) RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId)); @@ -99,8 +70,38 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_ // Node sends parent node. Controller answers with latest node configuration // which is picked up in process() sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG)->set(nc.parentNodeId)); - Serial.print("XX"); - Serial.println(nc.parentNodeId); + + // Wait configuration reply. + waitForReply(); +} + +void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { + failedTransmissions = 0; + + // Start up the radio library + RF24::begin(); + + if (!RF24::isPVariant()) { + debug(PSTR("check wires\n")); + while(1); + } + RF24::setAutoAck(1); + RF24::setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast + RF24::enableAckPayload(); + RF24::setChannel(channel); + RF24::setPALevel(paLevel); + RF24::setDataRate(dataRate); + RF24::setRetries(5,15); + RF24::setCRCLength(RF24_CRC_16); + RF24::enableDynamicPayloads(); + + // All nodes listen to broadcast pipe (for PING-ACK messages) + RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); +} + +void MySensor::setupRelayMode(){ + childNodeTable = new uint8_t[256]; + eeprom_read_block((void*)childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); } @@ -115,7 +116,8 @@ ControllerConfig MySensor::getConfig() { void MySensor::requestNodeId() { debug(PSTR("req node id\n")); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_ID)); + sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST)->set("")); + waitForReply(); } @@ -125,31 +127,33 @@ void MySensor::findParentNode() { failedTransmissions = 0; - // Open a reading pipe for current nodeId (if it differs from broadcast) - if (nc.nodeId != BROADCAST_PIPE) { - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); - } // Set distance to max nc.distance = 255; // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - build(&msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING); + build(&msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING)->set(""); sendWrite(BROADCAST_ADDRESS, &msg, true); + // Wait for ping response. + waitForReply(); +} + +void MySensor::waitForReply() { unsigned long enter = millis(); - // Wait for ack responses slightly more than 1 second - while (millis() - enter < 1200) { + // Wait a couple of seconds for response + while (millis() - enter < 2000) { process(); } - } boolean MySensor::sendRoute(MyMessage *message) { // Make sure to process any incoming messages before sending (could this end up in recursive loop?) // process(); + bool requestId = (mGetCommandP(message) == C_INTERNAL && message->type == I_ID_REQUEST); + bool responseId = (mGetCommandP(message) == C_INTERNAL && message->type == I_ID_RESPONSE); // If we still don't have any node id, re-request and skip this message. - if (nc.nodeId == AUTO && !(mGetCommandP(message) == C_INTERNAL && message->type == I_REQUEST_ID)) { + if (nc.nodeId == AUTO && !requestId) { requestNodeId(); return false; } @@ -157,16 +161,20 @@ boolean MySensor::sendRoute(MyMessage *message) { if (relayMode) { uint8_t dest = message->destination; uint8_t route = getChildRoute(dest); - if (route>0 && route<255 && dest != GATEWAY_ADDRESS) { - debug(PSTR("route %d.\n"), route); + if (route>GATEWAY_ADDRESS && routelast = nc.nodeId; mSetVersionP(message, PROTOCOL_VERSION); message->crc = crc8Message(message); - // Make sure radio has powered up RF24::powerUp(); RF24::stopListening(); @@ -199,8 +206,8 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage *message, bool broadcast) { RF24::startListening(); // Only debug print payload if it is of string type - debug(PSTR("tx: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), - message->sender,message->last, next, message->destination, message->sensor, mGetCommandP(message), message->type, ok?"ok":"fail", message->getString()); + debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), + message->sender,message->last, next, message->destination, message->sensor, mGetCommandP(message), message->type, ok?"ok":"fail", message->getString(convBuf)); return ok; } @@ -259,8 +266,8 @@ boolean MySensor::process() { // Make sure string gets terminated ok for full string messages. msg.data[len - HEADER_SIZE ] = '\0'; - debug(PSTR("rx: %d-%d-%d s=%d,c=%d,t=%d,cr=%s: %s\n"), - msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, valid==0?"ok":valid==1?"ec":"ev", msg.getString()); + debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,cr=%s: %s\n"), + msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, valid==0?"ok":valid==1?"ec":"ev", msg.getString(convBuf)); if (!ok) return false; @@ -276,7 +283,7 @@ boolean MySensor::process() { // Wait a random delay of 0-2 seconds to minimize collision // between ping ack messages from other relaying nodes delay(millis() & 0x3ff); - sendWrite(sender, build(&msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK)->set(nc.distance)); + sendWrite(sender, build(&msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK)->set(nc.distance), true); return false; } else if (destination == nc.nodeId) { // This message is addressed to this node @@ -286,8 +293,9 @@ boolean MySensor::process() { } if (command == C_INTERNAL) { - if (type == I_PING_ACK) { - // We've received a reply to our PING message looking for parent. + if (type == I_PING_ACK && nc.nodeId != GATEWAY_ADDRESS) { + // We've received a reply to a PING message. Check if the distance is + // shorter than we already have. uint8_t distance = msg.getByte(); if (distanceset(temperature,1)); + gw.send(msg.setSensor(i)->set(temperature,1)); lastTemperature[i]=temperature; } } diff --git a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino index 194aaa4..3bb8777 100644 --- a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino +++ b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino @@ -1,20 +1,22 @@ +#include #include #include -#include #include -#include #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your motion sensor. (Only 2 and 3 generates interrupt!) #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) -#define CHILD_ID 0 // Id of the sensor child +#define CHILD_ID 1 // Id of the sensor child -Sensor gw; +MySensor gw; Sleep sleep; +// Initialize motion message +MyMessage msg(CHILD_ID, V_TRIPPED); void setup() { - EEPROM.write(EEPROM_RELAY_ID_ADDRESS, 0); - + //eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, 1); + //eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, 255); + //delay(2000); gw.begin(); // Send the sketch version information to the gateway and Controller @@ -22,7 +24,10 @@ void setup() pinMode(DIGITAL_INPUT_SENSOR, INPUT); // sets the motion sensor digital pin as input // Register all sensors to gw (they will be created as child devices) - gw.sendSensorPresentation(CHILD_ID, S_MOTION); + delay(500); + gw.present(CHILD_ID, S_MOTION); + delay(1000); + } void loop() @@ -31,7 +36,7 @@ void loop() boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH; Serial.println(tripped); - gw.sendVariable(CHILD_ID, V_TRIPPED, tripped?"1":"0"); // Send tripped value to gw + gw.send(msg.set(tripped?"1":"0")); // Send tripped value to gw // Power down the radio. Note that the radio will get powered back up // on the next write() call. diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index e57ad78..f04b602 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -16,7 +16,7 @@ MySensor gw; void setup() { // Initialize library and add callback for incoming messages - gw.begin(incomingMessage, false); + gw.begin(incomingMessage, AUTO, true); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Relay", "1.0"); @@ -42,10 +42,9 @@ void incomingMessage(MyMessage message) { // We only expect one type of message from controller. But we better check anyway. if (message.type==V_LIGHT) { // Change relay state - digitalWrite(message.sensor, message.getBool()?RELAY_ON:RELAY_OFF); + digitalWrite(message.sensor-1+RELAY_1, message.getBool()?RELAY_ON:RELAY_OFF); // Store state in eeprom gw.saveState(message.sensor, message.getBool()); - // Write some debug info Serial.print("Incoming change for sensor:"); Serial.print(message.sensor); diff --git a/libraries/RF24/RF24.cpp b/libraries/RF24/RF24.cpp index 000cfed..2efb7bb 100644 --- a/libraries/RF24/RF24.cpp +++ b/libraries/RF24/RF24.cpp @@ -680,10 +680,10 @@ void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){ // Send the payload //write_payload( buf, len ); - write_payload( buf, len,multicast? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + write_payload( buf, len, multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD)) ; ce(HIGH); - ce(LOW); + ce(LOW); } From 6201d3cd54e1e8e14054b9b30c52a9eea2539154 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 19 May 2014 23:33:36 +0200 Subject: [PATCH 252/326] IsGateway added --- libraries/MySensors/MyGateway.cpp | 1 + libraries/MySensors/MySensor.cpp | 20 ++++++++------------ libraries/MySensors/MySensor.h | 3 ++- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index f2d0cd1..25800f5 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -29,6 +29,7 @@ MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, ui void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) { Serial.begin(BAUD_RATE); relayMode = true; + isGateway = true; setupRelayMode(); if (inDataCallback != NULL) { diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 5174777..75bb89d 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -28,6 +28,7 @@ MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { void MySensor::begin(void (*_msgCallback)(MyMessage), uint8_t _nodeId, boolean _relayMode, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { Serial.begin(BAUD_RATE); + isGateway = false; relayMode = _relayMode; msgCallback = _msgCallback; @@ -122,9 +123,6 @@ void MySensor::requestNodeId() { void MySensor::findParentNode() { - if (nc.nodeId == GATEWAY_ADDRESS) - return; // Gateway has no business here! - failedTransmissions = 0; // Set distance to max @@ -149,11 +147,10 @@ void MySensor::waitForReply() { boolean MySensor::sendRoute(MyMessage *message) { // Make sure to process any incoming messages before sending (could this end up in recursive loop?) // process(); - bool requestId = (mGetCommandP(message) == C_INTERNAL && message->type == I_ID_REQUEST); - bool responseId = (mGetCommandP(message) == C_INTERNAL && message->type == I_ID_RESPONSE); + bool isInternal = mGetCommandP(message) == C_INTERNAL; // If we still don't have any node id, re-request and skip this message. - if (nc.nodeId == AUTO && !requestId) { + if (nc.nodeId == AUTO && !(isInternal && message->type == I_ID_REQUEST)) { requestNodeId(); return false; } @@ -166,14 +163,14 @@ boolean MySensor::sendRoute(MyMessage *message) { // Message destination is not gateway and is in routing table for this node. // Send it downstream return sendWrite(route, message); - } else if (responseId && dest==BROADCAST_ADDRESS) { + } else if (isInternal && message->type == I_ID_RESPONSE && dest==BROADCAST_ADDRESS) { // Node has not yet received any id. We need to send it // by doing a broadcast sending, return sendWrite(BROADCAST_ADDRESS, message, true); } } - if (nc.nodeId != GATEWAY_ADDRESS) { + if (!isGateway) { // --- debug(PSTR("route parent\n")); // Should be routed back to gateway. bool ok = sendWrite(nc.parentNodeId, message); @@ -205,7 +202,6 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage *message, bool broadcast) { bool ok = RF24::write(message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); RF24::startListening(); - // Only debug print payload if it is of string type debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), message->sender,message->last, next, message->destination, message->sensor, mGetCommandP(message), message->type, ok?"ok":"fail", message->getString(convBuf)); @@ -293,7 +289,7 @@ boolean MySensor::process() { } if (command == C_INTERNAL) { - if (type == I_PING_ACK && nc.nodeId != GATEWAY_ADDRESS) { + if (type == I_PING_ACK && !isGateway) { // We've received a reply to a PING message. Check if the distance is // shorter than we already have. uint8_t distance = msg.getByte(); @@ -521,7 +517,7 @@ long MySensor::getInternalTemp(void) #ifdef DEBUG void MySensor::debugPrint(const char *fmt, ... ) { char fmtBuffer[300]; - if (nc.nodeId == GATEWAY_ADDRESS) { + if (isGateway) { // prepend debug message to be handled correctly by gw (C_INTERNAL, I_LOG_MESSAGE) Serial.write("0;0;4;8;"); } @@ -529,7 +525,7 @@ void MySensor::debugPrint(const char *fmt, ... ) { va_start (args, fmt ); vsnprintf_P(fmtBuffer, 300, fmt, args); va_end (args); - if (nc.nodeId == GATEWAY_ADDRESS) { + if (isGateway) { // Truncate message if this is gateway node vsnprintf_P(fmtBuffer, 60, fmt, args); fmtBuffer[59] = '\n'; diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 26a8004..94bdd84 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -213,7 +213,8 @@ class MySensor : public RF24 protected: NodeConfig nc; // Essential settings for node to work ControllerConfig cc; // Configuration coming from controller - boolean relayMode; + bool relayMode; + bool isGateway; MyMessage msg; // Buffer for incoming messages. MyMessage ack; // Buffer for ack messages. From c81a47c1f90cbd2f3fbaa2f1c8a9aaa3c0e6a272 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 28 May 2014 22:27:36 +0200 Subject: [PATCH 253/326] Fixed a couple of issues reported by a-lurker --- EthernetGateway/EthernetGateway.ino | 2 +- libraries/MySensors/MyMessage.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/EthernetGateway/EthernetGateway.ino b/EthernetGateway/EthernetGateway.ino index f6c2481..9059a07 100644 --- a/EthernetGateway/EthernetGateway.ino +++ b/EthernetGateway/EthernetGateway.ino @@ -83,7 +83,7 @@ EthernetServer server = EthernetServer(IP_PORT); MyGateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME); // Uncomment this constructor if you have leds and include button attached to your gateway -//Gateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); +//MyGateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 7d34eb2..999521c 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -44,13 +44,13 @@ char* MyMessage::getString(char *buffer) { itoa(iValue, buffer, MAX_PAYLOAD); break; case P_UINT16: - ltoa(uiValue, buffer, MAX_PAYLOAD); + utoa(uiValue, buffer, MAX_PAYLOAD); break; case P_LONG32: ltoa(lValue, buffer, MAX_PAYLOAD); break; case P_ULONG32: - ltoa(ulValue, buffer, MAX_PAYLOAD); + ultoa(ulValue, buffer, MAX_PAYLOAD); break; case P_CUSTOM: // TODO: Ok, what do we do here? We should probably convert this to hex @@ -198,14 +198,14 @@ MyMessage* MyMessage::set(long value) { } MyMessage* MyMessage::set(unsigned int value) { - miSetPayloadType(P_INT16); + miSetPayloadType(P_UINT16); miSetLength(2); uiValue = value; return this; } MyMessage* MyMessage::set(int value) { - miSetPayloadType(P_UINT16); + miSetPayloadType(P_INT16); miSetLength(2); iValue = value; return this; From d1ca16fe1b84b39e163ec856c6d06780420f5dbe Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 29 May 2014 00:30:30 +0200 Subject: [PATCH 254/326] correct radix fixed --- libraries/MySensors/MyMessage.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 999521c..d11c703 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -38,19 +38,19 @@ char* MyMessage::getString(char *buffer) { } else if (buffer != NULL) { switch(payloadType) { case P_BYTE: - itoa(bValue, buffer, MAX_PAYLOAD); + itoa(bValue, buffer, 10); break; case P_INT16: - itoa(iValue, buffer, MAX_PAYLOAD); + itoa(iValue, buffer, 10); break; case P_UINT16: - utoa(uiValue, buffer, MAX_PAYLOAD); + utoa(uiValue, buffer, 10); break; case P_LONG32: - ltoa(lValue, buffer, MAX_PAYLOAD); + ltoa(lValue, buffer, 10); break; case P_ULONG32: - ultoa(ulValue, buffer, MAX_PAYLOAD); + ultoa(ulValue, buffer, 10); break; case P_CUSTOM: // TODO: Ok, what do we do here? We should probably convert this to hex From af90d238d4cfbbe0c611a88dd96c05a47079e45e Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 13 Jun 2014 13:54:12 +0200 Subject: [PATCH 255/326] Start of example conversion. Added sleep to MySensors API. --- .../idleWakePeriodic/idleWakePeriodic.ino | 31 + .../powerDownWakeExternalInterrupt.ino | 33 + .../powerDownWakePeriodic.ino | 16 + libraries/LowPower/LowPower.cpp | 856 ++++++++++++++++++ libraries/LowPower/LowPower.h | 137 +++ libraries/LowPower/keywords.txt | 68 ++ libraries/MySensors/LowPower.cpp | 856 ++++++++++++++++++ libraries/MySensors/LowPower.h | 137 +++ libraries/MySensors/MySensor.cpp | 45 +- libraries/MySensors/MySensor.h | 21 +- .../BatteryPoweredSensor.ino | 18 +- .../BinarySwitchSensor/BinarySwitchSensor.ino | 11 +- .../DallasTemperatureSensor.ino | 8 +- .../DistanceSensor/DistanceSensor.ino | 23 +- .../EnergyMeterPulseSensor.ino | 60 +- .../HumiditySensor/HumiditySensor.ino | 32 +- .../MySensors/examples/IrSensor/IrSensor.ino | 42 +- .../LightLuxSensor/LightLuxSensor.ino | 23 +- .../examples/LightSensor/LightSensor.ino | 20 +- .../examples/MotionSensor/MotionSensor.ino | 15 +- .../PressureSensor/PressureSensor.ino | 35 +- .../examples/RelayActuator/RelayActuator.ino | 1 + .../RelayWithButtonActuator.ino | 91 +- .../examples/RelayingNode/RelayingNode.ino | 15 +- .../examples/ServoActuator/ServoActuator.ino | 7 +- 25 files changed, 2377 insertions(+), 224 deletions(-) create mode 100644 libraries/LowPower/Examples/idleWakePeriodic/idleWakePeriodic.ino create mode 100644 libraries/LowPower/Examples/powerDownWakeExternalInterrupt/powerDownWakeExternalInterrupt.ino create mode 100644 libraries/LowPower/Examples/powerDownWakePeriodic/powerDownWakePeriodic.ino create mode 100644 libraries/LowPower/LowPower.cpp create mode 100644 libraries/LowPower/LowPower.h create mode 100644 libraries/LowPower/keywords.txt create mode 100644 libraries/MySensors/LowPower.cpp create mode 100644 libraries/MySensors/LowPower.h diff --git a/libraries/LowPower/Examples/idleWakePeriodic/idleWakePeriodic.ino b/libraries/LowPower/Examples/idleWakePeriodic/idleWakePeriodic.ino new file mode 100644 index 0000000..e93072d --- /dev/null +++ b/libraries/LowPower/Examples/idleWakePeriodic/idleWakePeriodic.ino @@ -0,0 +1,31 @@ +// **** INCLUDES ***** +#include "LowPower.h" + +void setup() +{ + // No setup is required for this library +} + +void loop() +{ + // Enter idle state for 8 s with the rest of peripherals turned off + // Each microcontroller comes with different number of peripherals + // Comment off line of code where necessary + + // ATmega328P, ATmega168 + LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, + SPI_OFF, USART0_OFF, TWI_OFF); + + // ATmega32U4 + //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF, + // TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF); + + // ATmega2560 + //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER5_OFF, TIMER4_OFF, TIMER3_OFF, + // TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART3_OFF, + // USART2_OFF, USART1_OFF, USART0_OFF, TWI_OFF); + + // Do something here + // Example: Read sensor, data logging, data transmission. +} + diff --git a/libraries/LowPower/Examples/powerDownWakeExternalInterrupt/powerDownWakeExternalInterrupt.ino b/libraries/LowPower/Examples/powerDownWakeExternalInterrupt/powerDownWakeExternalInterrupt.ino new file mode 100644 index 0000000..f914bd6 --- /dev/null +++ b/libraries/LowPower/Examples/powerDownWakeExternalInterrupt/powerDownWakeExternalInterrupt.ino @@ -0,0 +1,33 @@ +// **** INCLUDES ***** +#include "LowPower.h" + +// Use pin 2 as wake up pin +const int wakeUpPin = 2; + +void wakeUp() +{ + // Just a handler for the pin interrupt. +} + +void setup() +{ + // Configure wake up pin as input. + // This will consumes few uA of current. + pinMode(wakeUpPin, INPUT); +} + +void loop() +{ + // Allow wake up pin to trigger interrupt on low. + attachInterrupt(0, wakeUp, LOW); + + // Enter power down state with ADC and BOD module disabled. + // Wake up when wake up pin is low. + LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); + + // Disable external pin interrupt on wake up pin. + detachInterrupt(0); + + // Do something here + // Example: Read sensor, data logging, data transmission. +} diff --git a/libraries/LowPower/Examples/powerDownWakePeriodic/powerDownWakePeriodic.ino b/libraries/LowPower/Examples/powerDownWakePeriodic/powerDownWakePeriodic.ino new file mode 100644 index 0000000..bce822a --- /dev/null +++ b/libraries/LowPower/Examples/powerDownWakePeriodic/powerDownWakePeriodic.ino @@ -0,0 +1,16 @@ +// **** INCLUDES ***** +#include "LowPower.h" + +void setup() +{ + // No setup is required for this library +} + +void loop() +{ + // Enter power down state for 8 s with ADC and BOD module disabled + LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); + + // Do something here + // Example: Read sensor, data logging, data transmission. +} diff --git a/libraries/LowPower/LowPower.cpp b/libraries/LowPower/LowPower.cpp new file mode 100644 index 0000000..a72cc31 --- /dev/null +++ b/libraries/LowPower/LowPower.cpp @@ -0,0 +1,856 @@ +/******************************************************************************* +* LowPower Library +* Version: 1.30 +* Date: 22-05-2013 +* Company: Rocket Scream Electronics +* Website: www.rocketscream.com +* +* This is a lightweight low power library for Arduino. Please check our wiki +* (www.rocketscream.com/wiki) for more information on using this piece of +* library. +* +* This library is licensed under Creative Commons Attribution-ShareAlike 3.0 +* Unported License. +* +* Revision Description +* ======== =========== +* 1.30 Added support for ATMega168, ATMega2560, ATMega1280 & ATMega32U4. +* Tested to work with Arduino IDE 1.0.1 - 1.0.4. +* 1.20 Remove typo error in idle method for checking whether Timer 0 was +* turned off. +* Remove dependecy on WProgram.h which is not required. +* Tested to work with Arduino IDE 1.0. +* 1.10 Added #ifndef for sleep_bod_disable() for compatibility with future +* Arduino IDE release. +* 1.00 Initial public release. +*******************************************************************************/ +#include +#include +#include +#include +#include "LowPower.h" + +// Only Pico Power devices can change BOD settings through software +#if defined __AVR_ATmega328P__ +#ifndef sleep_bod_disable +#define sleep_bod_disable() \ +do { \ + unsigned char tempreg; \ + __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \ + "ori %[tempreg], %[bods_bodse]" "\n\t" \ + "out %[mcucr], %[tempreg]" "\n\t" \ + "andi %[tempreg], %[not_bodse]" "\n\t" \ + "out %[mcucr], %[tempreg]" \ + : [tempreg] "=&d" (tempreg) \ + : [mcucr] "I" _SFR_IO_ADDR(MCUCR), \ + [bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \ + [not_bodse] "i" (~_BV(BODSE))); \ +} while (0) +#endif +#endif + +#define lowPowerBodOn(mode) \ +do { \ + set_sleep_mode(mode); \ + cli(); \ + sleep_enable(); \ + sei(); \ + sleep_cpu(); \ + sleep_disable(); \ + sei(); \ +} while (0); + +// Only Pico Power devices can change BOD settings through software +#if defined __AVR_ATmega328P__ +#define lowPowerBodOff(mode)\ +do { \ + set_sleep_mode(mode); \ + cli(); \ + sleep_enable(); \ + sleep_bod_disable(); \ + sei(); \ + sleep_cpu(); \ + sleep_disable(); \ + sei(); \ +} while (0); +#endif + +// Some macros is still missing from AVR GCC distribution for ATmega32U4 +#if defined __AVR_ATmega32U4__ + // Timer 4 PRR bit is currently not defined in iom32u4.h + #ifndef PRTIM4 + #define PRTIM4 4 + #endif + + // Timer 4 power reduction macro is not defined currently in power.h + #ifndef power_timer4_disable + #define power_timer4_disable() (PRR1 |= (uint8_t)(1 << PRTIM4)) + #endif + + #ifndef power_timer4_enable + #define power_timer4_enable() (PRR1 &= (uint8_t)~(1 << PRTIM4)) + #endif +#endif + +/******************************************************************************* +* Name: idle +* Description: Putting ATmega328P/168 into idle state. Please make sure you +* understand the implication and result of disabling module. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control: +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +* 4. timer1 Timer 1 module disable control: +* (a) TIMER1_OFF - Turn off Timer 1 module +* (b) TIMER1_ON - Leave Timer 1 module in its default state +* +* 5. timer0 Timer 0 module disable control: +* (a) TIMER0_OFF - Turn off Timer 0 module +* (b) TIMER0_ON - Leave Timer 0 module in its default state +* +* 6. spi SPI module disable control: +* (a) SPI_OFF - Turn off SPI module +* (b) SPI_ON - Leave SPI module in its default state +* +* 7. usart0 USART0 module disable control: +* (a) USART0_OFF - Turn off USART0 module +* (b) USART0_ON - Leave USART0 module in its default state +* +* 8. twi TWI module disable control: +* (a) TWI_OFF - Turn off TWI module +* (b) TWI_ON - Leave TWI module in its default state +* +*******************************************************************************/ +#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__) +void LowPowerClass::idle(period_t period, adc_t adc, timer2_t timer2, + timer1_t timer1, timer0_t timer0, + spi_t spi, usart0_t usart0, twi_t twi) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + + power_timer2_disable(); + } + + if (adc == ADC_OFF) + { + ADCSRA &= ~(1 << ADEN); + power_adc_disable(); + } + + if (timer1 == TIMER1_OFF) power_timer1_disable(); + if (timer0 == TIMER0_OFF) power_timer0_disable(); + if (spi == SPI_OFF) power_spi_disable(); + if (usart0 == USART0_OFF) power_usart0_disable(); + if (twi == TWI_OFF) power_twi_disable(); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_IDLE); + + if (adc == ADC_OFF) + { + power_adc_enable(); + ADCSRA |= (1 << ADEN); + } + + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + + power_timer2_enable(); + } + + if (timer1 == TIMER1_OFF) power_timer1_enable(); + if (timer0 == TIMER0_OFF) power_timer0_enable(); + if (spi == SPI_OFF) power_spi_enable(); + if (usart0 == USART0_OFF) power_usart0_enable(); + if (twi == TWI_OFF) power_twi_enable(); +} +#endif + +/******************************************************************************* +* Name: idle +* Description: Putting ATmega32U4 into idle state. Please make sure you +* understand the implication and result of disabling module. +* Take note that Timer 2 is not available and USART0 +* is replaced with USART1 on ATmega32U4. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control: +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer4 Timer 4 module disable control: +* (a) TIMER4_OFF - Turn off Timer 4 module +* (b) TIMER4_ON - Leave Timer 4 module in its default state +* +* 4. timer3 Timer 3 module disable control: +* (a) TIMER3_OFF - Turn off Timer 3 module +* (b) TIMER3_ON - Leave Timer 3 module in its default state +* +* 5. timer1 Timer 1 module disable control: +* (a) TIMER1_OFF - Turn off Timer 1 module +* (b) TIMER1_ON - Leave Timer 1 module in its default state +* +* 6. timer0 Timer 0 module disable control: +* (a) TIMER0_OFF - Turn off Timer 0 module +* (b) TIMER0_ON - Leave Timer 0 module in its default state +* +* 7. spi SPI module disable control: +* (a) SPI_OFF - Turn off SPI module +* (b) SPI_ON - Leave SPI module in its default state +* +* 8. usart1 USART1 module disable control: +* (a) USART1_OFF - Turn off USART1 module +* (b) USART1_ON - Leave USART1 module in its default state +* +* 9. twi TWI module disable control: +* (a) TWI_OFF - Turn off TWI module +* (b) TWI_ON - Leave TWI module in its default state +* +* 10.usb USB module disable control: +* (a) USB_OFF - Turn off USB module +* (b) USB_ON - Leave USB module in its default state +*******************************************************************************/ +#if defined __AVR_ATmega32U4__ +void LowPowerClass::idle(period_t period, adc_t adc, + timer4_t timer4, timer3_t timer3, + timer1_t timer1, timer0_t timer0, + spi_t spi, usart1_t usart1, twi_t twi, usb_t usb) +{ + if (adc == ADC_OFF) + { + ADCSRA &= ~(1 << ADEN); + power_adc_disable(); + } + + if (timer4 == TIMER4_OFF) power_timer4_disable(); + if (timer3 == TIMER3_OFF) power_timer3_disable(); + if (timer1 == TIMER1_OFF) power_timer1_disable(); + if (timer0 == TIMER0_OFF) power_timer0_disable(); + if (spi == SPI_OFF) power_spi_disable(); + if (usart1 == USART1_OFF) power_usart1_disable(); + if (twi == TWI_OFF) power_twi_disable(); + if (usb == USB_OFF) power_usb_disable(); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_IDLE); + + if (adc == ADC_OFF) + { + power_adc_enable(); + ADCSRA |= (1 << ADEN); + } + + if (timer4 == TIMER4_OFF) power_timer4_enable(); + if (timer3 == TIMER3_OFF) power_timer3_enable(); + if (timer1 == TIMER1_OFF) power_timer1_enable(); + if (timer0 == TIMER0_OFF) power_timer0_enable(); + if (spi == SPI_OFF) power_spi_enable(); + if (usart1 == USART1_OFF) power_usart1_enable(); + if (twi == TWI_OFF) power_twi_enable(); + if (usb == USB_OFF) power_usb_enable(); +} +#endif + +/******************************************************************************* +* Name: idle +* Description: Putting ATmega2560 & ATmega1280 into idle state. Please make sure +* you understand the implication and result of disabling module. +* Take note that extra Timer 5, 4, 3 compared to an ATmega328P/168. +* Also take note that extra USART 3, 2, 1 compared to an +* ATmega328P/168. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control: +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer5 Timer 5 module disable control: +* (a) TIMER5_OFF - Turn off Timer 5 module +* (b) TIMER5_ON - Leave Timer 5 module in its default state +* +* 4. timer4 Timer 4 module disable control: +* (a) TIMER4_OFF - Turn off Timer 4 module +* (b) TIMER4_ON - Leave Timer 4 module in its default state +* +* 5. timer3 Timer 3 module disable control: +* (a) TIMER3_OFF - Turn off Timer 3 module +* (b) TIMER3_ON - Leave Timer 3 module in its default state +* +* 6. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +* 7. timer1 Timer 1 module disable control: +* (a) TIMER1_OFF - Turn off Timer 1 module +* (b) TIMER1_ON - Leave Timer 1 module in its default state +* +* 8. timer0 Timer 0 module disable control: +* (a) TIMER0_OFF - Turn off Timer 0 module +* (b) TIMER0_ON - Leave Timer 0 module in its default state +* +* 9. spi SPI module disable control: +* (a) SPI_OFF - Turn off SPI module +* (b) SPI_ON - Leave SPI module in its default state +* +* 10.usart3 USART3 module disable control: +* (a) USART3_OFF - Turn off USART3 module +* (b) USART3_ON - Leave USART3 module in its default state +* +* 11.usart2 USART2 module disable control: +* (a) USART2_OFF - Turn off USART2 module +* (b) USART2_ON - Leave USART2 module in its default state +* +* 12.usart1 USART1 module disable control: +* (a) USART1_OFF - Turn off USART1 module +* (b) USART1_ON - Leave USART1 module in its default state +* +* 13.usart0 USART0 module disable control: +* (a) USART0_OFF - Turn off USART0 module +* (b) USART0_ON - Leave USART0 module in its default state +* +* 14.twi TWI module disable control: +* (a) TWI_OFF - Turn off TWI module +* (b) TWI_ON - Leave TWI module in its default state +* +*******************************************************************************/ +#if defined (__AVR_ATmega2560__) || defined (__AVR_ATmega1280__) +void LowPowerClass::idle(period_t period, adc_t adc, timer5_t timer5, + timer4_t timer4, timer3_t timer3, timer2_t timer2, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart3_t usart3, usart2_t usart2, usart1_t usart1, + usart0_t usart0, twi_t twi) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + + power_timer2_disable(); + } + + if (adc == ADC_OFF) + { + ADCSRA &= ~(1 << ADEN); + power_adc_disable(); + } + + if (timer5 == TIMER5_OFF) power_timer5_disable(); + if (timer4 == TIMER4_OFF) power_timer4_disable(); + if (timer3 == TIMER3_OFF) power_timer3_disable(); + if (timer1 == TIMER1_OFF) power_timer1_disable(); + if (timer0 == TIMER0_OFF) power_timer0_disable(); + if (spi == SPI_OFF) power_spi_disable(); + if (usart3 == USART3_OFF) power_usart3_disable(); + if (usart2 == USART2_OFF) power_usart2_disable(); + if (usart1 == USART1_OFF) power_usart1_disable(); + if (usart0 == USART0_OFF) power_usart0_disable(); + if (twi == TWI_OFF) power_twi_disable(); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_IDLE); + + if (adc == ADC_OFF) + { + power_adc_enable(); + ADCSRA |= (1 << ADEN); + } + + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + + power_timer2_enable(); + } + + if (timer5 == TIMER5_OFF) power_timer5_enable(); + if (timer4 == TIMER4_OFF) power_timer4_enable(); + if (timer3 == TIMER3_OFF) power_timer3_enable(); + if (timer1 == TIMER1_OFF) power_timer1_enable(); + if (timer0 == TIMER0_OFF) power_timer0_enable(); + if (spi == SPI_OFF) power_spi_enable(); + if (usart3 == USART3_OFF) power_usart3_enable(); + if (usart2 == USART2_OFF) power_usart2_enable(); + if (usart1 == USART1_OFF) power_usart1_enable(); + if (usart0 == USART0_OFF) power_usart0_enable(); + if (twi == TWI_OFF) power_twi_enable(); +} +#endif + +/******************************************************************************* +* Name: adcNoiseReduction +* Description: Putting microcontroller into ADC noise reduction state. This is +* a very useful state when using the ADC to achieve best and low +* noise signal. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +*******************************************************************************/ +void LowPowerClass::adcNoiseReduction(period_t period, adc_t adc, + timer2_t timer2) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + } + #endif + + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_ADC); + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + + } + #endif +} + +/******************************************************************************* +* Name: powerDown +* Description: Putting microcontroller into power down state. This is +* the lowest current consumption state. Use this together with +* external pin interrupt to wake up through external event +* triggering (example: RTC clockout pin, SD card detect pin). +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerDown(period_t period, adc_t adc, bod_t bod) +{ + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_PWR_DOWN); + #else + lowPowerBodOn(SLEEP_MODE_PWR_DOWN); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_PWR_DOWN); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); +} + +/******************************************************************************* +* Name: powerSave +* Description: Putting microcontroller into power save state. This is +* the lowest current consumption state after power down. +* Use this state together with an external 32.768 kHz crystal (but +* 8/16 MHz crystal/resonator need to be removed) to provide an +* asynchronous clock source to Timer 2. Please take note that +* Timer 2 is also used by the Arduino core for PWM operation. +* Please refer to wiring.c for explanation. Removal of the external +* 8/16 MHz crystal/resonator requires the microcontroller to run +* on its internal RC oscillator which is not so accurate for time +* critical operation. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +* 4. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerSave(period_t period, adc_t adc, bod_t bod, + timer2_t timer2) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + } + #endif + + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_PWR_SAVE); + #else + lowPowerBodOn(SLEEP_MODE_PWR_SAVE); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_PWR_SAVE); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + } + #endif +} + +/******************************************************************************* +* Name: powerStandby +* Description: Putting microcontroller into power standby state. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerStandby(period_t period, adc_t adc, bod_t bod) +{ + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_STANDBY); + #else + lowPowerBodOn(SLEEP_MODE_STANDBY); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_STANDBY); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); +} + +/******************************************************************************* +* Name: powerExtStandby +* Description: Putting microcontroller into power extended standby state. This +* is different from the power standby state as it has the +* capability to run Timer 2 asynchronously. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +* 4. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerExtStandby(period_t period, adc_t adc, bod_t bod, + timer2_t timer2) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + } + #endif + + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_EXT_STANDBY); + #else + lowPowerBodOn(SLEEP_MODE_EXT_STANDBY); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_EXT_STANDBY); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + } + #endif +} + +/******************************************************************************* +* Name: ISR (WDT_vect) +* Description: Watchdog Timer interrupt service routine. This routine is +* required to allow automatic WDIF and WDIE bit clearance in +* hardware. +* +*******************************************************************************/ +ISR (WDT_vect) +{ + // WDIE & WDIF is cleared in hardware upon entering this ISR + wdt_disable(); +} + +LowPowerClass LowPower; \ No newline at end of file diff --git a/libraries/LowPower/LowPower.h b/libraries/LowPower/LowPower.h new file mode 100644 index 0000000..ef05901 --- /dev/null +++ b/libraries/LowPower/LowPower.h @@ -0,0 +1,137 @@ +#ifndef LowPower_h +#define LowPower_h + +enum period_t +{ + SLEEP_15Ms, + SLEEP_30MS, + SLEEP_60MS, + SLEEP_120MS, + SLEEP_250MS, + SLEEP_500MS, + SLEEP_1S, + SLEEP_2S, + SLEEP_4S, + SLEEP_8S, + SLEEP_FOREVER +}; + +enum bod_t +{ + BOD_OFF, + BOD_ON +}; + +enum adc_t +{ + ADC_OFF, + ADC_ON +}; + +enum timer5_t +{ + TIMER5_OFF, + TIMER5_ON +}; + +enum timer4_t +{ + TIMER4_OFF, + TIMER4_ON +}; + +enum timer3_t +{ + TIMER3_OFF, + TIMER3_ON +}; + +enum timer2_t +{ + TIMER2_OFF, + TIMER2_ON +}; + +enum timer1_t +{ + TIMER1_OFF, + TIMER1_ON +}; + +enum timer0_t +{ + TIMER0_OFF, + TIMER0_ON +}; + +enum spi_t +{ + SPI_OFF, + SPI_ON +}; + +enum usart0_t +{ + USART0_OFF, + USART0_ON +}; + +enum usart1_t +{ + USART1_OFF, + USART1_ON +}; + +enum usart2_t +{ + USART2_OFF, + USART2_ON +}; + +enum usart3_t +{ + USART3_OFF, + USART3_ON +}; + +enum twi_t +{ + TWI_OFF, + TWI_ON +}; + +enum usb_t +{ + USB_OFF, + USB_ON +}; + +class LowPowerClass +{ + public: + #if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__) + void idle(period_t period, adc_t adc, timer2_t timer2, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart0_t usart0, twi_t twi); + #elif defined __AVR_ATmega2560__ + void idle(period_t period, adc_t adc, timer5_t timer5, + timer4_t timer4, timer3_t timer3, timer2_t timer2, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart3_t usart3, usart2_t usart2, usart1_t usart1, + usart0_t usart0, twi_t twi); + #elif defined __AVR_ATmega32U4__ + void idle(period_t period, adc_t adc, timer4_t timer4, timer3_t timer3, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart1_t usart1, twi_t twi, usb_t usb); + #else + #error "Please ensure chosen MCU is either 328P, 32U4 or 2560." + #endif + void adcNoiseReduction(period_t period, adc_t adc, timer2_t timer2); + void powerDown(period_t period, adc_t adc, bod_t bod); + void powerSave(period_t period, adc_t adc, bod_t bod, timer2_t timer2); + void powerStandby(period_t period, adc_t adc, bod_t bod); + void powerExtStandby(period_t period, adc_t adc, bod_t bod, timer2_t timer2); +}; + +extern LowPowerClass LowPower; +#endif \ No newline at end of file diff --git a/libraries/LowPower/keywords.txt b/libraries/LowPower/keywords.txt new file mode 100644 index 0000000..ef2461c --- /dev/null +++ b/libraries/LowPower/keywords.txt @@ -0,0 +1,68 @@ +####################################### +# Syntax Coloring Map LowPower +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +idle KEYWORD2 +adcNoiseReduction KEYWORD2 +powerDown KEYWORD2 +powerSave KEYWORD2 +powerStandby KEYWORD2 +powerExtStandby KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + +LowPower KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +SLEEP_15MS LITERAL1 +SLEEP_30MS LITERAL1 +SLEEP_60MS LITERAL1 +SLEEP_120MS LITERAL1 +SLEEP_250MS LITERAL1 +SLEEP_500MS LITERAL1 +SLEEP_1S LITERAL1 +SLEEP_2S LITERAL1 +SLEEP_4S LITERAL1 +SLEEP_8S LITERAL1 +SLEEP_FOREVER LITERAL1 +ADC_OFF LITERAL1 +ADC_ON LITERAL1 +BOD_OFF LITERAL1 +BOD_ON LITERAL1 +TIMER4_OFF LITERAL1 +TIMER4_ON LITERAL1 +TIMER3_OFF LITERAL1 +TIMER3_ON LITERAL1 +TIMER2_OFF LITERAL1 +TIMER2_ON LITERAL1 +TIMER1_OFF LITERAL1 +TIMER1_ON LITERAL1 +TIMER0_OFF LITERAL1 +TIMER0_ON LITERAL1 +USART3_OFF LITERAL1 +USART3_ON LITERAL1 +USART2_OFF LITERAL1 +USART2_ON LITERAL1 +USART1_OFF LITERAL1 +USART1_ON LITERAL1 +USART0_OFF LITERAL1 +USART0_ON LITERAL1 +SPI_OFF LITERAL1 +SPI_ON LITERAL1 +TWI_OFF LITERAL1 +TWI_ON LITERAL1 +USB_OFF LITERAL1 +USB_ON LITERAL1 \ No newline at end of file diff --git a/libraries/MySensors/LowPower.cpp b/libraries/MySensors/LowPower.cpp new file mode 100644 index 0000000..a72cc31 --- /dev/null +++ b/libraries/MySensors/LowPower.cpp @@ -0,0 +1,856 @@ +/******************************************************************************* +* LowPower Library +* Version: 1.30 +* Date: 22-05-2013 +* Company: Rocket Scream Electronics +* Website: www.rocketscream.com +* +* This is a lightweight low power library for Arduino. Please check our wiki +* (www.rocketscream.com/wiki) for more information on using this piece of +* library. +* +* This library is licensed under Creative Commons Attribution-ShareAlike 3.0 +* Unported License. +* +* Revision Description +* ======== =========== +* 1.30 Added support for ATMega168, ATMega2560, ATMega1280 & ATMega32U4. +* Tested to work with Arduino IDE 1.0.1 - 1.0.4. +* 1.20 Remove typo error in idle method for checking whether Timer 0 was +* turned off. +* Remove dependecy on WProgram.h which is not required. +* Tested to work with Arduino IDE 1.0. +* 1.10 Added #ifndef for sleep_bod_disable() for compatibility with future +* Arduino IDE release. +* 1.00 Initial public release. +*******************************************************************************/ +#include +#include +#include +#include +#include "LowPower.h" + +// Only Pico Power devices can change BOD settings through software +#if defined __AVR_ATmega328P__ +#ifndef sleep_bod_disable +#define sleep_bod_disable() \ +do { \ + unsigned char tempreg; \ + __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \ + "ori %[tempreg], %[bods_bodse]" "\n\t" \ + "out %[mcucr], %[tempreg]" "\n\t" \ + "andi %[tempreg], %[not_bodse]" "\n\t" \ + "out %[mcucr], %[tempreg]" \ + : [tempreg] "=&d" (tempreg) \ + : [mcucr] "I" _SFR_IO_ADDR(MCUCR), \ + [bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \ + [not_bodse] "i" (~_BV(BODSE))); \ +} while (0) +#endif +#endif + +#define lowPowerBodOn(mode) \ +do { \ + set_sleep_mode(mode); \ + cli(); \ + sleep_enable(); \ + sei(); \ + sleep_cpu(); \ + sleep_disable(); \ + sei(); \ +} while (0); + +// Only Pico Power devices can change BOD settings through software +#if defined __AVR_ATmega328P__ +#define lowPowerBodOff(mode)\ +do { \ + set_sleep_mode(mode); \ + cli(); \ + sleep_enable(); \ + sleep_bod_disable(); \ + sei(); \ + sleep_cpu(); \ + sleep_disable(); \ + sei(); \ +} while (0); +#endif + +// Some macros is still missing from AVR GCC distribution for ATmega32U4 +#if defined __AVR_ATmega32U4__ + // Timer 4 PRR bit is currently not defined in iom32u4.h + #ifndef PRTIM4 + #define PRTIM4 4 + #endif + + // Timer 4 power reduction macro is not defined currently in power.h + #ifndef power_timer4_disable + #define power_timer4_disable() (PRR1 |= (uint8_t)(1 << PRTIM4)) + #endif + + #ifndef power_timer4_enable + #define power_timer4_enable() (PRR1 &= (uint8_t)~(1 << PRTIM4)) + #endif +#endif + +/******************************************************************************* +* Name: idle +* Description: Putting ATmega328P/168 into idle state. Please make sure you +* understand the implication and result of disabling module. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control: +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +* 4. timer1 Timer 1 module disable control: +* (a) TIMER1_OFF - Turn off Timer 1 module +* (b) TIMER1_ON - Leave Timer 1 module in its default state +* +* 5. timer0 Timer 0 module disable control: +* (a) TIMER0_OFF - Turn off Timer 0 module +* (b) TIMER0_ON - Leave Timer 0 module in its default state +* +* 6. spi SPI module disable control: +* (a) SPI_OFF - Turn off SPI module +* (b) SPI_ON - Leave SPI module in its default state +* +* 7. usart0 USART0 module disable control: +* (a) USART0_OFF - Turn off USART0 module +* (b) USART0_ON - Leave USART0 module in its default state +* +* 8. twi TWI module disable control: +* (a) TWI_OFF - Turn off TWI module +* (b) TWI_ON - Leave TWI module in its default state +* +*******************************************************************************/ +#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__) +void LowPowerClass::idle(period_t period, adc_t adc, timer2_t timer2, + timer1_t timer1, timer0_t timer0, + spi_t spi, usart0_t usart0, twi_t twi) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + + power_timer2_disable(); + } + + if (adc == ADC_OFF) + { + ADCSRA &= ~(1 << ADEN); + power_adc_disable(); + } + + if (timer1 == TIMER1_OFF) power_timer1_disable(); + if (timer0 == TIMER0_OFF) power_timer0_disable(); + if (spi == SPI_OFF) power_spi_disable(); + if (usart0 == USART0_OFF) power_usart0_disable(); + if (twi == TWI_OFF) power_twi_disable(); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_IDLE); + + if (adc == ADC_OFF) + { + power_adc_enable(); + ADCSRA |= (1 << ADEN); + } + + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + + power_timer2_enable(); + } + + if (timer1 == TIMER1_OFF) power_timer1_enable(); + if (timer0 == TIMER0_OFF) power_timer0_enable(); + if (spi == SPI_OFF) power_spi_enable(); + if (usart0 == USART0_OFF) power_usart0_enable(); + if (twi == TWI_OFF) power_twi_enable(); +} +#endif + +/******************************************************************************* +* Name: idle +* Description: Putting ATmega32U4 into idle state. Please make sure you +* understand the implication and result of disabling module. +* Take note that Timer 2 is not available and USART0 +* is replaced with USART1 on ATmega32U4. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control: +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer4 Timer 4 module disable control: +* (a) TIMER4_OFF - Turn off Timer 4 module +* (b) TIMER4_ON - Leave Timer 4 module in its default state +* +* 4. timer3 Timer 3 module disable control: +* (a) TIMER3_OFF - Turn off Timer 3 module +* (b) TIMER3_ON - Leave Timer 3 module in its default state +* +* 5. timer1 Timer 1 module disable control: +* (a) TIMER1_OFF - Turn off Timer 1 module +* (b) TIMER1_ON - Leave Timer 1 module in its default state +* +* 6. timer0 Timer 0 module disable control: +* (a) TIMER0_OFF - Turn off Timer 0 module +* (b) TIMER0_ON - Leave Timer 0 module in its default state +* +* 7. spi SPI module disable control: +* (a) SPI_OFF - Turn off SPI module +* (b) SPI_ON - Leave SPI module in its default state +* +* 8. usart1 USART1 module disable control: +* (a) USART1_OFF - Turn off USART1 module +* (b) USART1_ON - Leave USART1 module in its default state +* +* 9. twi TWI module disable control: +* (a) TWI_OFF - Turn off TWI module +* (b) TWI_ON - Leave TWI module in its default state +* +* 10.usb USB module disable control: +* (a) USB_OFF - Turn off USB module +* (b) USB_ON - Leave USB module in its default state +*******************************************************************************/ +#if defined __AVR_ATmega32U4__ +void LowPowerClass::idle(period_t period, adc_t adc, + timer4_t timer4, timer3_t timer3, + timer1_t timer1, timer0_t timer0, + spi_t spi, usart1_t usart1, twi_t twi, usb_t usb) +{ + if (adc == ADC_OFF) + { + ADCSRA &= ~(1 << ADEN); + power_adc_disable(); + } + + if (timer4 == TIMER4_OFF) power_timer4_disable(); + if (timer3 == TIMER3_OFF) power_timer3_disable(); + if (timer1 == TIMER1_OFF) power_timer1_disable(); + if (timer0 == TIMER0_OFF) power_timer0_disable(); + if (spi == SPI_OFF) power_spi_disable(); + if (usart1 == USART1_OFF) power_usart1_disable(); + if (twi == TWI_OFF) power_twi_disable(); + if (usb == USB_OFF) power_usb_disable(); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_IDLE); + + if (adc == ADC_OFF) + { + power_adc_enable(); + ADCSRA |= (1 << ADEN); + } + + if (timer4 == TIMER4_OFF) power_timer4_enable(); + if (timer3 == TIMER3_OFF) power_timer3_enable(); + if (timer1 == TIMER1_OFF) power_timer1_enable(); + if (timer0 == TIMER0_OFF) power_timer0_enable(); + if (spi == SPI_OFF) power_spi_enable(); + if (usart1 == USART1_OFF) power_usart1_enable(); + if (twi == TWI_OFF) power_twi_enable(); + if (usb == USB_OFF) power_usb_enable(); +} +#endif + +/******************************************************************************* +* Name: idle +* Description: Putting ATmega2560 & ATmega1280 into idle state. Please make sure +* you understand the implication and result of disabling module. +* Take note that extra Timer 5, 4, 3 compared to an ATmega328P/168. +* Also take note that extra USART 3, 2, 1 compared to an +* ATmega328P/168. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control: +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer5 Timer 5 module disable control: +* (a) TIMER5_OFF - Turn off Timer 5 module +* (b) TIMER5_ON - Leave Timer 5 module in its default state +* +* 4. timer4 Timer 4 module disable control: +* (a) TIMER4_OFF - Turn off Timer 4 module +* (b) TIMER4_ON - Leave Timer 4 module in its default state +* +* 5. timer3 Timer 3 module disable control: +* (a) TIMER3_OFF - Turn off Timer 3 module +* (b) TIMER3_ON - Leave Timer 3 module in its default state +* +* 6. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +* 7. timer1 Timer 1 module disable control: +* (a) TIMER1_OFF - Turn off Timer 1 module +* (b) TIMER1_ON - Leave Timer 1 module in its default state +* +* 8. timer0 Timer 0 module disable control: +* (a) TIMER0_OFF - Turn off Timer 0 module +* (b) TIMER0_ON - Leave Timer 0 module in its default state +* +* 9. spi SPI module disable control: +* (a) SPI_OFF - Turn off SPI module +* (b) SPI_ON - Leave SPI module in its default state +* +* 10.usart3 USART3 module disable control: +* (a) USART3_OFF - Turn off USART3 module +* (b) USART3_ON - Leave USART3 module in its default state +* +* 11.usart2 USART2 module disable control: +* (a) USART2_OFF - Turn off USART2 module +* (b) USART2_ON - Leave USART2 module in its default state +* +* 12.usart1 USART1 module disable control: +* (a) USART1_OFF - Turn off USART1 module +* (b) USART1_ON - Leave USART1 module in its default state +* +* 13.usart0 USART0 module disable control: +* (a) USART0_OFF - Turn off USART0 module +* (b) USART0_ON - Leave USART0 module in its default state +* +* 14.twi TWI module disable control: +* (a) TWI_OFF - Turn off TWI module +* (b) TWI_ON - Leave TWI module in its default state +* +*******************************************************************************/ +#if defined (__AVR_ATmega2560__) || defined (__AVR_ATmega1280__) +void LowPowerClass::idle(period_t period, adc_t adc, timer5_t timer5, + timer4_t timer4, timer3_t timer3, timer2_t timer2, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart3_t usart3, usart2_t usart2, usart1_t usart1, + usart0_t usart0, twi_t twi) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + + power_timer2_disable(); + } + + if (adc == ADC_OFF) + { + ADCSRA &= ~(1 << ADEN); + power_adc_disable(); + } + + if (timer5 == TIMER5_OFF) power_timer5_disable(); + if (timer4 == TIMER4_OFF) power_timer4_disable(); + if (timer3 == TIMER3_OFF) power_timer3_disable(); + if (timer1 == TIMER1_OFF) power_timer1_disable(); + if (timer0 == TIMER0_OFF) power_timer0_disable(); + if (spi == SPI_OFF) power_spi_disable(); + if (usart3 == USART3_OFF) power_usart3_disable(); + if (usart2 == USART2_OFF) power_usart2_disable(); + if (usart1 == USART1_OFF) power_usart1_disable(); + if (usart0 == USART0_OFF) power_usart0_disable(); + if (twi == TWI_OFF) power_twi_disable(); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_IDLE); + + if (adc == ADC_OFF) + { + power_adc_enable(); + ADCSRA |= (1 << ADEN); + } + + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + + power_timer2_enable(); + } + + if (timer5 == TIMER5_OFF) power_timer5_enable(); + if (timer4 == TIMER4_OFF) power_timer4_enable(); + if (timer3 == TIMER3_OFF) power_timer3_enable(); + if (timer1 == TIMER1_OFF) power_timer1_enable(); + if (timer0 == TIMER0_OFF) power_timer0_enable(); + if (spi == SPI_OFF) power_spi_enable(); + if (usart3 == USART3_OFF) power_usart3_enable(); + if (usart2 == USART2_OFF) power_usart2_enable(); + if (usart1 == USART1_OFF) power_usart1_enable(); + if (usart0 == USART0_OFF) power_usart0_enable(); + if (twi == TWI_OFF) power_twi_enable(); +} +#endif + +/******************************************************************************* +* Name: adcNoiseReduction +* Description: Putting microcontroller into ADC noise reduction state. This is +* a very useful state when using the ADC to achieve best and low +* noise signal. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +*******************************************************************************/ +void LowPowerClass::adcNoiseReduction(period_t period, adc_t adc, + timer2_t timer2) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + } + #endif + + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + lowPowerBodOn(SLEEP_MODE_ADC); + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + + } + #endif +} + +/******************************************************************************* +* Name: powerDown +* Description: Putting microcontroller into power down state. This is +* the lowest current consumption state. Use this together with +* external pin interrupt to wake up through external event +* triggering (example: RTC clockout pin, SD card detect pin). +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerDown(period_t period, adc_t adc, bod_t bod) +{ + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_PWR_DOWN); + #else + lowPowerBodOn(SLEEP_MODE_PWR_DOWN); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_PWR_DOWN); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); +} + +/******************************************************************************* +* Name: powerSave +* Description: Putting microcontroller into power save state. This is +* the lowest current consumption state after power down. +* Use this state together with an external 32.768 kHz crystal (but +* 8/16 MHz crystal/resonator need to be removed) to provide an +* asynchronous clock source to Timer 2. Please take note that +* Timer 2 is also used by the Arduino core for PWM operation. +* Please refer to wiring.c for explanation. Removal of the external +* 8/16 MHz crystal/resonator requires the microcontroller to run +* on its internal RC oscillator which is not so accurate for time +* critical operation. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +* 4. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerSave(period_t period, adc_t adc, bod_t bod, + timer2_t timer2) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + } + #endif + + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_PWR_SAVE); + #else + lowPowerBodOn(SLEEP_MODE_PWR_SAVE); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_PWR_SAVE); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + } + #endif +} + +/******************************************************************************* +* Name: powerStandby +* Description: Putting microcontroller into power standby state. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerStandby(period_t period, adc_t adc, bod_t bod) +{ + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_STANDBY); + #else + lowPowerBodOn(SLEEP_MODE_STANDBY); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_STANDBY); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); +} + +/******************************************************************************* +* Name: powerExtStandby +* Description: Putting microcontroller into power extended standby state. This +* is different from the power standby state as it has the +* capability to run Timer 2 asynchronously. +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +* 4. timer2 Timer 2 module disable control: +* (a) TIMER2_OFF - Turn off Timer 2 module +* (b) TIMER2_ON - Leave Timer 2 module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerExtStandby(period_t period, adc_t adc, bod_t bod, + timer2_t timer2) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (TCCR2B & CS22) clockSource |= (1 << CS22); + if (TCCR2B & CS21) clockSource |= (1 << CS21); + if (TCCR2B & CS20) clockSource |= (1 << CS20); + + // Remove the clock source to shutdown Timer2 + TCCR2B &= ~(1 << CS22); + TCCR2B &= ~(1 << CS21); + TCCR2B &= ~(1 << CS20); + } + #endif + + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + WDTCSR |= (1 << WDIE); + } + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_EXT_STANDBY); + #else + lowPowerBodOn(SLEEP_MODE_EXT_STANDBY); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_EXT_STANDBY); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); + + #if !defined(__AVR_ATmega32U4__) + if (timer2 == TIMER2_OFF) + { + if (clockSource & CS22) TCCR2B |= (1 << CS22); + if (clockSource & CS21) TCCR2B |= (1 << CS21); + if (clockSource & CS20) TCCR2B |= (1 << CS20); + } + #endif +} + +/******************************************************************************* +* Name: ISR (WDT_vect) +* Description: Watchdog Timer interrupt service routine. This routine is +* required to allow automatic WDIF and WDIE bit clearance in +* hardware. +* +*******************************************************************************/ +ISR (WDT_vect) +{ + // WDIE & WDIF is cleared in hardware upon entering this ISR + wdt_disable(); +} + +LowPowerClass LowPower; \ No newline at end of file diff --git a/libraries/MySensors/LowPower.h b/libraries/MySensors/LowPower.h new file mode 100644 index 0000000..ef05901 --- /dev/null +++ b/libraries/MySensors/LowPower.h @@ -0,0 +1,137 @@ +#ifndef LowPower_h +#define LowPower_h + +enum period_t +{ + SLEEP_15Ms, + SLEEP_30MS, + SLEEP_60MS, + SLEEP_120MS, + SLEEP_250MS, + SLEEP_500MS, + SLEEP_1S, + SLEEP_2S, + SLEEP_4S, + SLEEP_8S, + SLEEP_FOREVER +}; + +enum bod_t +{ + BOD_OFF, + BOD_ON +}; + +enum adc_t +{ + ADC_OFF, + ADC_ON +}; + +enum timer5_t +{ + TIMER5_OFF, + TIMER5_ON +}; + +enum timer4_t +{ + TIMER4_OFF, + TIMER4_ON +}; + +enum timer3_t +{ + TIMER3_OFF, + TIMER3_ON +}; + +enum timer2_t +{ + TIMER2_OFF, + TIMER2_ON +}; + +enum timer1_t +{ + TIMER1_OFF, + TIMER1_ON +}; + +enum timer0_t +{ + TIMER0_OFF, + TIMER0_ON +}; + +enum spi_t +{ + SPI_OFF, + SPI_ON +}; + +enum usart0_t +{ + USART0_OFF, + USART0_ON +}; + +enum usart1_t +{ + USART1_OFF, + USART1_ON +}; + +enum usart2_t +{ + USART2_OFF, + USART2_ON +}; + +enum usart3_t +{ + USART3_OFF, + USART3_ON +}; + +enum twi_t +{ + TWI_OFF, + TWI_ON +}; + +enum usb_t +{ + USB_OFF, + USB_ON +}; + +class LowPowerClass +{ + public: + #if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__) + void idle(period_t period, adc_t adc, timer2_t timer2, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart0_t usart0, twi_t twi); + #elif defined __AVR_ATmega2560__ + void idle(period_t period, adc_t adc, timer5_t timer5, + timer4_t timer4, timer3_t timer3, timer2_t timer2, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart3_t usart3, usart2_t usart2, usart1_t usart1, + usart0_t usart0, twi_t twi); + #elif defined __AVR_ATmega32U4__ + void idle(period_t period, adc_t adc, timer4_t timer4, timer3_t timer3, + timer1_t timer1, timer0_t timer0, spi_t spi, + usart1_t usart1, twi_t twi, usb_t usb); + #else + #error "Please ensure chosen MCU is either 328P, 32U4 or 2560." + #endif + void adcNoiseReduction(period_t period, adc_t adc, timer2_t timer2); + void powerDown(period_t period, adc_t adc, bod_t bod); + void powerSave(period_t period, adc_t adc, bod_t bod, timer2_t timer2); + void powerStandby(period_t period, adc_t adc, bod_t bod); + void powerExtStandby(period_t period, adc_t adc, bod_t bod, timer2_t timer2); +}; + +extern LowPowerClass LowPower; +#endif \ No newline at end of file diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 75bb89d..bdfa7ab 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -10,6 +10,8 @@ */ #include "MySensor.h" +#include "LowPower.h" + // Inline function and macros inline MyMessage* build (MyMessage* msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) { @@ -499,7 +501,7 @@ uint8_t MySensor::getChildRoute(uint8_t childId) { } -long MySensor::getInternalTemp(void) +float MySensor::getInternalTemp(void) { long result; // Read internal temp sensor against 1.1V reference @@ -511,9 +513,48 @@ long MySensor::getInternalTemp(void) result |= ADCH<<8; result = (result - 125) * 1075; - return result; + return static_cast(static_cast(result/1000.)) / 10.; } +int continueTimer = true; +void wakeUp() //place to send the interrupts +{ + continueTimer = false; +} + +void MySensor::internalSleep(int ms) { + while (continueTimer && ms >= 8000) { LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); ms -= 8000; } + if (continueTimer && ms >= 4000) { LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); ms -= 4000; } + if (continueTimer && ms >= 2000) { LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); ms -= 2000; } + if (continueTimer && ms >= 1000) { LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); ms -= 1000; } + if (continueTimer && ms >= 500) { LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF); ms -= 500; } + if (continueTimer && ms >= 250) { LowPower.powerDown(SLEEP_250MS, ADC_OFF, BOD_OFF); ms -= 250; } + if (continueTimer && ms >= 125) { LowPower.powerDown(SLEEP_120MS, ADC_OFF, BOD_OFF); ms -= 120; } + if (continueTimer && ms >= 64) { LowPower.powerDown(SLEEP_60MS, ADC_OFF, BOD_OFF); ms -= 60; } + if (continueTimer && ms >= 32) { LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_OFF); ms -= 30; } + if (continueTimer && ms >= 16) { LowPower.powerDown(SLEEP_15Ms, ADC_OFF, BOD_OFF); ms -= 15; } +} + +void MySensor::sleep(int ms) { + RF24::powerDown(); + continueTimer = true; + internalSleep(ms); +} + +void MySensor::sleep(int interrupt, int mode, int ms) { + RF24::powerDown(); + attachInterrupt(interrupt, wakeUp, mode); //Interrupt on pin 3 for any change in solar power + if (ms>0) { + continueTimer = true; + sleep(ms); + } else { + LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); + } + detachInterrupt(interrupt); +} + + + #ifdef DEBUG void MySensor::debugPrint(const char *fmt, ... ) { char fmtBuffer[300]; diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 94bdd84..2c1a025 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -24,6 +24,7 @@ #include #include #include +#include "LowPower.h" #ifdef DEBUG #define debug(x,...) debugPrint(x, ##__VA_ARGS__) @@ -193,15 +194,30 @@ class MySensor : public RF24 */ MyMessage* getLastMessage(void); + /** + * Sleep (PowerDownMode) the Arduino and radio. Wake up on timer. + * @param ms Number of milliseconds to sleep. + */ + void sleep(int ms); + + /** + * Sleep (PowerDownMode) the Arduino and radio. Wake up on timer or pin change. + * See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin + * is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3 + * @param interrupt Interrupt that should trigger the wakeup + * @param mode RISING, FALLING, CHANGE + * @param ms Number of milliseconds to sleep or 0 to sleep forever + */ + void sleep(int interrupt, int mode, int ms=0); /** * getInternalTemp * * Read temp from internal (ATMEGA328 only) temperature sensor * - * @return Temperature in milli-degrees Celsius + * @return Temperature in degrees Celsius (one decimal) */ - long getInternalTemp(void); + float getInternalTemp(void); @@ -240,6 +256,7 @@ class MySensor : public RF24 uint8_t getChildRoute(uint8_t childId); void addChildRoute(uint8_t childId, uint8_t route); void removeChildRoute(uint8_t childId); + void internalSleep(int ms); }; #endif diff --git a/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino b/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino index 70a8cf8..8322b50 100644 --- a/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino +++ b/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino @@ -2,17 +2,13 @@ // Instructions for measuring battery capacity on A0 are available in the follwoing forum // thread: http://forum.micasaverde.com/index.php/topic,20078.0.html - -#include #include -#include #include -#include +#include int BATTERY_SENSE_PIN = A0; // select the input pin for the battery sense point -Sensor gw; -Sleep sleep; +MySensor gw; unsigned long SLEEP_TIME = 900000; // sleep time between reads (seconds * 1000 milliseconds) int oldBatteryPcnt = 0; @@ -54,12 +50,6 @@ void loop() } // delay to allow transmissions to gateway to be completed before sleep - delay(500); - - // Power down the radio. Note that the radio will get powered back up - // on the next write() call - gw.powerDown(); - - sleep.pwrDownMode(); // set sleep mode - sleep.sleepDelay(SLEEP_TIME); // sleep for SLEEP_TIME + delay(300); + gw.sleep(SLEEP_TIME); } diff --git a/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino b/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino index 61f4da2..f1a65ca 100644 --- a/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino +++ b/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino @@ -2,17 +2,20 @@ // Connect button or door/window reed switch between // digitial I/O pin 3 (BUTTON_PIN below) and GND. -#include +#include #include #include #include #include +#define CHILD_ID 3 #define BUTTON_PIN 3 // Arduino Digital I/O pin for button/reed switch -Sensor gw; +MySensor gw; Bounce debouncer = Bounce(); int oldValue=-1; +// Change to V_LIGHT if you use S_LIGHT in presentation below +MyMessage msg(CHILD_ID,V_TRIPPED); void setup() { @@ -30,7 +33,7 @@ void setup() // Register binary input sensor to gw (they will be created as child devices) // You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage. // If S_LIGHT is used, remember to update variable type you send in below. - gw.sendSensorPresentation(BUTTON_PIN, S_DOOR); + gw.present(CHILD_ID, S_DOOR); } @@ -43,7 +46,7 @@ void loop() if (value != oldValue) { // Send in the new value - gw.sendVariable(BUTTON_PIN, V_TRIPPED, value==HIGH ? "1" : "0"); // Change to V_LIGHT if you use S_LIGHT in presentation above + gw.send(msg.set(value==HIGH ? 1 : 0)); oldValue = value; } } diff --git a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino index b85aeeb..9a0282c 100644 --- a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino +++ b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino @@ -1,6 +1,5 @@ // Example sketch showing how to send in OneWire temperature readings #include -#include #include #include #include @@ -12,7 +11,6 @@ unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); MySensor gw; -Sleep sleep; float lastTemperature[MAX_ATTACHED_DS18B20]; int numSensors=0; boolean receivedConfig = false; @@ -64,10 +62,8 @@ void loop() } } // Power down the radio and sleep Arduino to save batteries. - delay(500); - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime + delay(300); + gw.sleep(SLEEP_TIME * 1000); } diff --git a/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino b/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino index 8c246d0..de1438a 100644 --- a/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino +++ b/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino @@ -1,19 +1,18 @@ -#include #include #include #include -#include +#include #include +#define CHILD_ID 1 #define TRIGGER_PIN 6 // Arduino pin tied to trigger pin on the ultrasonic sensor. #define ECHO_PIN 5 // Arduino pin tied to echo pin on the ultrasonic sensor. #define MAX_DISTANCE 300 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm. unsigned long SLEEP_TIME = 5; // Sleep time between reads (in seconds) -Sensor gw; +MySensor gw; NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance. -Sleep sleep; - +MyMessage msg(CHILD_ID, V_DISTANCE); int lastDist; boolean metric = true; @@ -25,8 +24,8 @@ void setup() gw.sendSketchInfo("Distance Sensor", "1.0"); // Register all sensors to gw (they will be created as child devices) - gw.sendSensorPresentation(0, S_DISTANCE); - metric = gw.isMetricSystem(); + gw.present(CHILD_ID, S_DISTANCE); + boolean metric = gw.getConfig().isMetric; } void loop() @@ -39,16 +38,12 @@ void loop() if (dist != lastDist) { - gw.sendVariable(0, V_DISTANCE, dist); + gw.send(msg.set(dist)); lastDist = dist; } - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - delay(1000); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime + delay(300); //delay to allow serial to fully print before sleep + gw.sleep(SLEEP_TIME*1000); } diff --git a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino index 2fc8cf8..3a65c37 100644 --- a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino +++ b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino @@ -7,26 +7,21 @@ // sleepmode. So we cannot make this sensor sleep if we also want // to calculate/report watt-number. -#include #include #include #include -#include +#include #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your light sensor. (Only 2 and 3 generates interrupt!) #define PULSE_FACTOR 1000 // Nummber of blinks per KWH of your meeter #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 filetrs outliers. #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) -#define CHILD_ID 1 // Id of the sensor child +#define CHILD_ID 1 // Id of the sensor child unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't wnat to spam the gateway. - -Sensor gw; -Sleep sleep; - -//double kwhPerBlink = 1.0/((double)PULSE_FACTOR); +MySensor gw; double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour - +boolean pcReceived = false; volatile unsigned long pulseCount = 0; volatile unsigned long lastBlink = 0; volatile unsigned long watt = 0; @@ -34,21 +29,24 @@ unsigned long oldPulseCount = 0; unsigned long oldWatt = 0; double oldKwh; unsigned long lastSend; +MyMessage wattMsg(CHILD_ID,V_WATT); +MyMessage kwhMsg(CHILD_ID,V_KWH); +MyMessage pcMsg(CHILD_ID,V_VAR1); + void setup() { - gw.begin(); + gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Energy Meter", "1.0"); // Register this device as power sensor - gw.sendSensorPresentation(CHILD_ID, S_POWER); + gw.present(CHILD_ID, S_POWER); // Fetch last known pulse count value from gw - pulseCount = oldPulseCount = atol(gw.getStatus(CHILD_ID, V_VAR1)); - Serial.print("Last pulse count from gw:"); - Serial.println(pulseCount); + gw.request(CHILD_ID, V_VAR1); + attachInterrupt(INTERRUPT, onPulse, RISING); lastSend=millis(); } @@ -56,15 +54,17 @@ void setup() void loop() { - unsigned long now = millis(); + gw.process(); + unsigned long now = millis(); // Only send values at a maximum frequency or woken up from sleep - if (SLEEP_MODE || now - lastSend > 1000*SEND_FREQUENCY) { + bool sendTime = now - lastSend > 1000*SEND_FREQUENCY; + if (pcReceived && (SLEEP_MODE || sendTime)) { // New watt value has been calculated if (!SLEEP_MODE && watt != oldWatt) { // Check that we dont get unresonable large watt value. // could hapen when long wraps or false interrupt triggered if (watt<((unsigned long)MAX_WATT)) { - gw.sendVariable(CHILD_ID, V_WATT, watt); // Send watt value to gw + gw.send(wattMsg.set(watt)); // Send watt value to gw } Serial.print("Watt:"); Serial.println(watt); @@ -73,26 +73,32 @@ void loop() // Pulse cout has changed if (pulseCount != oldPulseCount) { - gw.sendVariable(CHILD_ID, V_VAR1, pulseCount); // Send kwh value to gw + gw.send(pcMsg.set(pulseCount)); // Send pulse count value to gw double kwh = ((double)pulseCount/((double)PULSE_FACTOR)); oldPulseCount = pulseCount; - //Serial.print("Pulse count:"); - //Serial.println(pulseCount); if (kwh != oldKwh) { - gw.sendVariable(CHILD_ID, V_KWH, kwh, 4); // Send kwh value to gw - //Serial.print("KWH:"); - //Serial.println(kwh); + gw.send(kwhMsg.set(kwh, 4)); // Send kwh value to gw oldKwh = kwh; } - } + } lastSend = now; + } else if (sendTime && !pcReceived) { + // No count received. Try requesting it again + gw.request(CHILD_ID, V_VAR1); } if (SLEEP_MODE) { delay(300); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SEND_FREQUENCY * 1000); //sleep for: sleepTime + gw.sleep(SEND_FREQUENCY * 1000); + } +} + +void incomingMessage(MyMessage message) { + if (message.type==V_VAR1) { + pulseCount = oldPulseCount = message.getLong(); + Serial.print("Last pulse count from gw:"); + Serial.println(pulseCount); + pcReceived = true; } } diff --git a/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino b/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino index bef4943..3952ce9 100644 --- a/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino +++ b/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino @@ -1,8 +1,6 @@ -#include #include -#include #include -#include +#include #include #define CHILD_ID_HUM 0 @@ -10,12 +8,14 @@ #define HUMIDITY_SENSOR_DIGITAL_PIN 3 unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) -Sensor gw; +MySensor gw; DHT dht; -Sleep sleep; float lastTemp; float lastHum; boolean metric = true; +MyMessage msgHum(CHILD_ID_HUM, V_HUM); +MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP); + void setup() { @@ -26,10 +26,10 @@ void setup() gw.sendSketchInfo("Humidity", "1.0"); // Register all sensors to gw (they will be created as child devices) - gw.sendSensorPresentation(CHILD_ID_HUM, S_HUM); - gw.sendSensorPresentation(CHILD_ID_TEMP, S_TEMP); + gw.present(CHILD_ID_HUM, S_HUM); + gw.present(CHILD_ID_TEMP, S_TEMP); - metric = gw.isMetricSystem(); + metric = gw.getConfig().isMetric; } void loop() @@ -44,9 +44,9 @@ void loop() if (!metric) { temperature = dht.toFahrenheit(temperature); } - gw.sendVariable(CHILD_ID_TEMP, V_TEMP, temperature, 1); - Serial.print("T: "); - Serial.println(temperature); + gw.send(msgTemp.set(temperature, 1)); + Serial.print("T: "); + Serial.println(temperature); } float humidity = dht.getHumidity(); @@ -54,17 +54,13 @@ void loop() Serial.println("Failed reading humidity from DHT"); } else if (humidity != lastHum) { lastHum = humidity; - gw.sendVariable(CHILD_ID_HUM, V_HUM, humidity, 1); + gw.send(msgHum.set(humidity, 1)); Serial.print("H: "); Serial.println(humidity); } - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - delay(1000); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime + delay(300); //delay to allow serial to fully print before sleep + gw.sleep(SLEEP_TIME * 1000); //sleep a bit } diff --git a/libraries/MySensors/examples/IrSensor/IrSensor.ino b/libraries/MySensors/examples/IrSensor/IrSensor.ino index bb1ea65..a34e024 100644 --- a/libraries/MySensors/examples/IrSensor/IrSensor.ino +++ b/libraries/MySensors/examples/IrSensor/IrSensor.ino @@ -5,7 +5,7 @@ // When binary light on is clicked - sketch will send volume up ir command // When binary light off is clicked - sketch will send volume down ir command -#include +#include #include #include #include @@ -20,39 +20,26 @@ IRrecv irrecv(RECV_PIN); IRdecode decoder; //decode_results results; unsigned int Buffer[RAWBUF]; - -Sensor gw; +MySensor gw; +MyMessage msg(CHILD_1, V_VAR1); void setup() { irrecv.enableIRIn(); // Start the ir receiver decoder.UseExtnBuf(Buffer); - gw.begin(); + gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("IR Sensor", "1.0"); // Register a sensors to gw. Use binary light for test purposes. - gw.sendSensorPresentation(CHILD_1, S_LIGHT); + gw.present(CHILD_1, S_LIGHT); } void loop() { - if (gw.messageAvailable()) { - message_s message = gw.getMessage(); - - int incomingRelayStatus = atoi(message.data); - if (incomingRelayStatus == 1) { - irsend.send(NEC, 0x1EE17887, 32); // Vol up yamaha ysp-900 - } else { - irsend.send(NEC, 0x1EE1F807, 32); // Vol down yamaha ysp-900 - } - // Start receiving ir again... - irrecv.enableIRIn(); - } - - + gw.process(); if (irrecv.GetResults(&decoder)) { irrecv.resume(); decoder.decode(); @@ -61,11 +48,26 @@ void loop() char buffer[10]; sprintf(buffer, "%08lx", decoder.value); // Send ir result to gw - gw.sendVariable(CHILD_1, V_VAR1, buffer); + gw.send(msg.set(buffer)); } } + +void incomingMessage(MyMessage message) { + // We only expect one type of message from controller. But we better check anyway. + if (message.type==V_LIGHT) { + int incomingRelayStatus = message.getInt(); + if (incomingRelayStatus == 1) { + irsend.send(NEC, 0x1EE17887, 32); // Vol up yamaha ysp-900 + } else { + irsend.send(NEC, 0x1EE1F807, 32); // Vol down yamaha ysp-900 + } + // Start receiving ir again... + irrecv.enableIRIn(); + } +} + // Dumps out the decode_results structure. // Call this after IRrecv::decode() // void * to work around compiler issue diff --git a/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino b/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino index c995316..f834f0c 100644 --- a/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino +++ b/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino @@ -16,12 +16,9 @@ */ - -#include #include #include -#include -#include +#include #include #include @@ -29,9 +26,9 @@ #define LIGHT_SENSOR_ANALOG_PIN 0 unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) -BH1750 lightSensor; -Sensor gw; -Sleep sleep; +BH1750 lightSensor; +MySensor gw; +MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL); int lastlux; void setup() @@ -42,7 +39,7 @@ void setup() gw.sendSketchInfo("Light Lux Sensor", "1.0"); // Register all sensors to gateway (they will be created as child devices) - gw.sendSensorPresentation(CHILD_ID_LIGHT, S_LIGHT_LEVEL); + gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); lightSensor.begin(); } @@ -52,14 +49,10 @@ void loop() uint16_t lux = lightSensor.readLightLevel();// Get Lux value Serial.println(lux); if (lux != lastlux) { - gw.sendVariable(CHILD_ID_LIGHT, V_LIGHT_LEVEL, lux); + gw.send(msg.set(lux)); lastlux = lux; } - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - delay(1000); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime + delay(300); //delay to allow serial to fully print before sleep + gw.sleep(SLEEP_TIME * 1000); } diff --git a/libraries/MySensors/examples/LightSensor/LightSensor.ino b/libraries/MySensors/examples/LightSensor/LightSensor.ino index 0b5de6f..df31c89 100644 --- a/libraries/MySensors/examples/LightSensor/LightSensor.ino +++ b/libraries/MySensors/examples/LightSensor/LightSensor.ino @@ -1,17 +1,15 @@ -#include #include #include -#include -#include +#include #define CHILD_ID_LIGHT 0 #define LIGHT_SENSOR_ANALOG_PIN 0 unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) -Sensor gw; +MySensor gw; +MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL); int lastLightLevel; -Sleep sleep; void setup() { @@ -21,7 +19,7 @@ void setup() gw.sendSketchInfo("Light Sensor", "1.0"); // Register all sensors to gateway (they will be created as child devices) - gw.sendSensorPresentation(CHILD_ID_LIGHT, S_LIGHT_LEVEL); + gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); } void loop() @@ -29,16 +27,12 @@ void loop() int lightLevel = (1023-analogRead(LIGHT_SENSOR_ANALOG_PIN))/10.23; Serial.println(lightLevel); if (lightLevel != lastLightLevel) { - gw.sendVariable(CHILD_ID_LIGHT, V_LIGHT_LEVEL, lightLevel); + gw.send(msg.set(lightLevel)); lastLightLevel = lightLevel; } - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - delay(1000); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime + delay(300); //delay to allow serial to fully print before sleep + gw.sleep(SLEEP_TIME * 1000); } diff --git a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino index 3bb8777..6799854 100644 --- a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino +++ b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino @@ -1,5 +1,4 @@ #include -#include #include #include @@ -8,15 +7,11 @@ #define CHILD_ID 1 // Id of the sensor child MySensor gw; -Sleep sleep; // Initialize motion message MyMessage msg(CHILD_ID, V_TRIPPED); void setup() { - //eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, 1); - //eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, 255); - //delay(2000); gw.begin(); // Send the sketch version information to the gateway and Controller @@ -24,9 +19,7 @@ void setup() pinMode(DIGITAL_INPUT_SENSOR, INPUT); // sets the motion sensor digital pin as input // Register all sensors to gw (they will be created as child devices) - delay(500); gw.present(CHILD_ID, S_MOTION); - delay(1000); } @@ -38,12 +31,8 @@ void loop() Serial.println(tripped); gw.send(msg.set(tripped?"1":"0")); // Send tripped value to gw - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - delay(200); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepInterrupt(INTERRUPT,CHANGE); + // Sleep until interrupt comes in on motion sensor. Send update every two minute. + gw.sleep(INTERRUPT,CHANGE, 120000); } diff --git a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino index 603f402..636e79f 100644 --- a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino +++ b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino @@ -1,21 +1,21 @@ -#include #include -#include #include -#include +#include #include #include +#define BARO_CHILD 0 +#define TEMP_CHILD 1 + #define LIGHT_SENSOR_ANALOG_PIN 0 unsigned long SLEEP_TIME = 60; // Sleep time between reads (in seconds) Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor -Sensor gw; +MySensor gw; float lastPressure = -1; float lastTemp = -1; int lastForecast = -1; -Sleep sleep; char *weather[] = {"stable","sunny","cloudy","unstable","thunderstorm","unknown"}; int minutes; float pressureSamples[180]; @@ -24,6 +24,11 @@ bool firstRound = true; float pressureAvg[7]; float dP_dt; boolean metric; +MyMessage tempMsg(TEMP_CHILD, V_TEMP); +MyMessage pressureMsg(BARO_CHILD, V_PRESSURE); +MyMessage forecastMsg(BARO_CHILD, V_FORECAST); + + void setup() { gw.begin(); @@ -37,9 +42,9 @@ void setup() { } // Register sensors to gw (they will be created as child devices) - gw.sendSensorPresentation(0, S_BARO); - gw.sendSensorPresentation(1, S_TEMP); - metric = gw.isMetricSystem(); + gw.present(BARO_CHILD, S_BARO); + gw.present(TEMP_CHILD, S_TEMP); + metric = gw.getConfig().isMetric; } @@ -64,17 +69,17 @@ void loop() { if (temperature != lastTemp) { - gw.sendVariable(1, V_TEMP, temperature,1); + gw.send(tempMsg.set(temperature,1)); lastTemp = temperature; } if (pressure != lastPressure) { - gw.sendVariable(0, V_PRESSURE, pressure, 0); + gw.send(pressureMsg.set(pressure, 0)); lastPressure = pressure; } if (forecast != lastForecast) { - gw.sendVariable(0, V_FORECAST, weather[forecast]); + gw.send(forecastMsg.set(weather[forecast])); lastForecast = forecast; } @@ -89,12 +94,8 @@ void loop() { 5 = "Unknown (More Time needed) */ - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - delay(1000); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SLEEP_TIME * 1000); // sleep to conserve power + delay(300); //delay to allow serial to fully print before sleep + gw.sleep(SLEEP_TIME * 1000); } int sample(float pressure) { diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index f04b602..a850cce 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -39,6 +39,7 @@ void loop() } void incomingMessage(MyMessage message) { + Serial.println(gw.getInternalTemp()); // We only expect one type of message from controller. But we better check anyway. if (message.type==V_LIGHT) { // Change relay state diff --git a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino index 427f9fd..9aed1b4 100644 --- a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino +++ b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino @@ -1,11 +1,12 @@ // Example sketch för a "light switch" where you can control light or something // else from both vera and a local physical button (connected between digital // pin 3 and +5V). +// This node also works as a repeader for other nodes -#include +#include #include -#include #include +#include #define RELAY_PIN 4 // Arduino Digital I/O pin number for relay #define BUTTON_PIN 3 // Arduino Digital I/O pin number for button (with 10k pulldown) @@ -13,37 +14,40 @@ #define RELAY_ON 0 #define RELAY_OFF 1 -int buttVal; -int previousVal; -int state; -int switched = 0; -long pressTime = 0; // the last time the button pin was pressed -long debounce = 200; // the debounce time, increase if the output flickers - -Relay gw; +Bounce debouncer = Bounce(); +int oldValue=-1; +bool state; +MySensor gw; +MyMessage msg(CHILD_ID,V_LIGHT); void setup() { - gw.begin(); + gw.begin(incomingMessage, AUTO, true); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Relay & Button", "1.0"); - // Set buttonPin as Input - pinMode(BUTTON_PIN, INPUT); - buttVal = digitalRead(BUTTON_PIN); //read initial state + // Setup the button + pinMode(BUTTON_PIN,INPUT); + // Activate internal pull-up + digitalWrite(BUTTON_PIN,HIGH); + + // After setting up the button, setup debouncer + debouncer.attach(BUTTON_PIN); + debouncer.interval(5); + // Register all sensors to gw (they will be created as child devices) - gw.sendSensorPresentation(CHILD_ID, S_LIGHT); + gw.present(CHILD_ID, S_LIGHT); // Make sure relays are off when starting up digitalWrite(RELAY_PIN, RELAY_OFF); // Then set relay pins in output mode pinMode(RELAY_PIN, OUTPUT); - // Request/wait for relay status - gw.getStatus(CHILD_ID, V_LIGHT); - setRelayStatus(gw.getMessage()); // Wait here until status message arrive from gw + // Set relay to last known state (using eeprom storage) + digitalWrite(RELAY_PIN, gw.loadState(CHILD_ID)); + } @@ -53,40 +57,35 @@ void setup() */ void loop() { - if (gw.messageAvailable()) { - // ot new messsage from gw - message_s message = gw.getMessage(); - setRelayStatus(message); - } + gw.process(); + debouncer.update(); + // Get the update value + int value = debouncer.read(); - buttVal = digitalRead(BUTTON_PIN); // read input value and store it in val - - if (buttVal == HIGH && previousVal == LOW) { - // Start counter from when button was pressed - pressTime = millis(); - switched = 0; - } - - if (buttVal == HIGH && switched == 0 && millis() - pressTime > debounce) { - // Switch state if button pressed more than 200 msec - state = state==1?0:1; - gw.sendVariable(CHILD_ID, V_LIGHT, state); // We will receive an ack message - switched = 1; // No more switches until button is released + if (value != oldValue && value!=0) { + gw.saveState(CHILD_ID, state); + gw.send(msg.set(state==1?0:1), true); // Send and request ack + oldValue = value; } - previousVal = buttVal; } - -void setRelayStatus(message_s message) { - if (message.header.type==V_LIGHT) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE - state = atoi(message.data); +void incomingMessage(MyMessage message) { + Serial.println(gw.getInternalTemp()); + // We only expect one type of message from controller. But we better check anyway. + if (message.type==V_LIGHT) { // Change relay state - digitalWrite(RELAY_PIN, state==1?RELAY_ON:RELAY_OFF); + state = message.getBool(); + digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF); + // Store state in eeprom + gw.saveState(CHILD_ID, state); + // Write some debug info - Serial.print(message.header.messageType == M_ACK_VARIABLE ? "Button":"Gateway"); - Serial.print(" change. New state: "); - Serial.println(state == 1 ?"on":"off" ); - } + Serial.print("Incoming change for sensor:"); + Serial.print(message.sensor); + Serial.print(", New status: "); + Serial.println(message.getBool()); + } } + diff --git a/libraries/MySensors/examples/RelayingNode/RelayingNode.ino b/libraries/MySensors/examples/RelayingNode/RelayingNode.ino index 40864f7..95975b6 100644 --- a/libraries/MySensors/examples/RelayingNode/RelayingNode.ino +++ b/libraries/MySensors/examples/RelayingNode/RelayingNode.ino @@ -4,16 +4,16 @@ // gw.messageAvailable() frequently. This sketch should not // sleep. -#include +#include #include -#include #include -Relay gw; +MySensor gw; void setup() { - gw.begin(); + // The third argument enables relaying mode. + gw.begin(NULL, AUTO, true); //Send the sensor node sketch version information to the gateway gw.sendSketchInfo("Relaying Node", "1.0"); @@ -21,10 +21,7 @@ void setup() void loop() { - // By calling this regularely you route messages in the background - if (gw.messageAvailable()) { - - // Handleincoming message for this node here... - } + // By calling process() you route messages in the background + gw.process(); } diff --git a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino index 729694f..3fd555b 100644 --- a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino +++ b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino @@ -6,9 +6,8 @@ // Contribution by: Derek Macias -#include +#include #include -#include #include #include @@ -18,7 +17,7 @@ #define DETACH_DELAY 900 // Tune this to let your movement finish before detaching the servo #define CHILD_ID 10 // Id of the sensor child -Sensor gw; +MySensor gw; Servo myservo; // create servo object to control a servo // a maximum of eight servo objects can be created Sensor gw(9,10); unsigned long timeOfLastChange = 0; @@ -33,7 +32,7 @@ void setup() gw.sendSketchInfo("Servo", "1.0"); // Register all sensors to gw (they will be created as child devices) - gw.sendSensorPresentation(CHILD_ID, S_COVER); + gw.present(CHILD_ID, S_COVER); // Fetch servo state at startup gw.getStatus(CHILD_ID, V_DIMMER); From 8a9061aee73f9b88f8112c403b1021adb94c6d82 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 13 Jun 2014 20:19:48 +0200 Subject: [PATCH 256/326] The last examples converted to 1.4. --- .../EnergyMeterPulseSensor.ino | 2 +- .../examples/RelayActuator/RelayActuator.ino | 1 - .../examples/ServoActuator/ServoActuator.ino | 31 ++++---- .../SoilMoistSensor/SoilMoistSensor.ino | 23 +++--- .../WaterMeterPulseSensor.ino | 70 +++++++++---------- 5 files changed, 58 insertions(+), 69 deletions(-) diff --git a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino index 3a65c37..2c17df6 100644 --- a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino +++ b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino @@ -96,7 +96,7 @@ void loop() void incomingMessage(MyMessage message) { if (message.type==V_VAR1) { pulseCount = oldPulseCount = message.getLong(); - Serial.print("Last pulse count from gw:"); + Serial.print("Received last pulse count from gw:"); Serial.println(pulseCount); pcReceived = true; } diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index a850cce..f04b602 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -39,7 +39,6 @@ void loop() } void incomingMessage(MyMessage message) { - Serial.println(gw.getInternalTemp()); // We only expect one type of message from controller. But we better check anyway. if (message.type==V_LIGHT) { // Change relay state diff --git a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino index 3fd555b..b54aed2 100644 --- a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino +++ b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino @@ -18,6 +18,7 @@ #define CHILD_ID 10 // Id of the sensor child MySensor gw; +MyMessage msg(CHILD_ID, V_DIMMER); Servo myservo; // create servo object to control a servo // a maximum of eight servo objects can be created Sensor gw(9,10); unsigned long timeOfLastChange = 0; @@ -26,7 +27,8 @@ bool attachedServo = false; void setup() { - gw.begin(); + // Attach method for incoming messages + gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Servo", "1.0"); @@ -34,42 +36,37 @@ void setup() // Register all sensors to gw (they will be created as child devices) gw.present(CHILD_ID, S_COVER); - // Fetch servo state at startup - gw.getStatus(CHILD_ID, V_DIMMER); - setRelayStatus(gw.getMessage()); // Wait here until status message arrive from gw + // Request last servo state at startup + gw.request(CHILD_ID, V_DIMMER); } void loop() { - if (gw.messageAvailable()) { - // New messsage from gw - message_s message = gw.getMessage(); - setRelayStatus(message); - } + gw.process(); if (attachedServo && millis() - timeOfLastChange > DETACH_DELAY) { myservo.detach(); attachedServo = false; } } -void setRelayStatus(message_s message) { +void incomingMessage(MyMessage message) { myservo.attach(SERVO_DIGITAL_OUT_PIN); attachedServo = true; - if (message.header.type==V_DIMMER) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE - int val = atoi(message.data); + if (message.type==V_DIMMER) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE + int val = message.getInt(); myservo.write(SERVO_MAX + (SERVO_MIN-SERVO_MAX)/100 * val); // sets the servo position 0-180 // Write some debug info Serial.print("Servo changed. new state: "); Serial.println(val); - } else if (message.header.type==V_UP) { + } else if (message.type==V_UP) { Serial.println("Servo UP command"); myservo.write(SERVO_MIN); - gw.sendVariable(CHILD_ID, V_DIMMER, 100); - } else if (message.header.type==V_DOWN) { + gw.send(msg.set(100)); + } else if (message.type==V_DOWN) { Serial.println("Servo DOWN command"); myservo.write(SERVO_MAX); - gw.sendVariable(CHILD_ID, V_DIMMER, 0); - } else if (message.header.type==V_STOP) { + gw.send(msg.set(0)); + } else if (message.type==V_STOP) { Serial.println("Servo STOP command"); myservo.detach(); attachedServo = false; diff --git a/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino b/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino index 9cfb218..17a1fb5 100644 --- a/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino +++ b/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino @@ -1,15 +1,13 @@ -#include #include -#include #include -#include +#include #define DIGITAL_INPUT_SOIL_SENSOR 3 // Digital input did you attach your soil sensor. #define INTERRUPT DIGITAL_INPUT_SOIL_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) #define CHILD_ID 0 // Id of the sensor child -Sensor gw; -Sleep sleep; +MySensor gw; +MyMessage msg(CHILD_ID, V_TRIPPED); int lastSoilValue = -1; void setup() @@ -18,10 +16,10 @@ void setup() // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Soil Moisture Sensor", "1.0"); - - pinMode(DIGITAL_INPUT_SOIL_SENSOR, INPUT); // sets the soil sensor digital pin as input + // sets the soil sensor digital pin as input + pinMode(DIGITAL_INPUT_SOIL_SENSOR, INPUT); // Register all sensors to gw (they will be created as child devices) - gw.sendSensorPresentation(CHILD_ID, S_MOTION); + gw.present(CHILD_ID, S_MOTION); } void loop() @@ -30,14 +28,11 @@ void loop() int soilValue = digitalRead(DIGITAL_INPUT_SOIL_SENSOR); // 1 = Not triggered, 0 = In soil with water if (soilValue != lastSoilValue) { Serial.println(soilValue); - gw.sendVariable(CHILD_ID, V_TRIPPED, soilValue==0?"1":"0"); // Send the inverse to gw as tripped should be when no water in soil + gw.send(msg.set(soilValue==0?1:0)); // Send the inverse to gw as tripped should be when no water in soil lastSoilValue = soilValue; } - // Power down the radio. Note that the radio will get powered back up - // on the next write() call. - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepInterrupt(INTERRUPT,CHANGE); + // Power down the radio and arduino until digital input changes. + gw.sleep(INTERRUPT,CHANGE); } diff --git a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino index 3583ca8..26265ff 100644 --- a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino +++ b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino @@ -8,15 +8,10 @@ // sleepmode. So we cannot make this sensor sleep if we also want // to calculate/report flow. // -// Sensor on pin 3 - -#include -#include #include -#include #include -#include +#include #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!) #define PULSE_FACTOR 1000 // Nummber of blinks per m3 of your meter (One rotation/liter) @@ -26,14 +21,14 @@ #define CHILD_ID 5 // Id of the sensor child unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't want to spam the gateway. -Sensor gw; -Sleep sleep; +MySensor gw; double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter volatile unsigned long pulseCount = 0; volatile unsigned long lastBlink = 0; -volatile double flow = 0; +volatile double flow = 0; +boolean pcReceived = false; unsigned long oldPulseCount = 0; unsigned long newBlink = 0; double oldflow = 0; @@ -43,21 +38,24 @@ unsigned long lastSend; unsigned long lastPulse; unsigned long currentTime; boolean metric; +MyMessage flowMsg(CHILD_ID,V_FLOW); +MyMessage volumeMsg(CHILD_ID,V_VOLUME); +MyMessage pcMsg(CHILD_ID,V_VAR1); + void setup() { - gw.begin(); + gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Water Meter", "1.0"); // Register this device as Waterflow sensor - gw.sendSensorPresentation(CHILD_ID, S_WATER); + gw.present(CHILD_ID, S_WATER); // Fetch last known pulse count value from gw - pulseCount = oldPulseCount = atol(gw.getStatus(CHILD_ID, V_VAR1)); - //Serial.print("Last pulse count from gw:"); - //Serial.println(pulseCount); + gw.request(CHILD_ID, V_VAR1); + attachInterrupt(INTERRUPT, onPulse, RISING); lastSend = millis(); } @@ -65,16 +63,18 @@ void setup() void loop() { - currentTime = millis(); + gw.process(); + currentTime = millis(); // Only send values at a maximum frequency or woken up from sleep - if (SLEEP_MODE || currentTime - lastSend > 1000*SEND_FREQUENCY) { + bool sendTime = currentTime - lastSend > 1000*SEND_FREQUENCY; + if (pcReceived && (SLEEP_MODE || sendTime)) { // New flow value has been calculated if (!SLEEP_MODE && flow != oldflow) { // Check that we dont get unresonable large flow value. // could hapen when long wraps or false interrupt triggered if (flow<((unsigned long)MAX_FLOW)) { - gw.sendVariable(CHILD_ID, V_FLOW, flow, 2); // Send flow value to gw + gw.send(flowMsg.set(flow, 2)); // Send flow value to gw } //Serial.print("l/min:"); //Serial.println(flow); @@ -82,39 +82,39 @@ void loop() } // No Pulse count in 2min - - //Serial.print("currentTime"); - //Serial.println(currentTime); - //Serial.print("lastPulse"); - //Serial.println(lastPulse); - if(currentTime - lastPulse > 120000){ - flow = 0; - } + flow = 0; + } // Pulse count has changed if (pulseCount != oldPulseCount) { - gw.sendVariable(CHILD_ID, V_VAR1, pulseCount); // Send volumevalue to gw VAR1 + gw.send(pcMsg.set(pulseCount)); // Send volumevalue to gw VAR1 double volume = ((double)pulseCount/((double)PULSE_FACTOR)); oldPulseCount = pulseCount; - //Serial.print("Pulse count:"); - //Serial.println(pulseCount); if (volume != oldvolume) { - gw.sendVariable(CHILD_ID, V_VOLUME, volume, 3); // Send volume value to gw - //Serial.print("m3:"); - //Serial.println(volume, 3); + gw.send(volumeMsg.set(volume, 3)); // Send volume value to gw oldvolume = volume; } } lastSend = currentTime; + } else if (sendTime) { + // No count received. Try requesting it again + gw.request(CHILD_ID, V_VAR1); } if (SLEEP_MODE) { delay(300); //delay to allow serial to fully print before sleep - gw.powerDown(); - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(SEND_FREQUENCY * 1000); //sleep for: sleepTime + gw.sleep(SEND_FREQUENCY * 1000); + } +} + +void incomingMessage(MyMessage message) { + if (message.type==V_VAR1) { + pulseCount = oldPulseCount = message.getLong(); + Serial.print("Received last pulse count from gw:"); + Serial.println(pulseCount); + pcReceived = true; } } @@ -129,10 +129,8 @@ void onPulse() // Sometimes we get interrupt on RISING, 500000 = 0.5sek debounce ( max 120 l/min) return; } - flow = (60000000.0 /interval) / ppl; lastBlink = newBlink; - //Serial.println(flow, 4); } pulseCount++; } From 771401a3172bf417f50d33714c3deeb699b93de3 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 13 Jun 2014 21:20:30 +0200 Subject: [PATCH 257/326] Change version string --- libraries/MySensors/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/Version.h b/libraries/MySensors/Version.h index 8da4fa6..bc5edc4 100644 --- a/libraries/MySensors/Version.h +++ b/libraries/MySensors/Version.h @@ -6,6 +6,6 @@ #ifndef Version_h #define Version_h -#define LIBRARY_VERSION "1.3b3 (18848a2)" +#define LIBRARY_VERSION "1.4b1 (18848a2)" #endif From 9ddf442e42761c2e2437839b7bc95b334bc62edc Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 15 Jun 2014 17:10:27 +0200 Subject: [PATCH 258/326] Fixed CRC zero-padding. Thanks Yveaux. --- libraries/MySensors/MySensor.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index bdfa7ab..fb02828 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -432,11 +432,10 @@ uint8_t MySensor::crc8Message(MyMessage *message) { message->crc = 0; // fill unused space by zeroes for string data only - if(len>=0 && len < sizeof(message->data)-1) { - memset(&message->data[len], 0, sizeof(message->data) - 1 - len); + if(len>=0 && len < sizeof(message->data)) { + memset(&message->data[len], 0, sizeof(message->data) - len); } - for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) { data = ((uint8_t*)message)[loop_count]; From 90f79c97cda58751f3e7880e7e28b673e41833f7 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 16 Jun 2014 00:30:13 +0200 Subject: [PATCH 259/326] * Changed library to call by reference instead of using pointers. Makes code much cleaner. * RelayNodes renamed to RepeaterNode to distiguish this from RelayActuator. * Ack is now possible to request for all message types. Added ack-flag for some of the exposed internal messages and presentation message. --- libraries/MySensors/MyGateway.cpp | 16 +- libraries/MySensors/MyGateway.h | 4 +- libraries/MySensors/MyMessage.cpp | 74 ++++----- libraries/MySensors/MyMessage.h | 94 ++++++----- libraries/MySensors/MySensor.cpp | 151 +++++++++--------- libraries/MySensors/MySensor.h | 37 +++-- .../DallasTemperatureSensor.ino | 2 +- .../EnergyMeterPulseSensor.ino | 2 +- .../MySensors/examples/IrSensor/IrSensor.ino | 2 +- .../LightLuxSensor/LightLuxSensor.ino | 2 +- .../examples/RelayActuator/RelayActuator.ino | 2 +- .../RelayWithButtonActuator.ino | 3 +- .../RepeaterNode.ino} | 11 +- .../examples/ServoActuator/ServoActuator.ino | 2 +- .../WaterMeterPulseSensor.ino | 2 +- 15 files changed, 200 insertions(+), 204 deletions(-) rename libraries/MySensors/examples/{RelayingNode/RelayingNode.ino => RepeaterNode/RepeaterNode.ino} (53%) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index 25800f5..cf6abd4 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -28,9 +28,9 @@ MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, ui void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) { Serial.begin(BAUD_RATE); - relayMode = true; + repeaterMode = true; isGateway = true; - setupRelayMode(); + setupRepeaterMode(); if (inDataCallback != NULL) { useWriteCallback = true; @@ -157,7 +157,7 @@ void MyGateway::parseAndSend(char *commandBuffer) { msg.type = type; mSetCommand(msg,command); msg.set(value); - ok = sendRoute(&msg); + ok = sendRoute(msg); if (!ok) { errBlink(1); } @@ -178,7 +178,7 @@ void MyGateway::setInclusionMode(boolean newMode) { // Override normal validate to add error blink if crc check fails -uint8_t MyGateway::validate(MyMessage *message) { +uint8_t MyGateway::validate(MyMessage &message) { uint8_t res = MySensor::validate(message); if (res == VALIDATE_BAD_CRC) { errBlink(1); @@ -189,8 +189,8 @@ uint8_t MyGateway::validate(MyMessage *message) { void MyGateway::processRadioMessage() { if (process()) { // A new message was received from one of the sensors - MyMessage *message = getLastMessage(); - if (mGetCommandP(message) == C_PRESENTATION && inclusionMode) { + MyMessage message = getLastMessage(); + if (mGetCommand(message) == C_PRESENTATION && inclusionMode) { rxBlink(3); } else { rxBlink(1); @@ -215,8 +215,8 @@ void MyGateway::serial(const char *fmt, ... ) { } } -void MyGateway::serial(MyMessage *msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg->sender, msg->sensor, mGetCommandP(msg), msg->type, msg->getString(convBuf)); +void MyGateway::serial(MyMessage &msg) { + serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); } diff --git a/libraries/MySensors/MyGateway.h b/libraries/MySensors/MyGateway.h index e02b8e2..8b520e5 100644 --- a/libraries/MySensors/MyGateway.h +++ b/libraries/MySensors/MyGateway.h @@ -69,8 +69,8 @@ class MyGateway : public MySensor uint8_t pinEr; void serial(const char *fmt, ... ); - void serial(MyMessage *msg); - uint8_t validate(MyMessage *message); + void serial(MyMessage &msg); + uint8_t validate(MyMessage &message); void interruptStartInclusion(); void checkButtonTriggeredInclusion(); void setInclusionMode(boolean newMode); diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index d11c703..2ac6006 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -15,26 +15,26 @@ MyMessage::MyMessage(uint8_t _sensor, uint8_t _type) { /* Getters for payload converted to desired form */ -void* MyMessage::getCustom() { +void* MyMessage::getCustom() const { return (void *)data; } -char* MyMessage::getString() { +const char* MyMessage::getString() const { uint8_t payloadType = miGetPayloadType(); if (payloadType == P_STRING) { - data[miGetLength()] = '\0'; return data; } else { return NULL; } } -char* MyMessage::getString(char *buffer) { +char* MyMessage::getString(char *buffer) const { uint8_t payloadType = miGetPayloadType(); if (payloadType == P_STRING) { - data[miGetLength()] = '\0'; - return data; + strncpy(buffer, data, miGetLength()); + buffer[miGetLength()] = '\0'; + return buffer; } else if (buffer != NULL) { switch(payloadType) { case P_BYTE: @@ -65,7 +65,7 @@ char* MyMessage::getString(char *buffer) { } } -uint8_t MyMessage::getByte() { +uint8_t MyMessage::getByte() const { switch(miGetPayloadType()) { case P_BYTE: return data[0]; @@ -76,15 +76,15 @@ uint8_t MyMessage::getByte() { } } -bool MyMessage::getBool() { +bool MyMessage::getBool() const { return !(data[0]-'0'==0); } -double MyMessage::getDouble() { +double MyMessage::getDouble() const { return strtod(data, NULL); } -long MyMessage::getLong() { +long MyMessage::getLong() const { switch(miGetPayloadType()) { case P_LONG32: return lValue; @@ -95,7 +95,7 @@ long MyMessage::getLong() { } } -unsigned long MyMessage::getULong() { +unsigned long MyMessage::getULong() const { switch(miGetPayloadType()) { case P_ULONG32: return ulValue; @@ -106,7 +106,7 @@ unsigned long MyMessage::getULong() { } } -int MyMessage::getInt() { +int MyMessage::getInt() const { switch(miGetPayloadType()) { case P_INT16: return iValue; @@ -117,7 +117,7 @@ int MyMessage::getInt() { } } -unsigned int MyMessage::getUInt() { +unsigned int MyMessage::getUInt() const { switch(miGetPayloadType()) { case P_UINT16: return uiValue; @@ -130,85 +130,85 @@ unsigned int MyMessage::getUInt() { } -MyMessage* MyMessage::setType(uint8_t _type) { +MyMessage& MyMessage::setType(uint8_t _type) { type = _type; - return this; + return *this; } -MyMessage* MyMessage::setSensor(uint8_t _sensor) { +MyMessage& MyMessage::setSensor(uint8_t _sensor) { sensor = _sensor; - return this; + return *this; } -MyMessage* MyMessage::setDestination(uint8_t _destination) { +MyMessage& MyMessage::setDestination(uint8_t _destination) { destination = _destination; - return this; + return *this; } // Set payload -MyMessage* MyMessage::set(void* value, uint8_t length) { +MyMessage& MyMessage::set(void* value, uint8_t length) { miSetPayloadType(P_CUSTOM); miSetLength(length); memcpy(data, value, min(length, MAX_PAYLOAD)); - return this; + return *this; } -MyMessage* MyMessage::set(const char* value) { +MyMessage& MyMessage::set(const char* value) { uint8_t length = strlen(value); miSetLength(length); miSetPayloadType(P_STRING); strncpy(data, value, min(length, MAX_PAYLOAD)); - return this; + return *this; } -MyMessage* MyMessage::set(uint8_t value) { +MyMessage& MyMessage::set(uint8_t value) { miSetLength(1); miSetPayloadType(P_BYTE); data[0] = value; - return this; + return *this; } -MyMessage* MyMessage::set(bool value) { +MyMessage& MyMessage::set(bool value) { miSetLength(1); miSetPayloadType(P_STRING); data[0] = value?'1':'0'; - return this; + return *this; } -MyMessage* MyMessage::set(double value, uint8_t decimals) { +MyMessage& MyMessage::set(double value, uint8_t decimals) { dtostrf(value,2,decimals,data); miSetLength(strlen(data)); miSetPayloadType(P_STRING); - return this; + return *this; } -MyMessage* MyMessage::set(unsigned long value) { +MyMessage& MyMessage::set(unsigned long value) { miSetPayloadType(P_ULONG32); miSetLength(4); ulValue = value; - return this; + return *this; } -MyMessage* MyMessage::set(long value) { +MyMessage& MyMessage::set(long value) { miSetPayloadType(P_LONG32); miSetLength(4); lValue = value; - return this; + return *this; } -MyMessage* MyMessage::set(unsigned int value) { +MyMessage& MyMessage::set(unsigned int value) { miSetPayloadType(P_UINT16); miSetLength(2); uiValue = value; - return this; + return *this; } -MyMessage* MyMessage::set(int value) { +MyMessage& MyMessage::set(int value) { miSetPayloadType(P_INT16); miSetLength(2); iValue = value; - return this; + return *this; } diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index ab6da6b..6bebda3 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -24,9 +24,8 @@ typedef enum { C_PRESENTATION = 0, C_SET = 1, C_REQ = 2, - C_SET_WITH_ACK = 3, - C_INTERNAL = 4, - C_STREAM = 5 // For Firmaware and other larger chunks of data that need to be divided into pieces. + C_INTERNAL = 3, + C_STREAM = 4 // For Firmware and other larger chunks of data that need to be divided into pieces. } command; // Type of sensor data (for set/req/ack messages) @@ -49,7 +48,7 @@ typedef enum { typedef enum { S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, 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_RELAY, S_LOCK, S_IR, S_WATER + S_ARDUINO_REPEATER_NODE, S_LOCK, S_IR, S_WATER } sensor; // Type of data stream (for streamed message) @@ -83,29 +82,23 @@ typedef enum { // Getters/setters for special fields in header #define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 3) #define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) -#define mSetCommand(_msg,_command) BF_SET(_msg.command_payload, _command, 0, 4) -#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_payload, _pt, 4, 4) -#define mSetVersionP(_msg,_version) BF_SET(_msg->version_length, _version, 0, 3) -#define mSetLengthP(_msg,_length) BF_SET(_msg->version_length, _length, 3, 5) -#define mSetCommandP(_msg,_command) BF_SET(_msg->command_payload, _command, 0, 4) -#define mSetPayloadTypeP(_msg, _pt) BF_SET(_msg->command_payload, _pt, 4, 4) +#define mSetCommand(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 0, 3) +#define mSetAck(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 3, 1) +#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 4, 4) #define mGetVersion(_msg) BF_GET(_msg.version_length, 0, 3) #define mGetLength(_msg) BF_GET(_msg.version_length, 3, 5) -#define mGetCommand(_msg) BF_GET(_msg.command_payload, 0, 4) -#define mGetPayloadType(_msg) BF_GET(_msg.command_payload, 4, 4) -#define mGetVersionP(_msg) BF_GET(_msg->version_length, 0, 3) -#define mGetLengthP(_msg) BF_GET(_msg->version_length, 3, 5) -#define mGetCommandP(_msg) BF_GET(_msg->command_payload, 0, 4) -#define mGetPayloadTypeP(_msg) BF_GET(_msg->command_payload, 4, 4) +#define mGetCommand(_msg) BF_GET(_msg.command_ack_payload, 0, 3) +#define mGetAck(_msg) BF_GET(_msg.command_ack_payload, 3, 1) +#define mGetPayloadType(_msg) BF_GET(_msg.command_ack_payload, 4, 4) -// internal access for special fileds -#define miGetPayloadType() BF_GET(command_payload, 4, 4) +// internal access for special fields +#define miGetPayloadType() BF_GET(command_ack_payload, 4, 4) #define miGetLength() BF_GET(version_length, 3, 5) #define miSetLength(_length) BF_SET(version_length, _length, 3, 5) -#define miSetPayloadType(_pt) BF_SET(command_payload, _pt, 4, 4) +#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 4, 4) class MyMessage @@ -116,47 +109,50 @@ public: MyMessage(uint8_t sensor, uint8_t type); - char* getString(); /** * 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. */ - char* getString(char *buffer); - void* getCustom(); - uint8_t getByte(); - bool getBool(); - double getDouble(); - long getLong(); - unsigned long getULong(); - int getInt(); - unsigned int getUInt(); + char* getString(char *buffer) const; + const char* getString() const; + void* getCustom() const; + uint8_t getByte() const; + bool getBool() const; + double getDouble() const; + long getLong() const; + unsigned long getULong() const; + int getInt() const; + unsigned int getUInt() const; // Setters for building message "on the fly" - MyMessage* setType(uint8_t type); - MyMessage* setSensor(uint8_t sensor); - MyMessage* setDestination(uint8_t destination); + MyMessage& setType(uint8_t type); + MyMessage& setSensor(uint8_t sensor); + MyMessage& setDestination(uint8_t destination); // Setters for payload - MyMessage* set(void* payload, uint8_t length); - MyMessage* set(const char* value); - MyMessage* set(uint8_t value); - MyMessage* set(bool value); - MyMessage* set(double value, uint8_t decimals); - MyMessage* set(unsigned long value); - MyMessage* set(long value); - MyMessage* set(unsigned int value); - MyMessage* set(int value); + MyMessage& set(void* payload, uint8_t length); + MyMessage& set(const char* value); + MyMessage& set(uint8_t value); + MyMessage& set(bool value); + MyMessage& set(double value, uint8_t decimals); + MyMessage& set(unsigned long value); + MyMessage& set(long value); + MyMessage& set(unsigned int value); + MyMessage& set(int value); - uint8_t version_length; // 3 bit - Protocol version + 5 bit - Length of payload - uint8_t command_payload; // 4 bit - Command type + 4 bit - Payload data type - uint8_t type; // 8 bit - Type varies depending on command - uint8_t sensor; // 8 bit - Id of sensor that this message concerns. - uint8_t sender; // 8 bit - Id of sender node - uint8_t last; // 8 bit - Id of last node this message passed - uint8_t destination; // 8 bit - Id of destination node - uint8_t crc; // 8 bit - Message checksum + uint8_t version_length; // 3 bit - Protocol version + // 5 bit - Length of payload + uint8_t command_ack_payload; // 3 bit - Command type + // 1 bit - Indicator that receiver should send an ack back. + // 4 bit - Payload data type + uint8_t type; // 8 bit - Type varies depending on command + uint8_t sensor; // 8 bit - Id of sensor that this message concerns. + uint8_t sender; // 8 bit - Id of sender node + uint8_t last; // 8 bit - Id of last node this message passed + uint8_t destination; // 8 bit - Id of destination node + uint8_t crc; // 8 bit - Message checksum // Each message can transfer a payload. We add one extra byte for string // terminator \0 to be "printable" this is not transferred OTA diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index fb02828..44901a2 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -14,12 +14,13 @@ // Inline function and macros -inline MyMessage* build (MyMessage* msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) { - msg->sender = sender; - msg->destination = destination; - msg->sensor = sensor; - msg->type = type; - mSetCommandP(msg,command); +inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { + msg.sender = sender; + msg.destination = destination; + msg.sensor = sensor; + msg.type = type; + mSetCommand(msg,command); + mSetAck(msg,enableAck); return msg; } @@ -28,14 +29,14 @@ MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } -void MySensor::begin(void (*_msgCallback)(MyMessage), uint8_t _nodeId, boolean _relayMode, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { +void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { Serial.begin(BAUD_RATE); isGateway = false; - relayMode = _relayMode; + repeaterMode = _repeaterMode; msgCallback = _msgCallback; - if (relayMode) { - setupRelayMode(); + if (repeaterMode) { + setupRepeaterMode(); } setupRadio(paLevel, channel, dataRate); @@ -60,19 +61,19 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), uint8_t _nodeId, boolean _ requestNodeId(); } - debug(PSTR("%s started, id %d\n"), relayMode?"relay":"sensor", nc.nodeId); + debug(PSTR("%s started, id %d\n"), repeaterMode?"repeater":"sensor", nc.nodeId); // Open reading pipe for messages directed to this node (set write pipe to same) RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId)); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); // Send presentation for this radio node (attach - present(NODE_SENSOR_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE); + present(NODE_SENSOR_ID, repeaterMode? S_ARDUINO_REPEATER_NODE : S_ARDUINO_NODE); // Send a configuration exchange request to controller // Node sends parent node. Controller answers with latest node configuration // which is picked up in process() - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG)->set(nc.parentNodeId)); + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(nc.parentNodeId)); // Wait configuration reply. waitForReply(); @@ -102,7 +103,7 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); } -void MySensor::setupRelayMode(){ +void MySensor::setupRepeaterMode(){ childNodeTable = new uint8_t[256]; eeprom_read_block((void*)childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); } @@ -119,7 +120,7 @@ ControllerConfig MySensor::getConfig() { void MySensor::requestNodeId() { debug(PSTR("req node id\n")); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST)->set("")); + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("")); waitForReply(); } @@ -131,8 +132,8 @@ void MySensor::findParentNode() { nc.distance = 255; // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - build(&msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING)->set(""); - sendWrite(BROADCAST_ADDRESS, &msg, true); + build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set(""); + sendWrite(BROADCAST_ADDRESS, msg, true); // Wait for ping response. waitForReply(); @@ -146,26 +147,26 @@ void MySensor::waitForReply() { } } -boolean MySensor::sendRoute(MyMessage *message) { +boolean MySensor::sendRoute(MyMessage &message) { // Make sure to process any incoming messages before sending (could this end up in recursive loop?) // process(); - bool isInternal = mGetCommandP(message) == C_INTERNAL; + bool isInternal = mGetCommand(message) == C_INTERNAL; // If we still don't have any node id, re-request and skip this message. - if (nc.nodeId == AUTO && !(isInternal && message->type == I_ID_REQUEST)) { + if (nc.nodeId == AUTO && !(isInternal && message.type == I_ID_REQUEST)) { requestNodeId(); return false; } - if (relayMode) { - uint8_t dest = message->destination; + if (repeaterMode) { + uint8_t dest = message.destination; uint8_t route = getChildRoute(dest); if (route>GATEWAY_ADDRESS && routetype == I_ID_RESPONSE && dest==BROADCAST_ADDRESS) { + } else if (isInternal && message.type == I_ID_RESPONSE && dest==BROADCAST_ADDRESS) { // Node has not yet received any id. We need to send it // by doing a broadcast sending, return sendWrite(BROADCAST_ADDRESS, message, true); @@ -192,59 +193,56 @@ boolean MySensor::sendRoute(MyMessage *message) { return false; } -boolean MySensor::sendWrite(uint8_t next, MyMessage *message, bool broadcast) { - uint8_t length = mGetLengthP(message); - message->last = nc.nodeId; - mSetVersionP(message, PROTOCOL_VERSION); - message->crc = crc8Message(message); +boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) { + uint8_t length = mGetLength(message); + message.last = nc.nodeId; + mSetVersion(message, PROTOCOL_VERSION); + message.crc = crc8Message(message); // Make sure radio has powered up RF24::powerUp(); RF24::stopListening(); RF24::openWritingPipe(TO_ADDR(next)); - bool ok = RF24::write(message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); + bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); RF24::startListening(); debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), - message->sender,message->last, next, message->destination, message->sensor, mGetCommandP(message), message->type, ok?"ok":"fail", message->getString(convBuf)); + message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, ok?"ok":"fail", message.getString(convBuf)); return ok; } -bool MySensor::send(MyMessage *message, bool enableAck) { - message->sender = nc.nodeId; - if (enableAck) { - mSetCommandP(message,C_SET_WITH_ACK); - } else { - mSetCommandP(message,C_SET); - } +bool MySensor::send(MyMessage &message, bool enableAck) { + message.sender = nc.nodeId; + mSetCommand(message,C_SET); + mSetAck(message,enableAck); return sendRoute(message); } -void MySensor::sendBatteryLevel(uint8_t value) { - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL)->set(value)); +void MySensor::sendBatteryLevel(uint8_t value, bool enableAck) { + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value)); } -void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType)->set(LIBRARY_VERSION)); +void MySensor::present(uint8_t childSensorId, uint8_t sensorType, bool enableAck) { + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(LIBRARY_VERSION)); } -void MySensor::sendSketchInfo(const char *name, const char *version) { +void MySensor::sendSketchInfo(const char *name, const char *version, bool enableAck) { if (name != NULL) { - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME)->set(name)); + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name)); } if (version != NULL) { - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION)->set(version)); + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version)); } } void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - sendRoute(build(&msg, nc.nodeId, destination, childSensorId, C_REQ, variableType)); + sendRoute(build(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType, false)); } void MySensor::requestTime(void (* _timeCallback)(unsigned long)) { timeCallback = _timeCallback; - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME)); + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false)); } @@ -259,7 +257,7 @@ boolean MySensor::process() { RF24::read(&msg, len); RF24::writeAckPayload(pipe,&pipe, 1 ); - uint8_t valid = validate(&msg); + uint8_t valid = validate(msg); boolean ok = valid == VALIDATE_OK; // Make sure string gets terminated ok for full string messages. @@ -276,16 +274,26 @@ boolean MySensor::process() { uint8_t last = msg.last; uint8_t destination = msg.destination; - if (relayMode && command == C_INTERNAL && type == I_PING) { + if (repeaterMode && command == C_INTERNAL && type == I_PING) { // Relaying nodes should always answer ping messages // Wait a random delay of 0-2 seconds to minimize collision // between ping ack messages from other relaying nodes delay(millis() & 0x3ff); - sendWrite(sender, build(&msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK)->set(nc.distance), true); + sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK, false).set(nc.distance), true); return false; } else if (destination == nc.nodeId) { + // Check if sender requests an ack back. + if (mGetAck(msg)) { + // Copy message + ack = msg; + mSetAck(ack,false); // Reply without ack flag (otherwise we would end up in an eternal loop) + ack.sender = nc.nodeId; + ack.destination = msg.sender; + sendRoute(ack); + } + // This message is addressed to this node - if (relayMode && last != nc.parentNodeId) { + if (repeaterMode && last != nc.parentNodeId) { // Message is from one of the child nodes. Add it to routing table. addChildRoute(sender, last); } @@ -334,13 +342,13 @@ boolean MySensor::process() { } break; case I_CHILDREN: - if (relayMode && msg.getByte() == 'C') { + if (repeaterMode && msg.getByte() == 'C') { // Clears child relay data for this node debug(PSTR("rd=clear\n")); for (uint8_t i=0;i< sizeof(childNodeTable); i++) { removeChildRoute(i); } - sendRoute(build(&msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN)->set("")); + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); } break; case I_TIME: @@ -352,24 +360,13 @@ boolean MySensor::process() { return false; } } - // Check if sender requests an ack back. - if (command == C_SET_WITH_ACK) { - // Copy message - ack = msg; - mSetCommand(ack,C_SET); - ack.sender = nc.nodeId; - ack.destination = msg.sender; - sendRoute(&ack); - // The library user should not need to care about this ack request. Just treat it as a normal SET. - mSetCommand(msg,C_SET); - } // Call incoming message callback if available if (msgCallback != NULL) { msgCallback(msg); } // Return true if message was addressed for this node... return true; - } else if (relayMode && pipe == CURRENT_NODE_PIPE) { + } else if (repeaterMode && pipe == CURRENT_NODE_PIPE) { // We should try to relay this message to another node uint8_t route = getChildRoute(msg.destination); @@ -385,7 +382,7 @@ boolean MySensor::process() { // We're node C, Message comes from A and has destination D // // lookup route in table and send message there - sendWrite(route, &msg); + sendWrite(route, msg); } else { // A message comes from a child node and we have no // route for it. @@ -399,7 +396,7 @@ boolean MySensor::process() { // Message should be passed to node A (this nodes relay) // This message should be routed back towards sensor net gateway - sendWrite(nc.parentNodeId, &msg); + sendWrite(nc.parentNodeId, msg); // Add this child to our "routing table" if it not already exist addChildRoute(sender, last); } @@ -411,16 +408,16 @@ boolean MySensor::process() { } -MyMessage* MySensor::getLastMessage() { - return &msg; +MyMessage& MySensor::getLastMessage() { + return msg; } /* * calculate CRC8 on MyMessage data taking care of data structure and protocol version */ -uint8_t MySensor::crc8Message(MyMessage *message) { - uint8_t len = mGetLengthP(message); +uint8_t MySensor::crc8Message(MyMessage &message) { + uint8_t len = mGetLength(message); uint8_t crc = 0x00; uint8_t loop_count; uint8_t bit_counter; @@ -429,16 +426,16 @@ uint8_t MySensor::crc8Message(MyMessage *message) { uint8_t number_of_bytes_to_read = (uint8_t)sizeof(MyMessage); // Must set crc to a constant value. - message->crc = 0; + message.crc = 0; // fill unused space by zeroes for string data only - if(len>=0 && len < sizeof(message->data)) { - memset(&message->data[len], 0, sizeof(message->data) - len); + if(len>=0 && len < sizeof(message.data)) { + memset(&message.data[len], 0, sizeof(message.data) - len); } for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) { - data = ((uint8_t*)message)[loop_count]; + data = ((uint8_t*)&message)[loop_count]; bit_counter = 8; do { @@ -461,11 +458,11 @@ uint8_t MySensor::crc8Message(MyMessage *message) { } -uint8_t MySensor::validate(MyMessage *message) { - uint8_t oldCrc = message->crc; +uint8_t MySensor::validate(MyMessage &message) { + uint8_t oldCrc = message.crc; uint8_t newCrc = crc8Message(message); - if(!(mGetVersionP(message) == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; + if(!(mGetVersion(message) == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; return VALIDATE_OK; diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 2c1a025..15d8e62 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -95,13 +95,13 @@ class MySensor : public RF24 * Call this in setup(), before calling any other sensor net library methods. * @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL. * @param nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. - * @param relayMode Activate relay mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false + * @param repeaterMode Activate repeater mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX * @param channel Radio channel. Default is channel 76 * @param dataRate Radio transmission speed. Default RF24_1MBPS */ - void begin(void (* msgCallback)(MyMessage)=NULL, uint8_t nodeId=AUTO, boolean relayMode=false, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); + void begin(void (* msgCallback)(const MyMessage &)=NULL, uint8_t nodeId=AUTO, boolean repeaterMode=false, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); /** * Return the nodes nodeId. @@ -113,16 +113,19 @@ class MySensor : public RF24 * 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 Message.h. + * @param sensorType The sensor type. See sensor typedef in MyMessage.h. + * @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. */ - void present(uint8_t sensorId, uint8_t sensorType); + void present(uint8_t sensorId, uint8_t sensorType, bool ack=false); /** * Sends sketch meta information to the gateway. Not mandatory but a nice thing to do. * @param name String containing a short Sketch name or NULL if not applicable * @param version String containing a short Sketch version or NULL if not applicable + * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. + * */ - void sendSketchInfo(const char *name, const char *version); + void sendSketchInfo(const char *name, const char *version, bool ack=false); /** * Sends a message to gateway or one of the other nodes in the radio network @@ -131,19 +134,21 @@ class MySensor : public RF24 * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. * @return true Returns true if message reached the first stop on its way to destination. */ - bool send(MyMessage *msg, bool ack=false); + bool send(MyMessage &msg, bool ack=false); /** * Send this nodes battery level to gateway. * @param level Level between 0-100(%) + * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. + * */ - void sendBatteryLevel(uint8_t level); + void sendBatteryLevel(uint8_t level, bool ack=false); /** * Requests a value from gateway or some other sensor in the radio network. * Make sure to add callback-method in begin-method to handle request responses. * - * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. + * @param childSensorId The unique child id for the different sensors connected to this Arduino. 0-254. * @param variableType The variableType to fetch * @param destination The nodeId of other node in radio network. Default is gateway */ @@ -192,7 +197,7 @@ class MySensor : public RF24 /** * Returns the last received message */ - MyMessage* getLastMessage(void); + MyMessage& getLastMessage(void); /** * Sleep (PowerDownMode) the Arduino and radio. Wake up on timer. @@ -229,16 +234,16 @@ class MySensor : public RF24 protected: NodeConfig nc; // Essential settings for node to work ControllerConfig cc; // Configuration coming from controller - bool relayMode; + bool repeaterMode; bool isGateway; MyMessage msg; // Buffer for incoming messages. MyMessage ack; // Buffer for ack messages. - void setupRelayMode(); + void setupRepeaterMode(); void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); - boolean sendRoute(MyMessage *message); - boolean sendWrite(uint8_t dest, MyMessage *message, bool broadcast=false); - uint8_t validate(MyMessage *message); + boolean sendRoute(MyMessage &message); + boolean sendWrite(uint8_t dest, MyMessage &message, bool broadcast=false); + uint8_t validate(MyMessage &message); private: #ifdef DEBUG @@ -247,12 +252,12 @@ class MySensor : public RF24 uint8_t failedTransmissions; uint8_t *childNodeTable; // In memory buffer for routing information to other nodes. also stored in EEPROM void (*timeCallback)(unsigned long); // Callback for requested time messages - void (*msgCallback)(MyMessage); // Callback for incoming messages from other nodes and gateway. + void (*msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway. void waitForReply(); void requestNodeId(); void findParentNode(); - uint8_t crc8Message(MyMessage *message); + uint8_t crc8Message(MyMessage &message); uint8_t getChildRoute(uint8_t childId); void addChildRoute(uint8_t childId, uint8_t route); void removeChildRoute(uint8_t childId); diff --git a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino index 9a0282c..047068e 100644 --- a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino +++ b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino @@ -57,7 +57,7 @@ void loop() if (lastTemperature[i] != temperature && temperature != -127.00) { // Send in the new temperature - gw.send(msg.setSensor(i)->set(temperature,1)); + gw.send(msg.setSensor(i).set(temperature,1)); lastTemperature[i]=temperature; } } diff --git a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino index 2c17df6..f8836ff 100644 --- a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino +++ b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino @@ -93,7 +93,7 @@ void loop() } } -void incomingMessage(MyMessage message) { +void incomingMessage(const MyMessage &message) { if (message.type==V_VAR1) { pulseCount = oldPulseCount = message.getLong(); Serial.print("Received last pulse count from gw:"); diff --git a/libraries/MySensors/examples/IrSensor/IrSensor.ino b/libraries/MySensors/examples/IrSensor/IrSensor.ino index a34e024..4505d48 100644 --- a/libraries/MySensors/examples/IrSensor/IrSensor.ino +++ b/libraries/MySensors/examples/IrSensor/IrSensor.ino @@ -54,7 +54,7 @@ void loop() -void incomingMessage(MyMessage message) { +void incomingMessage(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. if (message.type==V_LIGHT) { int incomingRelayStatus = message.getInt(); diff --git a/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino b/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino index f834f0c..d74a9d7 100644 --- a/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino +++ b/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino @@ -29,7 +29,7 @@ unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) BH1750 lightSensor; MySensor gw; MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL); -int lastlux; +uint16_t lastlux; void setup() { diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index f04b602..0e4994d 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -38,7 +38,7 @@ void loop() gw.process(); } -void incomingMessage(MyMessage message) { +void incomingMessage(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. if (message.type==V_LIGHT) { // Change relay state diff --git a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino index 9aed1b4..d242988 100644 --- a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino +++ b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino @@ -71,8 +71,7 @@ void loop() -void incomingMessage(MyMessage message) { - Serial.println(gw.getInternalTemp()); +void incomingMessage(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. if (message.type==V_LIGHT) { // Change relay state diff --git a/libraries/MySensors/examples/RelayingNode/RelayingNode.ino b/libraries/MySensors/examples/RepeaterNode/RepeaterNode.ino similarity index 53% rename from libraries/MySensors/examples/RelayingNode/RelayingNode.ino rename to libraries/MySensors/examples/RepeaterNode/RepeaterNode.ino index 95975b6..e023176 100644 --- a/libraries/MySensors/examples/RelayingNode/RelayingNode.ino +++ b/libraries/MySensors/examples/RepeaterNode/RepeaterNode.ino @@ -1,8 +1,7 @@ -// Example sketch showing how to create a node thay relays messages +// Example sketch showing how to create a node thay repeates messages // from nodes far from gateway back to gateway. -// Important here is that node uses Relay-class and calls -// gw.messageAvailable() frequently. This sketch should not -// sleep. +// It is important that nodes that has enabled repeater mode calls +// gw.preocess() frequently. This node should never sleep. #include #include @@ -12,11 +11,11 @@ MySensor gw; void setup() { - // The third argument enables relaying mode. + // The third argument enables repeater mode. gw.begin(NULL, AUTO, true); //Send the sensor node sketch version information to the gateway - gw.sendSketchInfo("Relaying Node", "1.0"); + gw.sendSketchInfo("Repeater Node", "1.0"); } void loop() diff --git a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino index b54aed2..a0b8853 100644 --- a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino +++ b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino @@ -49,7 +49,7 @@ void loop() } } -void incomingMessage(MyMessage message) { +void incomingMessage(const MyMessage &message) { myservo.attach(SERVO_DIGITAL_OUT_PIN); attachedServo = true; if (message.type==V_DIMMER) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE diff --git a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino index 26265ff..352900c 100644 --- a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino +++ b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino @@ -109,7 +109,7 @@ void loop() } } -void incomingMessage(MyMessage message) { +void incomingMessage(const MyMessage &message) { if (message.type==V_VAR1) { pulseCount = oldPulseCount = message.getLong(); Serial.print("Received last pulse count from gw:"); From c332667a8dcd75d9e95ab6b010a617e3e94c3d7b Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Tue, 17 Jun 2014 22:16:38 +0200 Subject: [PATCH 260/326] RF24 moved to utility folder of library. This results in one less include in sketches! Serial flush in sleep method also means one less code in examples. Removed set(bool) in message. Not needed. getBool just calls getInt __attribute__((packed)) for RPI compability Print more info in send/read debug prints. Get internalTemp returns full degrees and uses longer Vref delay Fix debug print gateway prepend (wrong command/type) Fixed problem in RelayWithButton example. Now working with ack in both directions. Serial protocol changed (incoming serial message have an new ack-parameter) --- SerialGateway/SerialGateway.ino | 2 - libraries/MySensors/MyGateway.cpp | 11 +- libraries/MySensors/MyMessage.cpp | 11 +- libraries/MySensors/MyMessage.h | 9 +- libraries/MySensors/MySensor.cpp | 35 +- libraries/MySensors/MySensor.h | 15 +- .../BatteryPoweredSensor.ino | 6 +- .../BinarySwitchSensor/BinarySwitchSensor.ino | 5 +- .../DallasTemperatureSensor.ino | 7 +- .../DistanceSensor/DistanceSensor.ino | 9 +- .../EnergyMeterPulseSensor.ino | 7 +- .../HumiditySensor/HumiditySensor.ino | 7 +- .../MySensors/examples/IrSensor/IrSensor.ino | 2 - .../LightLuxSensor/LightLuxSensor.ino | 6 +- .../examples/LightSensor/LightSensor.ino | 7 +- .../examples/MotionSensor/MotionSensor.ino | 4 +- .../PressureSensor/PressureSensor.ino | 9 +- .../examples/RelayActuator/RelayActuator.ino | 4 +- .../RelayWithButtonActuator.ino | 29 +- .../examples/RepeaterNode/RepeaterNode.ino | 1 - .../examples/ServoActuator/ServoActuator.ino | 2 - .../SoilMoistSensor/SoilMoistSensor.ino | 1 - .../TimeAwareSensor/TimeAwareSensor.ino | 1 - .../WaterMeterPulseSensor.ino | 6 +- .../MySensors/{ => utility}/LowPower.cpp | 0 libraries/MySensors/{ => utility}/LowPower.h | 0 libraries/MySensors/utility/RF24.cpp | 1228 +++++++++++++++++ libraries/MySensors/utility/RF24.h | 1132 +++++++++++++++ libraries/MySensors/utility/RF24_config.h | 129 ++ libraries/MySensors/utility/nRF24L01.h | 127 ++ 30 files changed, 2691 insertions(+), 121 deletions(-) rename libraries/MySensors/{ => utility}/LowPower.cpp (100%) rename libraries/MySensors/{ => utility}/LowPower.h (100%) create mode 100644 libraries/MySensors/utility/RF24.cpp create mode 100644 libraries/MySensors/utility/RF24.h create mode 100644 libraries/MySensors/utility/RF24_config.h create mode 100644 libraries/MySensors/utility/nRF24L01.h diff --git a/SerialGateway/SerialGateway.ino b/SerialGateway/SerialGateway.ino index c245b92..7eca35f 100644 --- a/SerialGateway/SerialGateway.ino +++ b/SerialGateway/SerialGateway.ino @@ -22,8 +22,6 @@ */ #include -#include -#include #include #include #include diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index cf6abd4..f3c7936 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -114,10 +114,11 @@ void MyGateway::parseAndSend(char *commandBuffer) { uint8_t sensor = 0; uint8_t command = 0; uint8_t type = 0; + uint8_t ack = 0; // Extract command data coming on serial line for (str = strtok_r(commandBuffer, ";", &p); // split using semicolon - str && i < 5; // loop while str is not null an max 5 times + str && i < 6; // loop while str is not null an max 5 times str = strtok_r(NULL, ";", &p) // get subsequent tokens ) { switch (i) { @@ -130,10 +131,13 @@ void MyGateway::parseAndSend(char *commandBuffer) { case 2: // Message type command = atoi(str); break; - case 3: // Data type + case 3: // Should we request ack from destination? + ack = atoi(str); + break; + case 4: // Data type type = atoi(str); break; - case 4: // Variable value + case 5: // Variable value value = str; break; } @@ -156,6 +160,7 @@ void MyGateway::parseAndSend(char *commandBuffer) { msg.sensor = sensor; msg.type = type; mSetCommand(msg,command); + mSetAck(msg,ack==0?false:true); msg.set(value); ok = sendRoute(msg); if (!ok) { diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 2ac6006..96ae96e 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -32,8 +32,7 @@ char* MyMessage::getString(char *buffer) const { uint8_t payloadType = miGetPayloadType(); if (payloadType == P_STRING) { - strncpy(buffer, data, miGetLength()); - buffer[miGetLength()] = '\0'; + strcpy(buffer, data); return buffer; } else if (buffer != NULL) { switch(payloadType) { @@ -77,7 +76,7 @@ uint8_t MyMessage::getByte() const { } bool MyMessage::getBool() const { - return !(data[0]-'0'==0); + return getInt(); } double MyMessage::getDouble() const { @@ -169,12 +168,6 @@ MyMessage& MyMessage::set(uint8_t value) { return *this; } -MyMessage& MyMessage::set(bool value) { - miSetLength(1); - miSetPayloadType(P_STRING); - data[0] = value?'1':'0'; - return *this; -} MyMessage& MyMessage::set(double value, uint8_t decimals) { dtostrf(value,2,decimals,data); diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 6bebda3..08acf63 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -82,12 +82,14 @@ typedef enum { // Getters/setters for special fields in header #define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 3) #define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) + #define mSetCommand(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 0, 3) #define mSetAck(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 3, 1) #define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 4, 4) #define mGetVersion(_msg) BF_GET(_msg.version_length, 0, 3) #define mGetLength(_msg) BF_GET(_msg.version_length, 3, 5) + #define mGetCommand(_msg) BF_GET(_msg.command_ack_payload, 0, 3) #define mGetAck(_msg) BF_GET(_msg.command_ack_payload, 3, 1) #define mGetPayloadType(_msg) BF_GET(_msg.command_ack_payload, 4, 4) @@ -134,7 +136,6 @@ public: MyMessage& set(void* payload, uint8_t length); MyMessage& set(const char* value); MyMessage& set(uint8_t value); - MyMessage& set(bool value); MyMessage& set(double value, uint8_t decimals); MyMessage& set(unsigned long value); MyMessage& set(long value); @@ -164,9 +165,7 @@ public: unsigned int uiValue; int iValue; char data[MAX_PAYLOAD + 1]; - }; - - -}; + } __attribute__((packed)); +} __attribute__((packed)); #endif diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 44901a2..2bc4091 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -10,7 +10,9 @@ */ #include "MySensor.h" -#include "LowPower.h" +#include "utility/LowPower.h" +#include "utility/RF24.h" +#include "utility/RF24_config.h" // Inline function and macros @@ -205,8 +207,8 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) { bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); RF24::startListening(); - debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d, st=%s:%s\n"), - message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, ok?"ok":"fail", message.getString(convBuf)); + debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,st=%s:%s\n"), + message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, mGetPayloadType(message), mGetLength(message), ok?"ok":"fail", message.getString(convBuf)); return ok; } @@ -215,7 +217,7 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) { bool MySensor::send(MyMessage &message, bool enableAck) { message.sender = nc.nodeId; mSetCommand(message,C_SET); - mSetAck(message,enableAck); + mSetAck(message,enableAck); return sendRoute(message); } @@ -260,10 +262,10 @@ boolean MySensor::process() { uint8_t valid = validate(msg); boolean ok = valid == VALIDATE_OK; - // Make sure string gets terminated ok for full string messages. - msg.data[len - HEADER_SIZE ] = '\0'; - debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,cr=%s: %s\n"), - msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, valid==0?"ok":valid==1?"ec":"ev", msg.getString(convBuf)); + // Add termination, good for string messages. + msg.data[mGetLength(msg)] = '\0'; + debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,cr=%s:%s\n"), + msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), valid==0?"ok":valid==1?"ec":"ev", msg.getString(convBuf)); if (!ok) return false; @@ -497,19 +499,19 @@ uint8_t MySensor::getChildRoute(uint8_t childId) { } -float MySensor::getInternalTemp(void) +int MySensor::getInternalTemp(void) { long result; // Read internal temp sensor against 1.1V reference ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3); - delay(2); // Wait until Vref has settled + delay(20); // Wait until Vref has settled ADCSRA |= _BV(ADSC); while (bit_is_set(ADCSRA,ADSC)); result = ADCL; result |= ADCH<<8; - result = (result - 125) * 1075; + result = (result - 125) * 1075 + 500; // add 500 to round to nearest full degree - return static_cast(static_cast(result/1000.)) / 10.; + return result/10000; } int continueTimer = true; @@ -532,12 +534,16 @@ void MySensor::internalSleep(int ms) { } void MySensor::sleep(int ms) { + // Let serial prints finish (debug, log etc) + Serial.flush(); RF24::powerDown(); continueTimer = true; internalSleep(ms); } void MySensor::sleep(int interrupt, int mode, int ms) { + // Let serial prints finish (debug, log etc) + Serial.flush(); RF24::powerDown(); attachInterrupt(interrupt, wakeUp, mode); //Interrupt on pin 3 for any change in solar power if (ms>0) { @@ -556,11 +562,11 @@ void MySensor::debugPrint(const char *fmt, ... ) { char fmtBuffer[300]; if (isGateway) { // prepend debug message to be handled correctly by gw (C_INTERNAL, I_LOG_MESSAGE) - Serial.write("0;0;4;8;"); + snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;%d;"), C_INTERNAL, I_LOG_MESSAGE); + Serial.print(fmtBuffer); } va_list args; va_start (args, fmt ); - vsnprintf_P(fmtBuffer, 300, fmt, args); va_end (args); if (isGateway) { // Truncate message if this is gateway node @@ -574,7 +580,6 @@ void MySensor::debugPrint(const char *fmt, ... ) { Serial.print(fmtBuffer); Serial.flush(); - //Serial.write("0;0;4;11;Mem free:"); //Serial.write(freeRam()); } #endif diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 15d8e62..eaad9b0 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -15,16 +15,15 @@ #include "Version.h" // Auto generated by bot #include "MyConfig.h" #include "MyMessage.h" -#include -#include -#include #include #include #include #include #include #include -#include "LowPower.h" +#include "utility/LowPower.h" +#include "utility/RF24.h" +#include "utility/RF24_config.h" #ifdef DEBUG #define debug(x,...) debugPrint(x, ##__VA_ARGS__) @@ -218,11 +217,13 @@ class MySensor : public RF24 /** * getInternalTemp * - * Read temp from internal (ATMEGA328 only) temperature sensor + * Read temp from internal (ATMEGA328 only) temperature sensor. This reading is very + * inaccurate so we round the result to full degrees celsius. + * http://playground.arduino.cc/Main/InternalTemperatureSensor * - * @return Temperature in degrees Celsius (one decimal) + * @return Temperature in full degrees Celsius. */ - float getInternalTemp(void); + int getInternalTemp(void); diff --git a/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino b/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino index 8322b50..97bc0c1 100644 --- a/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino +++ b/libraries/MySensors/examples/BatteryPoweredSensor/BatteryPoweredSensor.ino @@ -3,7 +3,6 @@ // thread: http://forum.micasaverde.com/index.php/topic,20078.0.html #include -#include #include int BATTERY_SENSE_PIN = A0; // select the input pin for the battery sense point @@ -27,7 +26,7 @@ void loop() // get the battery Voltage int sensorValue = analogRead(BATTERY_SENSE_PIN); Serial.println(sensorValue); - + // 1M, 470K divider across battery and using internal ADC ref of 1.1V // Sense point is bypassed with 0.1 uF cap to reduce noise at that point // ((1e6+470e3)/470e3)*1.1 = Vmax = 3.44 Volts @@ -48,8 +47,5 @@ void loop() gw.sendBatteryLevel(batteryPcnt); oldBatteryPcnt = batteryPcnt; } - - // delay to allow transmissions to gateway to be completed before sleep - delay(300); gw.sleep(SLEEP_TIME); } diff --git a/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino b/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino index f1a65ca..6ca5886 100644 --- a/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino +++ b/libraries/MySensors/examples/BinarySwitchSensor/BinarySwitchSensor.ino @@ -4,8 +4,6 @@ #include #include -#include -#include #include #define CHILD_ID 3 @@ -14,6 +12,7 @@ MySensor gw; Bounce debouncer = Bounce(); int oldValue=-1; + // Change to V_LIGHT if you use S_LIGHT in presentation below MyMessage msg(CHILD_ID,V_TRIPPED); @@ -32,7 +31,7 @@ void setup() // Register binary input sensor to gw (they will be created as child devices) // You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage. - // If S_LIGHT is used, remember to update variable type you send in below. + // If S_LIGHT is used, remember to update variable type you send in. See "msg" above. gw.present(CHILD_ID, S_DOOR); } diff --git a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino index 047068e..3c6c2c0 100644 --- a/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino +++ b/libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino @@ -3,11 +3,10 @@ #include #include #include -#include #define ONE_WIRE_BUS 3 // Pin where dallase sensor is connected #define MAX_ATTACHED_DS18B20 16 -unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) +unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); MySensor gw; @@ -61,9 +60,7 @@ void loop() lastTemperature[i]=temperature; } } - // Power down the radio and sleep Arduino to save batteries. - delay(300); - gw.sleep(SLEEP_TIME * 1000); + gw.sleep(SLEEP_TIME); } diff --git a/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino b/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino index de1438a..abd0530 100644 --- a/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino +++ b/libraries/MySensors/examples/DistanceSensor/DistanceSensor.ino @@ -1,6 +1,4 @@ #include -#include -#include #include #include @@ -8,7 +6,7 @@ #define TRIGGER_PIN 6 // Arduino pin tied to trigger pin on the ultrasonic sensor. #define ECHO_PIN 5 // Arduino pin tied to echo pin on the ultrasonic sensor. #define MAX_DISTANCE 300 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm. -unsigned long SLEEP_TIME = 5; // Sleep time between reads (in seconds) +unsigned long SLEEP_TIME = 5000; // Sleep time between reads (in milliseconds) MySensor gw; NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance. @@ -31,19 +29,16 @@ void setup() void loop() { int dist = metric?sonar.ping_cm():sonar.ping_in(); - Serial.print("Ping: "); Serial.print(dist); // Convert ping time to distance in cm and print result (0 = outside set distance range) Serial.println(metric?" cm":" in"); - if (dist != lastDist) { gw.send(msg.set(dist)); lastDist = dist; } - delay(300); //delay to allow serial to fully print before sleep - gw.sleep(SLEEP_TIME*1000); + gw.sleep(SLEEP_TIME); } diff --git a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino index f8836ff..7833171 100644 --- a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino +++ b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino @@ -8,8 +8,6 @@ // to calculate/report watt-number. #include -#include -#include #include #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your light sensor. (Only 2 and 3 generates interrupt!) @@ -18,7 +16,7 @@ #define MAX_WATT 10000 // Max watt value to report. This filetrs outliers. #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) #define CHILD_ID 1 // Id of the sensor child -unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't wnat to spam the gateway. +unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't wnat to spam the gateway. MySensor gw; double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour boolean pcReceived = false; @@ -88,8 +86,7 @@ void loop() } if (SLEEP_MODE) { - delay(300); //delay to allow serial to fully print before sleep - gw.sleep(SEND_FREQUENCY * 1000); + gw.sleep(SEND_FREQUENCY); } } diff --git a/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino b/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino index 3952ce9..54dc5eb 100644 --- a/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino +++ b/libraries/MySensors/examples/HumiditySensor/HumiditySensor.ino @@ -1,12 +1,11 @@ #include -#include #include #include #define CHILD_ID_HUM 0 #define CHILD_ID_TEMP 1 #define HUMIDITY_SENSOR_DIGITAL_PIN 3 -unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) +unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) MySensor gw; DHT dht; @@ -59,9 +58,7 @@ void loop() Serial.println(humidity); } - delay(300); //delay to allow serial to fully print before sleep - gw.sleep(SLEEP_TIME * 1000); //sleep a bit + gw.sleep(SLEEP_TIME); //sleep a bit } - diff --git a/libraries/MySensors/examples/IrSensor/IrSensor.ino b/libraries/MySensors/examples/IrSensor/IrSensor.ino index 4505d48..e954fcd 100644 --- a/libraries/MySensors/examples/IrSensor/IrSensor.ino +++ b/libraries/MySensors/examples/IrSensor/IrSensor.ino @@ -7,8 +7,6 @@ #include #include -#include -#include #include int RECV_PIN = 8; diff --git a/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino b/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino index d74a9d7..441c65e 100644 --- a/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino +++ b/libraries/MySensors/examples/LightLuxSensor/LightLuxSensor.ino @@ -17,14 +17,13 @@ */ #include -#include #include #include #include #define CHILD_ID_LIGHT 0 #define LIGHT_SENSOR_ANALOG_PIN 0 -unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) +unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) BH1750 lightSensor; MySensor gw; @@ -53,6 +52,5 @@ void loop() lastlux = lux; } - delay(300); //delay to allow serial to fully print before sleep - gw.sleep(SLEEP_TIME * 1000); + gw.sleep(SLEEP_TIME); } diff --git a/libraries/MySensors/examples/LightSensor/LightSensor.ino b/libraries/MySensors/examples/LightSensor/LightSensor.ino index df31c89..534f8bb 100644 --- a/libraries/MySensors/examples/LightSensor/LightSensor.ino +++ b/libraries/MySensors/examples/LightSensor/LightSensor.ino @@ -1,11 +1,10 @@ #include -#include #include #define CHILD_ID_LIGHT 0 #define LIGHT_SENSOR_ANALOG_PIN 0 -unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) +unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) MySensor gw; MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL); @@ -30,9 +29,7 @@ void loop() gw.send(msg.set(lightLevel)); lastLightLevel = lightLevel; } - - delay(300); //delay to allow serial to fully print before sleep - gw.sleep(SLEEP_TIME * 1000); + gw.sleep(SLEEP_TIME); } diff --git a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino index 6799854..db8373f 100644 --- a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino +++ b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino @@ -1,7 +1,7 @@ #include #include -#include +unsigned long SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds) #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your motion sensor. (Only 2 and 3 generates interrupt!) #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) #define CHILD_ID 1 // Id of the sensor child @@ -32,7 +32,7 @@ void loop() gw.send(msg.set(tripped?"1":"0")); // Send tripped value to gw // Sleep until interrupt comes in on motion sensor. Send update every two minute. - gw.sleep(INTERRUPT,CHANGE, 120000); + gw.sleep(INTERRUPT,CHANGE, SLEEP_TIME); } diff --git a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino index 636e79f..f4cde74 100644 --- a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino +++ b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -8,7 +7,7 @@ #define TEMP_CHILD 1 #define LIGHT_SENSOR_ANALOG_PIN 0 -unsigned long SLEEP_TIME = 60; // Sleep time between reads (in seconds) +unsigned long SLEEP_TIME = 60000; // Sleep time between reads (in seconds) Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor MySensor gw; @@ -28,8 +27,6 @@ MyMessage tempMsg(TEMP_CHILD, V_TEMP); MyMessage pressureMsg(BARO_CHILD, V_PRESSURE); MyMessage forecastMsg(BARO_CHILD, V_FORECAST); - - void setup() { gw.begin(); @@ -47,7 +44,6 @@ void setup() { metric = gw.getConfig().isMetric; } - void loop() { float pressure = bmp.readPressure(); float temperature = bmp.readTemperature(); @@ -94,8 +90,7 @@ void loop() { 5 = "Unknown (More Time needed) */ - delay(300); //delay to allow serial to fully print before sleep - gw.sleep(SLEEP_TIME * 1000); + gw.sleep(SLEEP_TIME); } int sample(float pressure) { diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index 0e4994d..3f0e94f 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -3,8 +3,6 @@ #include #include -#include - #define RELAY_1 3 // Arduino Digital I/O pin number for first relay (second on pin+1 etc) #define NUMBER_OF_RELAYS 1 // Total number of attached relays @@ -27,7 +25,7 @@ void setup() // Then set relay pins in output mode pinMode(pin, OUTPUT); // Set relay to last known state (using eeprom storage) - digitalWrite(pin, gw.loadState(sensor)); + digitalWrite(pin, gw.loadState(sensor)?RELAY_ON:RELAY_OFF); } } diff --git a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino index d242988..40187c4 100644 --- a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino +++ b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino @@ -5,17 +5,16 @@ #include #include -#include #include #define RELAY_PIN 4 // Arduino Digital I/O pin number for relay -#define BUTTON_PIN 3 // Arduino Digital I/O pin number for button (with 10k pulldown) +#define BUTTON_PIN 3 // Arduino Digital I/O pin number for button #define CHILD_ID 1 // Id of the sensor child -#define RELAY_ON 0 -#define RELAY_OFF 1 +#define RELAY_ON 1 +#define RELAY_OFF 0 Bounce debouncer = Bounce(); -int oldValue=-1; +int oldValue=0; bool state; MySensor gw; MyMessage msg(CHILD_ID,V_LIGHT); @@ -36,7 +35,6 @@ void setup() debouncer.attach(BUTTON_PIN); debouncer.interval(5); - // Register all sensors to gw (they will be created as child devices) gw.present(CHILD_ID, S_LIGHT); @@ -45,10 +43,9 @@ void setup() // Then set relay pins in output mode pinMode(RELAY_PIN, OUTPUT); - // Set relay to last known state (using eeprom storage) - digitalWrite(RELAY_PIN, gw.loadState(CHILD_ID)); - - + // Set relay to last known state (using eeprom storage) + state = gw.loadState(CHILD_ID); + digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF); } @@ -61,16 +58,12 @@ void loop() debouncer.update(); // Get the update value int value = debouncer.read(); - - if (value != oldValue && value!=0) { - gw.saveState(CHILD_ID, state); - gw.send(msg.set(state==1?0:1), true); // Send and request ack - oldValue = value; - } + if (value != oldValue && value==0) { + gw.send(msg.set(state?false:true), true); // Send new state and request ack back + } + oldValue = value; } - - void incomingMessage(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. if (message.type==V_LIGHT) { diff --git a/libraries/MySensors/examples/RepeaterNode/RepeaterNode.ino b/libraries/MySensors/examples/RepeaterNode/RepeaterNode.ino index e023176..8ada2de 100644 --- a/libraries/MySensors/examples/RepeaterNode/RepeaterNode.ino +++ b/libraries/MySensors/examples/RepeaterNode/RepeaterNode.ino @@ -5,7 +5,6 @@ #include #include -#include MySensor gw; diff --git a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino index a0b8853..561fc73 100644 --- a/libraries/MySensors/examples/ServoActuator/ServoActuator.ino +++ b/libraries/MySensors/examples/ServoActuator/ServoActuator.ino @@ -9,7 +9,6 @@ #include #include #include -#include #define SERVO_DIGITAL_OUT_PIN 3 #define SERVO_MIN 0 // Fine tune your servos min. 0-180 @@ -23,7 +22,6 @@ Servo myservo; // create servo object to control a servo // a maximum of eight servo objects can be created Sensor gw(9,10); unsigned long timeOfLastChange = 0; bool attachedServo = false; - void setup() { diff --git a/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino b/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino index 17a1fb5..ca6249a 100644 --- a/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino +++ b/libraries/MySensors/examples/SoilMoistSensor/SoilMoistSensor.ino @@ -1,5 +1,4 @@ #include -#include #include #define DIGITAL_INPUT_SOIL_SENSOR 3 // Digital input did you attach your soil sensor. diff --git a/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino b/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino index f7197ba..2635f2e 100644 --- a/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino +++ b/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino @@ -2,7 +2,6 @@ // Example sketch showing how to request time from controller #include -#include #include #include diff --git a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino index 352900c..93caa68 100644 --- a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino +++ b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino @@ -10,7 +10,6 @@ // #include -#include #include #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!) @@ -19,7 +18,7 @@ #define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers. #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) #define CHILD_ID 5 // Id of the sensor child -unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't want to spam the gateway. +unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't want to spam the gateway. MySensor gw; @@ -104,8 +103,7 @@ void loop() } if (SLEEP_MODE) { - delay(300); //delay to allow serial to fully print before sleep - gw.sleep(SEND_FREQUENCY * 1000); + gw.sleep(SEND_FREQUENCY); } } diff --git a/libraries/MySensors/LowPower.cpp b/libraries/MySensors/utility/LowPower.cpp similarity index 100% rename from libraries/MySensors/LowPower.cpp rename to libraries/MySensors/utility/LowPower.cpp diff --git a/libraries/MySensors/LowPower.h b/libraries/MySensors/utility/LowPower.h similarity index 100% rename from libraries/MySensors/LowPower.h rename to libraries/MySensors/utility/LowPower.h diff --git a/libraries/MySensors/utility/RF24.cpp b/libraries/MySensors/utility/RF24.cpp new file mode 100644 index 0000000..2efb7bb --- /dev/null +++ b/libraries/MySensors/utility/RF24.cpp @@ -0,0 +1,1228 @@ +/* + Copyright (C) 2011 J. Coliz + + 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 "nRF24L01.h" +#include "RF24_config.h" +#include "RF24.h" + +/****************************************************************************/ + +void RF24::csn(bool mode) +{ + // Minimum ideal SPI bus speed is 2x data rate + // If we assume 2Mbs data rate and 16Mhz clock, a + // divider of 4 is the minimum we want. + // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz +#ifdef ARDUINO + #if !defined( __AVR_ATtiny85__ ) && !defined( __AVR_ATtiny84__) && !defined (__arm__) + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV2); + #endif +#endif + +#ifndef __arm__ + digitalWrite(csn_pin,mode); +#endif + +} + +/****************************************************************************/ + +void RF24::ce(bool level) +{ + digitalWrite(ce_pin,level); +} + +/****************************************************************************/ + +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +{ + uint8_t status; + +#if defined (__arm__) + status = SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); + while ( len-- > 1 ){ + *buf++ = SPI.transfer(csn_pin,0xff, SPI_CONTINUE); + } + *buf++ = SPI.transfer(csn_pin,0xff); + +#else + csn(LOW); + status = SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ){ + *buf++ = SPI.transfer(0xff); + } + csn(HIGH); + +#endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::read_register(uint8_t reg) +{ + + #if defined (__arm__) + SPI.transfer(csn_pin, R_REGISTER | ( REGISTER_MASK & reg ) , SPI_CONTINUE); + uint8_t result = SPI.transfer(csn_pin,0xff); + #else + csn(LOW); + SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + uint8_t result = SPI.transfer(0xff); + + csn(HIGH); + #endif + + return result; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + uint8_t status; + + #if defined (__arm__) + status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE ); + while ( len-- > 1){ + SPI.transfer(csn_pin,*buf++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*buf++); + #else + + csn(LOW); + status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + SPI.transfer(*buf++); + + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, uint8_t value) +{ + uint8_t status; + + IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"),reg,value)); + + #if defined (__arm__) + status = SPI.transfer(csn_pin, W_REGISTER | ( REGISTER_MASK & reg ), SPI_CONTINUE); + SPI.transfer(csn_pin,value); + #else + + csn(LOW); + status = SPI.transfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + SPI.transfer(value); + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::write_payload(const void* buf, uint8_t data_len, const uint8_t writeType) +{ + uint8_t status; + const uint8_t* current = reinterpret_cast(buf); + + if(data_len > payload_size) data_len = payload_size; + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + //printf("[Writing %u bytes %u blanks]",data_len,blank_len); + + #if defined (__arm__) + + status = SPI.transfer(csn_pin, writeType , SPI_CONTINUE); + + if(blank_len){ + while ( data_len--){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + while ( --blank_len ){ + SPI.transfer(csn_pin,0, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0); + }else{ + while( --data_len ){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*current); + } + + #else + + csn(LOW); + status = SPI.transfer( writeType ); + while ( data_len-- ) { + SPI.transfer(*current++); + } + while ( blank_len-- ) { + SPI.transfer(0); + } + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::read_payload(void* buf, uint8_t data_len) +{ + uint8_t status; + uint8_t* current = reinterpret_cast(buf); + + if(data_len > payload_size) data_len = payload_size; + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + //printf("[Reading %u bytes %u blanks]",data_len,blank_len); + + + #if defined (__arm__) + + status = SPI.transfer(csn_pin, R_RX_PAYLOAD, SPI_CONTINUE ); + + if( blank_len ){ + + while ( data_len-- ){ + *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + while ( --blank_len ){ + SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + SPI.transfer(csn_pin,0xFF); + }else{ + while ( --data_len ){ + *current++ = SPI.transfer(csn_pin,0xFF, SPI_CONTINUE); + } + *current = SPI.transfer(csn_pin,0xFF); + } + + #else + + csn(LOW); + status = SPI.transfer( R_RX_PAYLOAD ); + while ( data_len-- ) { + *current++ = SPI.transfer(0xFF); + } + while ( blank_len-- ) { + SPI.transfer(0xff); + } + csn(HIGH); + + #endif + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_rx(void) +{ + return spiTrans( FLUSH_RX ); +} + +/****************************************************************************/ + +uint8_t RF24::flush_tx(void) +{ + return spiTrans( FLUSH_TX ); +} + +/****************************************************************************/ + +uint8_t RF24::spiTrans(uint8_t cmd){ + + uint8_t status; + #if defined (__arm__) + status = SPI.transfer(csn_pin, cmd ); + #else + + csn(LOW); + status = SPI.transfer( cmd ); + csn(HIGH); + #endif + return status; + +} + +/****************************************************************************/ + +uint8_t RF24::get_status(void) +{ + return spiTrans(NOP); +} + +/****************************************************************************/ + +void RF24::print_status(uint8_t status) +{ + printf_P(PSTR("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), + status, + (status & _BV(RX_DR))?1:0, + (status & _BV(TX_DS))?1:0, + (status & _BV(MAX_RT))?1:0, + ((status >> RX_P_NO) & B111), + (status & _BV(TX_FULL))?1:0 + ); +} + +/****************************************************************************/ + +void RF24::print_observe_tx(uint8_t value) +{ + printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), + value, + (value >> PLOS_CNT) & B1111, + (value >> ARC_CNT) & B1111 + ); +} + +/****************************************************************************/ + +void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen_P(name) < 8 ? '\t' : 0; + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); + while (qty--) + printf_P(PSTR(" 0x%02x"),read_register(reg++)); + printf_P(PSTR("\r\n")); +} + +/****************************************************************************/ + +void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen_P(name) < 8 ? '\t' : 0; + printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab); + + while (qty--) + { + uint8_t buffer[5]; + read_register(reg++,buffer,sizeof buffer); + + printf_P(PSTR(" 0x")); + uint8_t* bufptr = buffer + sizeof buffer; + while( --bufptr >= buffer ) + printf_P(PSTR("%02x"),*bufptr); + } + + printf_P(PSTR("\r\n")); +} + +/****************************************************************************/ + +RF24::RF24(uint8_t _cepin, uint8_t _cspin): + ce_pin(_cepin), csn_pin(_cspin), wide_band(false), p_variant(false), + payload_size(32), dynamic_payloads_enabled(false), + pipe0_reading_address(0) +{ +} + +/****************************************************************************/ + +void RF24::setChannel(uint8_t channel) +{ + // TODO: This method could take advantage of the 'wide_band' calculation + // done in setChannel() to require certain channel spacing. + + const uint8_t max_channel = 127; + write_register(RF_CH,min(channel,max_channel)); +} + +/****************************************************************************/ + +void RF24::setPayloadSize(uint8_t size) +{ + payload_size = min(size,32); +} + +/****************************************************************************/ + +uint8_t RF24::getPayloadSize(void) +{ + return payload_size; +} + +/****************************************************************************/ + +#if !defined (MINIMAL) + +static const char rf24_datarate_e_str_0[] PROGMEM = "1MBPS"; +static const char rf24_datarate_e_str_1[] PROGMEM = "2MBPS"; +static const char rf24_datarate_e_str_2[] PROGMEM = "250KBPS"; +static const char * const rf24_datarate_e_str_P[] PROGMEM = { + rf24_datarate_e_str_0, + rf24_datarate_e_str_1, + rf24_datarate_e_str_2, +}; +static const char rf24_model_e_str_0[] PROGMEM = "nRF24L01"; +static const char rf24_model_e_str_1[] PROGMEM = "nRF24L01+"; +static const char * const rf24_model_e_str_P[] PROGMEM = { + rf24_model_e_str_0, + rf24_model_e_str_1, +}; +static const char rf24_crclength_e_str_0[] PROGMEM = "Disabled"; +static const char rf24_crclength_e_str_1[] PROGMEM = "8 bits"; +static const char rf24_crclength_e_str_2[] PROGMEM = "16 bits" ; +static const char * const rf24_crclength_e_str_P[] PROGMEM = { + rf24_crclength_e_str_0, + rf24_crclength_e_str_1, + rf24_crclength_e_str_2, +}; +static const char rf24_pa_dbm_e_str_0[] PROGMEM = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] PROGMEM = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] PROGMEM = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_3[] PROGMEM = "PA_MAX"; +static const char * const rf24_pa_dbm_e_str_P[] PROGMEM = { + rf24_pa_dbm_e_str_0, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + +void RF24::printDetails(void) +{ + print_status(get_status()); + + print_address_register(PSTR("RX_ADDR_P0-1"),RX_ADDR_P0,2); + print_byte_register(PSTR("RX_ADDR_P2-5"),RX_ADDR_P2,4); + print_address_register(PSTR("TX_ADDR"),TX_ADDR); + + print_byte_register(PSTR("RX_PW_P0-6"),RX_PW_P0,6); + print_byte_register(PSTR("EN_AA"),EN_AA); + print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR); + print_byte_register(PSTR("RF_CH"),RF_CH); + print_byte_register(PSTR("RF_SETUP"),RF_SETUP); + print_byte_register(PSTR("CONFIG"),CONFIG); + print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2); + +#if defined(__arm__) + printf_P(PSTR("Data Rate\t = %s\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); + printf_P(PSTR("Model\t\t = %s\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); + printf_P(PSTR("CRC Length\t = %s\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); + printf_P(PSTR("PA Power\t = %s\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); +#else + printf_P(PSTR("Data Rate\t = %S\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()])); + printf_P(PSTR("Model\t\t = %S\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()])); + printf_P(PSTR("CRC Length\t = %S\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()])); + printf_P(PSTR("PA Power\t = %S\r\n"),pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()])); +#endif +} + +#endif +/****************************************************************************/ + +void RF24::begin(void) +{ + // Initialize pins + pinMode(ce_pin,OUTPUT); + + #if defined(__arm__) + SPI.begin(csn_pin); // Using the extended SPI features of the DUE + SPI.setClockDivider(csn_pin, 9); // Set the bus speed to 8.4mhz on Due + SPI.setBitOrder(csn_pin,MSBFIRST); // Set the bit order and mode specific to this device + SPI.setDataMode(csn_pin,SPI_MODE0); + ce(LOW); + //csn(HIGH); + #else + pinMode(csn_pin,OUTPUT); + SPI.begin(); + ce(LOW); + csn(HIGH); + #endif + + // Must allow the radio time to settle else configuration bits will not necessarily stick. + // This is actually only required following power up but some settling time also appears to + // be required after resets too. For full coverage, we'll always assume the worst. + // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped. + // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure. + // WARNING: Delay is based on P-variant whereby non-P *may* require different timing. + delay( 5 ) ; + + // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier + // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet + // sizes must never be used. See documentation for a more complete explanation. + setRetries(5,15); + + // Reset value is MAX + //setPALevel( RF24_PA_MAX ) ; + + // Determine if this is a p or non-p RF24 module and then + // reset our data rate back to default value. This works + // because a non-P variant won't allow the data rate to + // be set to 250Kbps. + if( setDataRate( RF24_250KBPS ) ) + { + p_variant = true ; + } + + // Then set the data rate to the slowest (and most reliable) speed supported by all + // hardware. + setDataRate( RF24_1MBPS ) ; + + // Initialize CRC and request 2-byte (16bit) CRC + setCRCLength( RF24_CRC_16 ) ; + + // Disable dynamic payloads, to match dynamic_payloads_enabled setting - Reset value is 0 + //write_register(DYNPD,0); + + // Reset current status + // Notice reset and flush is the last thing we do + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Set up default configuration. Callers can always change it later. + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); + + // Flush buffers + flush_rx(); + flush_tx(); + + powerUp(); //Power up by default when begin() is called + + // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + // PTX should use only 22uA of power + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + + +} + +/****************************************************************************/ + +void RF24::startListening(void) +{ + powerUp(); + write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address){ + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + } + + // Flush buffers + flush_rx(); + flush_tx(); + + // Go! + ce(HIGH); + +} + +/****************************************************************************/ + +void RF24::stopListening(void) +{ + ce(LOW); + flush_tx(); + flush_rx(); + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + +} + +/****************************************************************************/ + +void RF24::powerDown(void) +{ + ce(LOW); // Guarantee CE is low on powerDown + write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); +} + +/****************************************************************************/ + +//Power up now. Radio will not power down unless instructed by MCU for config changes etc. +void RF24::powerUp(void) +{ + uint8_t cfg = read_register(CONFIG); + + // if not powered up then power up and wait for the radio to initialize + if (!(cfg & _BV(PWR_UP))){ + write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); + + // For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode. + // There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode before + // the CEis set high. - Tpd2stby can be up to 5ms per the 1.0 datasheet + delay(5); + + } +} + +/******************************************************************/ + +//Similar to the previous write, clears the interrupt flags +bool RF24::write( const void* buf, uint8_t len, const bool multicast ) +{ + //Start Writing + startWrite(buf,len,multicast); + + //Wait until complete or failed + //ACK payloads that are handled improperly will cause this to hang + //If autoAck is ON, a payload has to be written prior to reading a payload, else write after reading a payload + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + //Max retries exceeded + if( status & _BV(MAX_RT)){ + flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush + return 0; + } + //TX OK 1 or 0 + return 1; +} + +bool RF24::write( const void* buf, uint8_t len ){ + return write(buf,len,0); +} +/****************************************************************************/ + +//For general use, the interrupt flags are not important to clear +bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //This way the FIFO will fill up and allow blocking until packets go through + //The radio will auto-clear everything in the FIFO as long as CE remains high + + uint32_t timer = millis(); //Get the time that the payload transmission started + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout + + if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached + reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag + if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 + } + + } + + //Start Writing + startFastWrite(buf,len,0); //Write the payload if a buffer is clear + + return 1; //Return 1 to indicate successful transmission +} + +/****************************************************************************/ + +void RF24::reUseTX(){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + spiTrans( REUSE_TX_PL ); + ce(LOW); //Re-Transfer packet + ce(HIGH); +} + +/****************************************************************************/ + +bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //Return 0 so the user can control the retrys and set a timer or failure counter if required + //The radio will auto-clear everything in the FIFO as long as CE remains high + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail + + if( get_status() & _BV(MAX_RT)){ + //reUseTX(); //Set re-transmit + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + return 0; //Return 0. The previous payload has been retransmitted + //From the user perspective, if you get a 0, just keep trying to send the same payload + } + + } + //Start Writing + startFastWrite(buf,len,multicast); + + return 1; +} + +bool RF24::writeFast( const void* buf, uint8_t len ){ + return writeFast(buf,len,0); +} + +/****************************************************************************/ + +//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high +//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) +//Otherwise we enter Standby-II mode, which is still faster than standby mode +//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data + +void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 + + //write_payload( buf,len); + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + ce(HIGH); + +} + + +//Added the original startWrite back in so users can still use interrupts, ack payloads, etc +//Allows the library to pass all tests +void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ){ + + // Send the payload + + //write_payload( buf, len ); + write_payload( buf, len, multicast?static_cast(W_TX_PAYLOAD_NO_ACK):static_cast(W_TX_PAYLOAD)) ; + + ce(HIGH); + ce(LOW); + + +} + +bool RF24::txStandBy(){ + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + ce(LOW); + flush_tx(); //Non blocking, flush the data + return 0; + } + } + + ce(LOW); //Set STANDBY-I mode + return 1; +} + +bool RF24::txStandBy(uint32_t timeout){ + + uint32_t start = millis(); + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + ce(LOW); //Set re-transmit + ce(HIGH); + if(millis() - start >= timeout){ + ce(LOW); flush_tx(); return 0; + } + } + } + ce(LOW); //Set STANDBY-I mode + return 1; + +} +/****************************************************************************/ + +void RF24::maskIRQ(bool tx, bool fail, bool rx){ + + write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); +} + +/****************************************************************************/ + +uint8_t RF24::getDynamicPayloadSize(void) +{ + uint8_t result = 0; + + #if defined (__arm__) + SPI.transfer(csn_pin, R_RX_PL_WID, SPI_CONTINUE ); + result = SPI.transfer(csn_pin,0xff); + #else + csn(LOW); + SPI.transfer( R_RX_PL_WID ); + result = SPI.transfer(0xff); + csn(HIGH); + + #endif + + if(result > 32) { flush_rx(); return 0; } + return result; +} + +/****************************************************************************/ + +bool RF24::available(void) +{ + return available(NULL); +} + +/****************************************************************************/ + +bool RF24::available(uint8_t* pipe_num) +{ + + //Check the FIFO buffer to see if data is waitng to be read + + if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ + + // If the caller wants the pipe number, include that + if ( pipe_num ){ + uint8_t status = get_status(); + *pipe_num = ( status >> RX_P_NO ) & B111; + } + return 1; + } + + return 0; + + + +} + +/****************************************************************************/ + +void RF24::read( void* buf, uint8_t len ){ + + // Fetch the payload + read_payload( buf, len ); + + //Clear the two possible interrupt flags with one command + write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); + +} + +/****************************************************************************/ + +void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) +{ + // Read the status & reset the status in one easy call + // Or is that such a good idea? + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Report to the user what happened + tx_ok = status & _BV(TX_DS); + tx_fail = status & _BV(MAX_RT); + rx_ready = status & _BV(RX_DR); +} + +/****************************************************************************/ + +void RF24::openWritingPipe(uint64_t value) +{ + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) + // expects it LSB first too, so we're good. + + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); + write_register(TX_ADDR, reinterpret_cast(&value), 5); + + //const uint8_t max_payload_size = 32; + //write_register(RX_PW_P0,min(payload_size,max_payload_size)); + write_register(RX_PW_P0,payload_size); +} + +/****************************************************************************/ + +static const uint8_t child_pipe[] PROGMEM = +{ + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 +}; +static const uint8_t child_payload_size[] PROGMEM = +{ + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 +}; +static const uint8_t child_pipe_enable[] PROGMEM = +{ + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 +}; + +void RF24::openReadingPipe(uint8_t child, uint64_t address) +{ + // If this is pipe 0, cache the address. This is needed because + // openWritingPipe() will overwrite the pipe 0 address, so + // startListening() will have to restore it. + if (child == 0) + pipe0_reading_address = address; + + if (child <= 6) + { + // For pipes 2-5, only write the LSB + if ( child < 2 ) + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); + else + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); + + write_register(pgm_read_byte(&child_payload_size[child]),payload_size); + + // Note it would be more efficient to set all of the bits for all open + // pipes at once. However, I thought it would make the calling code + // more simple to do it this way. + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); + } +} + +/****************************************************************************/ + +void RF24::closeReadingPipe( uint8_t pipe ) +{ + write_register(EN_RXADDR,read_register(EN_RXADDR) & ~_BV(pgm_read_byte(&child_pipe_enable[pipe]))); +} + + +/****************************************************************************/ + +void RF24::toggle_features(void) +{ + + #if defined (__arm__) + SPI.transfer(csn_pin, ACTIVATE, SPI_CONTINUE ); + SPI.transfer(csn_pin, 0x73 ); + #else + csn(LOW); + SPI.transfer( ACTIVATE ); + SPI.transfer( 0x73 ); + csn(HIGH); + #endif +} + +/****************************************************************************/ + +void RF24::enableDynamicPayloads(void) +{ + // Enable dynamic payload throughout the system + + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); + + + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + + // Enable dynamic payload on all pipes + // + // Not sure the use case of only having dynamic payload on certain + // pipes, so the library does not support it. + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + + dynamic_payloads_enabled = true; +} + +/****************************************************************************/ + +void RF24::enableAckPayload(void) +{ + // + // enable ack payload and dynamic payload features + // + + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + + // + // Enable dynamic payload on pipes 0 & 1 + // + + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/****************************************************************************/ + +void RF24::enableDynamicAck(void){ + // + // enable dynamic ack features + // + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); + + IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + + +} + +/****************************************************************************/ + +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,32); + + #if defined (__arm__) + SPI.transfer(csn_pin, W_ACK_PAYLOAD | ( pipe & B111 ), SPI_CONTINUE); + while ( data_len-- > 1 ){ + SPI.transfer(csn_pin,*current++, SPI_CONTINUE); + } + SPI.transfer(csn_pin,*current++); + + #else + csn(LOW); + SPI.transfer(W_ACK_PAYLOAD | ( pipe & B111 ) ); + + while ( data_len-- ) + SPI.transfer(*current++); + + csn(HIGH); + + #endif +} + +/****************************************************************************/ + +bool RF24::isAckPayloadAvailable(void) +{ + return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); +} + +/****************************************************************************/ + +bool RF24::isPVariant(void) +{ + return p_variant ; +} + +/****************************************************************************/ + +void RF24::setAutoAck(bool enable) +{ + if ( enable ) + write_register(EN_AA, B111111); + else + write_register(EN_AA, 0); +} + +/****************************************************************************/ + +void RF24::setAutoAck( uint8_t pipe, bool enable ) +{ + if ( pipe <= 6 ) + { + uint8_t en_aa = read_register( EN_AA ) ; + if( enable ) + { + en_aa |= _BV(pipe) ; + } + else + { + en_aa &= ~_BV(pipe) ; + } + write_register( EN_AA, en_aa ) ; + } +} + +/****************************************************************************/ + +bool RF24::testCarrier(void) +{ + return ( read_register(CD) & 1 ); +} + +/****************************************************************************/ + +bool RF24::testRPD(void) +{ + return ( read_register(RPD) & 1 ) ; +} + +/****************************************************************************/ + +void RF24::setPALevel(uint8_t level) +{ + + uint8_t setup = read_register(RF_SETUP) & 0b11111000; + + if(level > 3){ // If invalid level, go to max PA + level = RF24_PA_MAX << 1 + 1; // +1 to support the SI24R1 chip extra bit + }else{ + level = (level << 1) + 1; // Else set level as requested + } + + + write_register( RF_SETUP, setup |= level ) ; // Write it to the chip +} + +/****************************************************************************/ + +uint8_t RF24::getPALevel(void) +{ + + return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; +} + +/****************************************************************************/ + +bool RF24::setDataRate(rf24_datarate_e speed) +{ + bool result = false; + uint8_t setup = read_register(RF_SETUP) ; + + // HIGH and LOW '00' is 1Mbs - our default + wide_band = false ; + setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; + if( speed == RF24_250KBPS ) + { + // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 + // Making it '10'. + wide_band = false ; + setup |= _BV( RF_DR_LOW ) ; + } + else + { + // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 + // Making it '01' + if ( speed == RF24_2MBPS ) + { + wide_band = true ; + setup |= _BV(RF_DR_HIGH); + } + else + { + // 1Mbs + wide_band = false ; + } + } + write_register(RF_SETUP,setup); + + // Verify our result + if ( read_register(RF_SETUP) == setup ) + { + result = true; + } + else + { + wide_band = false; + } + + return result; +} + +/****************************************************************************/ + +rf24_datarate_e RF24::getDataRate( void ) +{ + rf24_datarate_e result ; + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + // switch uses RAM (evil!) + // Order matters in our case below + if ( dr == _BV(RF_DR_LOW) ) + { + // '10' = 250KBPS + result = RF24_250KBPS ; + } + else if ( dr == _BV(RF_DR_HIGH) ) + { + // '01' = 2MBPS + result = RF24_2MBPS ; + } + else + { + // '00' = 1MBPS + result = RF24_1MBPS ; + } + return result ; +} + +/****************************************************************************/ + +void RF24::setCRCLength(rf24_crclength_e length) +{ + uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; + + // switch uses RAM (evil!) + if ( length == RF24_CRC_DISABLED ) + { + // Do nothing, we turned it off above. + } + else if ( length == RF24_CRC_8 ) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV( CRCO ); + } + write_register( CONFIG, config ) ; +} + +/****************************************************************************/ + +rf24_crclength_e RF24::getCRCLength(void) +{ + rf24_crclength_e result = RF24_CRC_DISABLED; + uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; + + if ( config & _BV(EN_CRC ) ) + { + if ( config & _BV(CRCO) ) + result = RF24_CRC_16; + else + result = RF24_CRC_8; + } + + return result; +} + +/****************************************************************************/ + +void RF24::disableCRC( void ) +{ + uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; + write_register( CONFIG, disable ) ; +} + +/****************************************************************************/ +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR,(delay&0xf)< + + 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. + */ + +/** + * @file RF24.h + * + * Class declaration for RF24 and helper enums + */ + +#ifndef __RF24_H__ +#define __RF24_H__ + + +/** + * Power Amplifier level. + * + * For use with setPALevel() + */ +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; + +/** + * Data rate. How fast data moves through the air. + * + * For use with setDataRate() + */ +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +/** + * CRC Length. How big (if any) of a CRC is included. + * + * For use with setCRCLength() + */ +typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; + +/** + * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + */ + +class RF24 +{ +private: + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + bool wide_band; /* 2Mbs data rate in use? */ + bool p_variant; /* False for RF24L01 and true for RF24L01P */ + uint8_t payload_size; /**< Fixed size of payloads */ + bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + +public: + + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ + + /** + * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(uint8_t _cepin, uint8_t _cspin); + + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + * + * @note Optimization: The radio is partially configured in PTX mode + * when begin is called to make the transition to PTX mode simpler. + */ + void begin(void); + + /** + * Start listening on the pipes opened for reading. + * + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. + */ + void startListening(void); + + /** + * Stop listening for incoming messages + * + * Do this before calling write(). + * + * @note Optimization: The radio will be taken out of PRX mode as soon + * as listening is stopped. Enables quicker and simpler engaging of + * primary transmitter (PTX) mode. + */ + void stopListening(void); + + /** + * Test whether there are bytes available to be read + * + * @note Optimization: The available functino now checks the FIFO + * buffers directly for data instead of relying of interrupt flags. + * + * @note Interrupt flags will not be cleared until a payload is + * actually read from the FIFO + * + * @see txStandBy() + * @see startWrite() + * @see write() + * @see writeFast() + * + * @return True if there is a payload available, false if none is + */ + bool available(void); + + /** + * Read the payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @note Optimization: No longer boolean. Use available to + * determine if packets are available. Interrupt flags are now cleared + * during reads instead of when calling available(). + * + * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer + * @return No return value. Use available(). + */ + void read( void* buf, uint8_t len ); + + /** + * @note Optimization: Improved performance slightly + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * TX/RX/RT interrupt flags will be cleared every time write is called + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool write( const void* buf, uint8_t len ); + + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param address The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); + + /** + * Open a pipe for reading + * + * Up to 6 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning Pipes 1-5 should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * @code + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); + * @endcode + * + * @warning Pipe 0 is also used by the writing pipe. So if you open + * pipe 0 for reading, and then startListening(), it will overwrite the + * writing pipe. Ergo, do an openWritingPipe() again before write(). + * + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits + * + * @param number Which pipe# to open, 0-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); + + + + /** + * Close a pipe after it has been previously opened. + * Can be safely called without having previously opened a pipe. + * @param pipe Which pipe # to close, 0-5. + */ + void closeReadingPipe( uint8_t pipe ) ; + + /**@}*/ + /** + * @name Advanced Operation + * + * Methods you can use to drive the chip in more advanced ways + */ + /**@{*/ + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); + + /** + * Test whether there are bytes available to be read in the + * FIFO buffers. This optimized version does not rely on interrupt + * flags, but checks the actual FIFO buffers. + * + * @note Optimization: Interrupt flags are no longer cleared when available is called, + * but will be reset only when the data is read from the FIFO buffers. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + bool available(uint8_t* pipe_num); + + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening, or powerUp(). + * + * @note Optimization: The radio will never enter power down unless instructed + * by the MCU via this command. + */ + void powerDown(void); + + /** + * Leave low-power mode - making radio more responsive + * + * To return to low power mode, call powerDown(). + */ + void powerUp(void) ; + + /** + * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * + * Can be used with enableAckPayload() to request a response + * @see enableDynamicAck() + * @see setAutoAck() + * @see write() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0), NOACK (1) + */ + bool write( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command * + * This will not block until the 3 FIFO buffers are filled with data. + * Once the FIFOs are full, writeFast will simply wait for success or + * timeout, and return 1 or 0 respectively. From a user perspective, just + * keep trying to send the same data. The library will keep auto retrying + * the current payload using the built in functionality. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeFast is called + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(1000); // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0. + * @endcode + * + * @see txStandBy() + * @see write() + * @see writeBlocking() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool writeFast( const void* buf, uint8_t len ); + + /** + * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + */ + bool writeFast( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command + * This function extends the auto-retry mechanism to any specified duration. + * It will not block until the 3 FIFO buffers are filled with data. + * If so the library will auto retry until a new payload is written + * or the user specified timeout period is reached. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeBlocking is called + * @code + * Example (Full blocking): + * + * radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers + * txStandBy(1000); //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed. + * //Blocks only until user timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @see txStandBy() + * @see write() + * @see writeFast() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param timeout User defined timeout in milliseconds. + * @return True if the payload was loaded into the buffer successfully false if not + */ + bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); + + /** + * @note Optimization: New Command + * This function should be called as soon as transmission is finished to + * drop the radio back to STANDBY-I mode. If not issued, the radio will + * remain in STANDBY-II mode which, per the data sheet, is not a recommended + * operating mode. + * + * @note When transmitting data in rapid succession, it is still recommended by + * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is + * time enough between sends for the FIFOs to empty. + * + * Relies on built-in auto retry functionality. + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. + * //Blocks only until MAX_RT timeout or success. Data flushed on fail. + * @endcode + * @see txStandBy(unsigned long timeout) + * @return True if transmission is successful + * + */ + bool txStandBy(); + + /** + * @note Optimization: New Command + * + * This function allows extended blocking and auto-retries per a user defined timeout + * @code + * Fully Blocking Example: + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. + * //Blocks only until user defined timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @param timeout Number of milliseconds to retry failed payloads + * @return True if transmission is successful + * + */ + bool txStandBy(uint32_t timeout); + + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time as there are only 3 FIFO buffers. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + /** + * Enable dynamic ACKs (single write multicasting) for chosen messages + * + * @note To enable full multicasting or per-pipe multicast, use setAutoAck() + * + * @warning This MUST be called prior to attempting single write NOACK calls + * @code + * radio.enableDynamicAck(); + * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested + * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK + * @endcode + */ + void enableDynamicAck(); + + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @note Optimization: Calling this function NO LONGER clears the interrupt + * flag. The new functionality checks the RX FIFO buffer for an ACK payload + * instead of relying on interrupt flags.Reading the payload will clear the flags. + * + * @return True if an ack payload is available. + */ + bool isAckPayloadAvailable(void); + + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + + /** + * Non-blocking write to the open writing pipe used for buffered writes + * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * @see write() + * @see writeFast() + * @see startWrite() + * @see writeBlocking() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * @return True if the payload was delivered successfully false if not + */ + void startFastWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @note Optimization: This function again behaves as it did previously for backwards-compatibility. + * with user code. The library uses startFastWrite() internally. + * This is mainly used for single-payload transactions. + * + * @see write() + * @see writeFast() + * @see startFastWrite() + * @see whatHappened() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * + */ + void startWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Optimization: New Command + * + * This function is mainly used internally to take advantage of the auto payload + * re-use functionality of the chip, but can be beneficial to users as well. + * + * The function will instruct the radio to re-use the data in the FIFO buffers, + * and instructs the radio to re-send once the timeout limit has been reached. + * Used by writeFast and writeBlocking to initiate retries when a TX failure + * occurs. Retries are automatically initiated except with the standard write(). + * This way, data is not flushed from the buffer until switching between modes. + * + * @note This is to be used AFTER auto-retry fails if wanting to resend + * using the built-in payload reuse features. + * After issuing reUseTX(), it will keep reending the same payload forever or until + * a payload is written to the FIFO, or a flush_tx command is given. + */ + void reUseTX(); + + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); + + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + bool testCarrier(void); + + /** + * Test whether a signal (carrier or otherwise) greater than + * or equal to -64dBm is present on the channel. Valid only + * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * + * Useful to check for interference on the current channel and + * channel hopping strategies. + * + * @return true if signal => -64dBm, false if not + */ + bool testRPD(void) ; + + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + + /** + * The radio will generate interrupt signals when a transmission is complete, + * a transmission fails, or a payload is received. This allows users to mask + * those interrupts to prevent them from generating a signal on the interrupt + * pin. + * + * @code + * Mask all interrupts except the receive interrupt: + * + * radio.maskIRQ(1,1,0); + * @endcode + * + * @param tx_ok Mask transmission complete interrupts + * @param tx_fail Mask transmit failure interrupts + * @param rx_ready Mask payload received interrupts + */ + void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); + + /**@}*/ + + /**@}*/ + /** + * @name Optional Configurators + * + * Methods you can use to get or set the configuration of the chip. + * None are required. Calling begin() sets up a reasonable set of + * defaults. + */ + /**@{*/ + + /** + * Set the number and delay of retries upon failed submit + * + * @param delay How long to wait between each retry, in multiples of 250us, + * max is 15. 0 means 250us, 15 means 4000us. + * @param count How many retries before giving up, max 15 + */ + void setRetries(uint8_t delay, uint8_t count); + + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); + + /** + * Set Static Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); + + /** + * Get Static Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void); + + /** + * Get Dynamic Payload Size + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @note Optimization: Corrupt packets are now detected and flushed per the + * manufacturer. + * + * @return Payload length of last-received dynamic payload + */ + uint8_t getDynamicPayloadSize(void); + + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde + */ + void enableAckPayload(void); + + /** + * Enable dynamically-sized payloads + * + * This way you don't always have to send large packets just to send them + * once in a while. This enables dynamic payloads on ALL pipes. + * + * @see examples/pingpair_pl/pingpair_dyn.pde + */ + void enableDynamicPayloads(void); + + /** + * Determine whether the hardware is an nRF24L01+ or not. + * + * @return true if the hardware is nRF24L01+ (or compatible) and false + * if its not. + */ + bool isPVariant(void) ; + + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); + + /** + * Enable or disable auto-acknowlede packets on a per pipeline basis. + * + * AA is enabled by default, so it's only needed if you want to turn + * it off/on for some reason on a per pipeline basis. + * + * @param pipe Which pipeline to modify + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck( uint8_t pipe, bool enable ) ; + + /** + * Set Power Amplifier (PA) level to one of four levels: + * RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX + * + * The power levels correspond to the following output levels respectively: + * NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm + * + * SI24R1: -6dBm, 0dBm, 3dBM, and 7dBm. + * + * @param level Desired PA level. + */ + void setPALevel ( uint8_t level ); + + /** + * Fetches the current PA level. + * + * NRF24L01: -18dBm, -12dBm, -6dBm and 0dBm + * SI24R1: -6dBm, 0dBm, 3dBm, 7dBm + * + * @return Returns values 0 to 3 representing the PA Level. + */ + uint8_t getPALevel( void ); + + /** + * Set the transmission data rate + * + * @warning setting RF24_250KBPS will fail for non-plus units + * + * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps + * @return true if the change was successful + */ + bool setDataRate(rf24_datarate_e speed); + + /** + * Fetches the transmission data rate + * + * @return Returns the hardware's currently configured datarate. The value + * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the + * rf24_datarate_e enum. + */ + rf24_datarate_e getDataRate( void ) ; + + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); + + /** + * Get the CRC length + * + * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + rf24_crclength_e getCRCLength(void); + + /** + * Disable CRC validation + * + */ + void disableCRC( void ) ; + +private: + + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. + */ + /**@{*/ + + /** + * Set chip select pin + * + * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data + * and best of all, we make use of the radio's FIFO buffers. A lower speed + * means we're less likely to effectively leverage our FIFOs and pay a higher + * AVR runtime cost as toll. + * + * @param mode HIGH to take this unit off the SPI bus, LOW to put it on + */ + void csn(bool mode); + + /** + * Set chip enable + * + * @param level HIGH to actively begin transmission or LOW to put in standby. Please see data sheet + * for a much more detailed description of this pin. + */ + void ce(bool level); + + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); + + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); + + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); + + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @param len Number of bytes to be sent + * @return Current value of status register + */ + uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); + + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register + */ + uint8_t read_payload(void* buf, uint8_t len); + + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); + + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void); + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status); + + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value); + + /** + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); + + /** + * Built in spi transfer function to simplify repeating code repeating code + */ + + uint8_t spiTrans(uint8_t cmd); + /**@}*/ + +}; + +/** + * @example GettingStarted.ino + * Updated: TMRh20 2014
+ * + * This is an example of how to use the RF24 class to communicate on a basic level. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting with the serial monitor and
+ * sending a 'T'. The ping node sends the current time to the pong node, which responds by sending the value + * back. The ping node can then see how long the whole cycle took.
+ * @note For a more efficient call-response scenario see the GettingStarted_CallResponse.ino example. + * @note When switching between sketches, the radio may need to be powered down to clear settings that are not "un-set" otherwise + */ + +/** + * @example GettingStarted_CallResponse.ino + * New: TMRh20 2014
+ * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to
+ * primary transmitter if wanting to initiate communication instead of respond to a commmunication. + */ + +/** + * @example Transfer.ino + * New: TMRh20
+ * This example demonstrates half-rate transfer using the FIFO buffers
+ * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting
+ * with the serial monitor and sending a 'T'. The data transfer will begin, + * with the receiver displaying the payload count. (32Byte Payloads)
+ */ + +/** + * @example TransferTimeouts.ino + * New: TMRh20
+ * This example demonstrates the use of and extended timeout period and + * auto-retries/auto-reUse to increase reliability in noisy or low signal scenarios.
+ * + * Write this sketch to two different nodes. Put one of the nodes into 'transmit' + * mode by connecting with the serial monitor and sending a 'T'. The data
+ * transfer will begin, with the receiver displaying the payload count and the + * data transfer rate. + */ + +/** + * @example starping.pde + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +/** + * @example pingpair_ack.ino + * Update: TMRh20
+ * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to if wanting
+ * to initiate communication instead of respond to a commmunication. + */ + +/** + * @example pingpair_irq.ino + * Update: TMRh20
+ * This is an example of how to user interrupts to interact with the radio, and a demonstration + * of how to use them to sleep when receiving, and not miss any payloads.
+ * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter. + * Sleep functionality is built directly into my fork of the RF24Network library
+ */ + +/** + * @example pingpair_sleepy.ino + * Update: TMRh20
+ * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the GettingStarted_CallResponse example, but the
+ * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle, and the receiver sleeps between payloads.
+ */ + +/** + * @example pingpair_dyn.ino + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + +/** + * @example led_remote.pde + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + +/** + * @example scanner.ino + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +/** + * @mainpage Optimized High Speed Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + * + * @section Goals Design Goals + * + * This library fork is designed to be... + * @li More compliant with the manufacturer specified operation of the chip, while allowing advanced users + * to work outside the reccommended operation. + * @li Utilize the capabilities of the radio to their full potential via Arduino + * @li More reliable, responsive and feature rich + * @li Easy for beginners to use, with well documented examples and features + * @li Consumed with a public interface that's similiar to other Arduino standard libraries + * + * @section News News + * + * April 2014: Official Release: Still some work to do, but most benefits have been realized
+ * - The library has been tweaked to allow full use of the FIFO buffers for maximum transfer speeds + * - Changes to read() and available () functionality have increased reliability and response + * - Extended timeout periods have been added to aid in noisy or otherwise unreliable environments + * - Delays have been removed where possible to ensure maximum efficiency + * - Full Due support with extended SPI functions + * - ATTiny 24/44/84 25/45/85 now supported. + * - More! See the links below and class documentation for more info. + * + * If issues are discovered with the documentation, please report them here: here + * @section Useful Useful References + * + * Please refer to: + * + * @li Documentation Main Page + * @li RF24 Class Documentation + * @li Source Code + * @li Downloads Page + * @li Chip Datasheet + * @li Original Library + * + * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or + * the SPI hardware will go into 'slave' mode. + * + * @section Board_Support Board Support + * + * Most standard Arduino based boards are supported: + * - ATMega 328 based boards (Uno, Nano, etc) + * - Mega Boards (1280, 2560, etc) + * - ARM based boards (Arduino Due) Note: Do not include printf.h or use printf begin. This functionality is already present. Must use one of the + * hardware SS/CSN pins as extended SPI methods are used. + * Initial Due support taken from https://github.com/mcrosson/RF24/tree/due + * - ATTiny board support added from https://github.com/jscrane/RF24 + * Note: ATTiny support is built into the library. Do not include SPI.h.
+ * ATTiny 85: D0(pin 5): MISO, D1(pin6) MOSI, D2(pin7) SCK, D3(pin2):CSN/SS, D4(pin3): CE
+ * ATTiny 84: PA6:MISO, PA5:MOSI, PA4:SCK, PA7:CSN/SS, CE as desired
+ * + * @section More More Information + * + * @section Info and Projects + * + * @li Project blog: + * @li TMRh20.blogspot.com + * @li RF24 Wireless Audio Library (Coming Soon) + * @li Optimized RF24 Network Layer + * @li ManiacBug on GitHub (Original Library Author) + */ + +#endif // __RF24_H__ + + diff --git a/libraries/MySensors/utility/RF24_config.h b/libraries/MySensors/utility/RF24_config.h new file mode 100644 index 0000000..b7154a5 --- /dev/null +++ b/libraries/MySensors/utility/RF24_config.h @@ -0,0 +1,129 @@ + +/* + Copyright (C) 2011 J. Coliz + + 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. + + Added Arduino Due support from https://github.com/mcrosson/ + */ + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ + + #if ARDUINO < 100 + #include + #else + #include + #endif + + #include + + //TMRh20: + //#define MINIMAL + + // Define _BV for non-Arduino platforms and for Arduino DUE +#if defined (ARDUINO) + #include +#else + #include + #include + #include + extern HardwareSPI SPI; + #define _BV(x) (1<<(x)) + #endif + + + #undef SERIAL_DEBUG + #ifdef SERIAL_DEBUG + #define IF_SERIAL_DEBUG(x) ({x;}) + #else + #define IF_SERIAL_DEBUG(x) + #if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) + #define printf_P(...) + #endif + #endif + +// Avoid spurious warnings +// Arduino DUE is arm and uses traditional PROGMEM constructs +#if 1 +#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) +#undef PROGMEM +#define PROGMEM __attribute__(( section(".progmem.data") )) +#undef PSTR +#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) +#endif +#endif + +// Progmem is Arduino-specific +// Arduino DUE is arm and does not include avr/pgmspace +#if defined(ARDUINO) && ! defined(__arm__) + #include + #define PRIPSTR "%S" +#else +#if ! defined(ARDUINO) // This doesn't work on Arduino DUE + typedef char const char; +#else // Fill in pgm_read_byte that is used, but missing from DUE + #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif + + typedef uint16_t prog_uint16_t; + #define PSTR(x) (x) + #define printf_P printf + #define strlen_P strlen + #define PROGMEM + #define pgm_read_word(p) (*(p)) + #define PRIPSTR "%s" +#endif + + + +// ATTiny support code is from https://github.com/jscrane/RF24 + +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +#include +#include +#include + +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 +//#define SPI_CLOCK_DIV64 0x07 + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + + + +class SPIClass { +public: + static byte transfer(byte _data); + + // SPI Configuration methods + + inline static void attachInterrupt(); + inline static void detachInterrupt(); // Default + + static void begin(); // Default + static void end(); + + static void setBitOrder(uint8_t); + static void setDataMode(uint8_t); + static void setClockDivider(uint8_t); +}; +extern SPIClass SPI; + +#endif //ATTiny +#endif // __RF24_CONFIG_H__ + diff --git a/libraries/MySensors/utility/nRF24L01.h b/libraries/MySensors/utility/nRF24L01.h new file mode 100644 index 0000000..132de94 --- /dev/null +++ b/libraries/MySensors/utility/nRF24L01.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2007 Stefan Engelke + Portions Copyright (C) 2011 Greg Copeland + + 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. +*/ + +/* Memory Map */ +#define CONFIG 0x00 +#define EN_AA 0x01 +#define EN_RXADDR 0x02 +#define SETUP_AW 0x03 +#define SETUP_RETR 0x04 +#define RF_CH 0x05 +#define RF_SETUP 0x06 +#define STATUS 0x07 +#define OBSERVE_TX 0x08 +#define CD 0x09 +#define RX_ADDR_P0 0x0A +#define RX_ADDR_P1 0x0B +#define RX_ADDR_P2 0x0C +#define RX_ADDR_P3 0x0D +#define RX_ADDR_P4 0x0E +#define RX_ADDR_P5 0x0F +#define TX_ADDR 0x10 +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D + +/* Bit Mnemonics */ +#define MASK_RX_DR 6 +#define MASK_TX_DS 5 +#define MASK_MAX_RT 4 +#define EN_CRC 3 +#define CRCO 2 +#define PWR_UP 1 +#define PRIM_RX 0 +#define ENAA_P5 5 +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW 0 +#define ARD 4 +#define ARC 0 +#define PLL_LOCK 4 +#define RF_DR 3 +#define RF_PWR 6 +#define RX_DR 6 +#define TX_DS 5 +#define MAX_RT 4 +#define RX_P_NO 1 +#define TX_FULL 0 +#define PLOS_CNT 4 +#define ARC_CNT 0 +#define TX_REUSE 6 +#define FIFO_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 + +/* Instruction Mnemonics */ +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define NOP 0xFF + +/* Non-P omissions */ +#define LNA_HCURR 0 + +/* P model memory Map */ +#define RPD 0x09 +#define W_TX_PAYLOAD_NO_ACK 0xB0 + +/* P model bit Mnemonics */ +#define RF_DR_LOW 5 +#define RF_DR_HIGH 3 +#define RF_PWR_LOW 1 +#define RF_PWR_HIGH 2 From f3bd9e9b305986e229ec34b75d40c8c02165ae54 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 26 Jun 2014 00:26:45 +0200 Subject: [PATCH 261/326] Change default transfer speed to 250kbps. Set default value for config. Return wakeup trigger after sleep. --- libraries/MySensors/MyConfig.h | 2 +- libraries/MySensors/MySensor.cpp | 11 +++++++++-- libraries/MySensors/MySensor.h | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 474b705..cbbc5e3 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -5,7 +5,7 @@ * Configure Sensor Network */ #define RF24_CHANNEL 76 //RF channel for the sensor net, 0-127 -#define RF24_DATARATE RF24_1MBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps +#define RF24_DATARATE RF24_250KBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps #define RF24_PA_LEVEL RF24_PA_MAX //Sensor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_HIGH=0dBm #define RF24_PA_LEVEL_GW RF24_PA_LOW //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 2bc4091..ad4e055 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -46,7 +46,10 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); // Read latest received controller configuration from EEPROM eeprom_read_block((void*)&cc, (void*)EEPROM_LOCAL_CONFIG_ADDRESS, sizeof(ControllerConfig)); - + if (cc.isMetric == 0xff) { + // Eeprom empty, set default to metric + cc.isMetric = 0x01; + } if (_nodeId != AUTO) { // Set static id @@ -541,18 +544,22 @@ void MySensor::sleep(int ms) { internalSleep(ms); } -void MySensor::sleep(int interrupt, int mode, int ms) { +bool MySensor::sleep(int interrupt, int mode, int ms) { // Let serial prints finish (debug, log etc) + bool pinTriggeredWakeup = true; Serial.flush(); RF24::powerDown(); attachInterrupt(interrupt, wakeUp, mode); //Interrupt on pin 3 for any change in solar power if (ms>0) { continueTimer = true; sleep(ms); + pinTriggeredWakeup = !continueTimer; } else { + Serial.flush(); LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); } detachInterrupt(interrupt); + return pinTriggeredWakeup; } diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index eaad9b0..b141837 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -211,8 +211,9 @@ class MySensor : public RF24 * @param interrupt Interrupt that should trigger the wakeup * @param mode RISING, FALLING, CHANGE * @param ms Number of milliseconds to sleep or 0 to sleep forever + * @return true if wake up was triggered by pin change and false means timer woke it up. */ - void sleep(int interrupt, int mode, int ms=0); + bool sleep(int interrupt, int mode, int ms=0); /** * getInternalTemp From 9cf6c03c5d9b65d4f53bfa1957f81e2dbb53e8d9 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 30 Jun 2014 23:45:18 +0200 Subject: [PATCH 262/326] Fix commend and wrong default pa level for gateway --- libraries/MySensors/MyConfig.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index cbbc5e3..f959650 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -6,8 +6,8 @@ */ #define RF24_CHANNEL 76 //RF channel for the sensor net, 0-127 #define RF24_DATARATE RF24_250KBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps -#define RF24_PA_LEVEL RF24_PA_MAX //Sensor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_HIGH=0dBm -#define RF24_PA_LEVEL_GW RF24_PA_LOW //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. +#define RF24_PA_LEVEL RF24_PA_MAX //Sensor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_MAX=0dBm +#define RF24_PA_LEVEL_GW RF24_PA_LEVEL //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. /*** * Enable/Disable debug logging From 17e476977022112c0e614766b39f6896fa8a7881 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 30 Jun 2014 23:47:46 +0200 Subject: [PATCH 263/326] Remove application layer crc check. Rearrange of message header to keep non routing information at the end (preparation for OTA firmware messages) --- libraries/MySensors/MyGateway.cpp | 10 ----- libraries/MySensors/MyGateway.h | 1 - libraries/MySensors/MyMessage.h | 10 +---- libraries/MySensors/MySensor.cpp | 70 +++---------------------------- libraries/MySensors/MySensor.h | 1 - 5 files changed, 7 insertions(+), 85 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index f3c7936..9040bbc 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -181,16 +181,6 @@ void MyGateway::setInclusionMode(boolean newMode) { } } - -// Override normal validate to add error blink if crc check fails -uint8_t MyGateway::validate(MyMessage &message) { - uint8_t res = MySensor::validate(message); - if (res == VALIDATE_BAD_CRC) { - errBlink(1); - } - return res; -} - void MyGateway::processRadioMessage() { if (process()) { // A new message was received from one of the sensors diff --git a/libraries/MySensors/MyGateway.h b/libraries/MySensors/MyGateway.h index 8b520e5..185d933 100644 --- a/libraries/MySensors/MyGateway.h +++ b/libraries/MySensors/MyGateway.h @@ -70,7 +70,6 @@ class MyGateway : public MySensor void serial(const char *fmt, ... ); void serial(MyMessage &msg); - uint8_t validate(MyMessage &message); void interruptStartInclusion(); void checkButtonTriggeredInclusion(); void setInclusionMode(boolean newMode); diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 08acf63..c401870 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -60,11 +60,6 @@ typedef enum { P_STRING, P_BYTE, P_INT16, P_UINT16, P_LONG32, P_ULONG32, P_CUSTOM } payload; -// Possible return values by validate() when doing crc check of received message. -typedef enum { - VALIDATE_OK, VALIDATE_BAD_CRC, VALIDATE_BAD_VERSION -} validationResult; - #define BIT(n) ( 1<<(n) ) @@ -148,12 +143,11 @@ public: uint8_t command_ack_payload; // 3 bit - Command type // 1 bit - Indicator that receiver should send an ack back. // 4 bit - Payload data type - uint8_t type; // 8 bit - Type varies depending on command - uint8_t sensor; // 8 bit - Id of sensor that this message concerns. uint8_t sender; // 8 bit - Id of sender node uint8_t last; // 8 bit - Id of last node this message passed uint8_t destination; // 8 bit - Id of destination node - uint8_t crc; // 8 bit - Message checksum + uint8_t type; // 8 bit - Type varies depending on command + uint8_t sensor; // 8 bit - Id of sensor that this message concerns. // Each message can transfer a payload. We add one extra byte for string // terminator \0 to be "printable" this is not transferred OTA diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index ad4e055..d999617 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -202,7 +202,6 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) { uint8_t length = mGetLength(message); message.last = nc.nodeId; mSetVersion(message, PROTOCOL_VERSION); - message.crc = crc8Message(message); // Make sure radio has powered up RF24::powerUp(); RF24::stopListening(); @@ -262,16 +261,15 @@ boolean MySensor::process() { RF24::read(&msg, len); RF24::writeAckPayload(pipe,&pipe, 1 ); - uint8_t valid = validate(msg); - boolean ok = valid == VALIDATE_OK; - // Add termination, good for string messages. msg.data[mGetLength(msg)] = '\0'; - debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,cr=%s:%s\n"), - msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), valid==0?"ok":valid==1?"ec":"ev", msg.getString(convBuf)); + debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d:%s\n"), + msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), msg.getString(convBuf)); - if (!ok) + if(!(mGetVersion(msg) == PROTOCOL_VERSION)) { + debug(PSTR("version mismatch")); return false; + } uint8_t command = mGetCommand(msg); uint8_t type = msg.type; @@ -405,10 +403,7 @@ boolean MySensor::process() { // Add this child to our "routing table" if it not already exist addChildRoute(sender, last); } - } - - return false; } @@ -418,61 +413,6 @@ MyMessage& MySensor::getLastMessage() { } -/* - * calculate CRC8 on MyMessage data taking care of data structure and protocol version - */ -uint8_t MySensor::crc8Message(MyMessage &message) { - uint8_t len = mGetLength(message); - uint8_t crc = 0x00; - uint8_t loop_count; - uint8_t bit_counter; - uint8_t data; - uint8_t feedback_bit; - uint8_t number_of_bytes_to_read = (uint8_t)sizeof(MyMessage); - - // Must set crc to a constant value. - message.crc = 0; - - // fill unused space by zeroes for string data only - if(len>=0 && len < sizeof(message.data)) { - memset(&message.data[len], 0, sizeof(message.data) - len); - } - - for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) - { - data = ((uint8_t*)&message)[loop_count]; - - bit_counter = 8; - do { - feedback_bit = (crc ^ data) & 0x01; - - if ( feedback_bit == 0x01 ) { - crc = crc ^ 0x18; //0X18 = X^8+X^5+X^4+X^0 - } - crc = (crc >> 1) & 0x7F; - if ( feedback_bit == 0x01 ) { - crc = crc | 0x80; - } - - data = data >> 1; - bit_counter--; - - } while (bit_counter > 0); - } - return crc; -} - - -uint8_t MySensor::validate(MyMessage &message) { - uint8_t oldCrc = message.crc; - uint8_t newCrc = crc8Message(message); - - if(!(mGetVersion(message) == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; - - if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; - return VALIDATE_OK; -} - void MySensor::saveState(uint8_t pos, uint8_t value) { if (loadState(pos) != value) { eeprom_write_byte((uint8_t*)(EEPROM_LOCAL_CONFIG_ADDRESS+pos), value); diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index b141837..2ff9cab 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -245,7 +245,6 @@ class MySensor : public RF24 void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); boolean sendRoute(MyMessage &message); boolean sendWrite(uint8_t dest, MyMessage &message, bool broadcast=false); - uint8_t validate(MyMessage &message); private: #ifdef DEBUG From 7b4bef5be8f5e6c507a6659fca398e23aae6da2c Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 2 Jul 2014 23:15:49 +0200 Subject: [PATCH 264/326] Added MQ2 Air quality sensor by epierre (converted to 1.4) --- libraries/MySensors/MyMessage.h | 2 +- .../AirQualitySensor/AirQualitySensor.ino | 196 ++++++++++++++++++ 2 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 libraries/MySensors/examples/AirQualitySensor/AirQualitySensor.ino diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index c401870..c3ae7e3 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -48,7 +48,7 @@ typedef enum { typedef enum { S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, 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_ARDUINO_REPEATER_NODE, S_LOCK, S_IR, S_WATER, S_AIR_QUALITY } sensor; // Type of data stream (for streamed message) diff --git a/libraries/MySensors/examples/AirQualitySensor/AirQualitySensor.ino b/libraries/MySensors/examples/AirQualitySensor/AirQualitySensor.ino new file mode 100644 index 0000000..efe12f3 --- /dev/null +++ b/libraries/MySensors/examples/AirQualitySensor/AirQualitySensor.ino @@ -0,0 +1,196 @@ +/* + Vera Arduino MQ2 + + connect the MQ2 sensor as follows : + + A H A >>> 5V + B >>> A0 + H >>> GND + B >>> 10K ohm >>> GND + + Contribution: epierre + Based on http://sandboxelectronics.com/?p=165 + + License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) + + Modified by HEK to work in 1.4 +*/ + +#include +#include +#include + +#define CHILD_ID_MQ 0 +/************************Hardware Related Macros************************************/ +#define MQ_SENSOR_ANALOG_PIN (0) //define which analog input channel you are going to use +#define RL_VALUE (5) //define the load resistance on the board, in kilo ohms +#define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO, + //which is derived from the chart in datasheet +/***********************Software Related Macros************************************/ +#define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase +#define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interal(in milisecond) between each samples in the + //cablibration phase +#define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation +#define READ_SAMPLE_TIMES (5) //define the time interal(in milisecond) between each samples in + //normal operation +/**********************Application Related Macros**********************************/ +#define GAS_LPG (0) +#define GAS_CO (1) +#define GAS_SMOKE (2) +/*****************************Globals***********************************************/ +unsigned long 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; +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. + //data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59) +float COCurve[3] = {2.3,0.72,-0.34}; //two points are taken from the curve. + //with these two points, a line is formed which is "approximately equivalent" + //to the original curve. + //data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000, 0.15) +float SmokeCurve[3] ={2.3,0.53,-0.44}; //two points are taken from the curve. + //with these two points, a line is formed which is "approximately equivalent" + //to the original curve. + //data format:{ x, y, slope}; point1: (lg200, 0.53), point2:(lg10000,-0.22) + + +MySensor gw; +MyMessage msg(CHILD_ID_MQ, V_VAR1); + + +void setup() +{ + gw.begin(); + + // Send the sketch version information to the gateway and Controller + gw.sendSketchInfo("Air Quality Sensor", "1.0"); + + // Register all sensors to gateway (they will be created as child devices) + gw.present(CHILD_ID_MQ, S_AIR_QUALITY); + + Ro = MQCalibration(MQ_SENSOR_ANALOG_PIN); //Calibrating the sensor. Please make sure the sensor is in clean air + //when you perform the calibration +} + +void loop() +{ + uint16_t valMQ = MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO); + Serial.println(val); + + Serial.print("LPG:"); + Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_LPG) ); + Serial.print( "ppm" ); + Serial.print(" "); + Serial.print("CO:"); + Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO) ); + Serial.print( "ppm" ); + Serial.print(" "); + Serial.print("SMOKE:"); + Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_SMOKE) ); + Serial.print( "ppm" ); + Serial.print("\n"); + + if (valMQ != lastMQ) { + gw.send(msg.set((int)ceil(valMQ))); + lastMQ = ceil(valMQ); + } + + gw.sleep(SLEEP_TIME); //sleep for: sleepTime +} + +/****************** MQResistanceCalculation **************************************** +Input: raw_adc - raw value read from adc, which represents the voltage +Output: the calculated sensor resistance +Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage + across the load resistor and its resistance, the resistance of the sensor + could be derived. +************************************************************************************/ +float MQResistanceCalculation(int raw_adc) +{ + return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc)); +} + +/***************************** MQCalibration **************************************** +Input: mq_pin - analog channel +Output: Ro of the sensor +Remarks: This function assumes that the sensor is in clean air. It use + MQResistanceCalculation to calculates the sensor resistance in clean air + and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about + 10, which differs slightly between different sensors. +************************************************************************************/ +float MQCalibration(int mq_pin) +{ + int i; + float val=0; + + for (i=0;i Date: Thu, 3 Jul 2014 12:41:49 +0200 Subject: [PATCH 265/326] Allow static parent node to be set when calling begin (which also disables auto-parent-finding) --- libraries/MySensors/MySensor.cpp | 15 +++++++++++---- libraries/MySensors/MySensor.h | 4 +++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index d999617..a0ddcf2 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -31,7 +31,7 @@ MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } -void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { +void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, uint8_t _parentNodeId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { Serial.begin(BAUD_RATE); isGateway = false; repeaterMode = _repeaterMode; @@ -51,13 +51,20 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b cc.isMetric = 0x01; } + if (_parentNodeId != AUTO) { + nc.parentNodeId = _parentNodeId; + autoFindParent = false; + } else { + autoFindParent = true; + } + if (_nodeId != AUTO) { // Set static id nc.nodeId = _nodeId; } - // If no parent was found. Try to find one. - if (nc.parentNodeId == 0xff) { + // If no parent was found in eeprom. Try to find one. + if (autoFindParent && nc.parentNodeId == 0xff) { findParentNode(); } @@ -186,7 +193,7 @@ boolean MySensor::sendRoute(MyMessage &message) { if (!ok) { // Failure when sending to parent node. The parent node might be down and we // need to find another route to gateway. - if (failedTransmissions > SEARCH_FAILURES) { + if (autoFindParent && failedTransmissions > SEARCH_FAILURES) { findParentNode(); } failedTransmissions++; diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 2ff9cab..6e16071 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -95,12 +95,13 @@ class MySensor : public RF24 * @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL. * @param nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. * @param repeaterMode Activate repeater mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false + * @param parentNodeId Use this to force node to always communicate with a certain parent node. Default is AUTO which means node automatically tries to find a parent. * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX * @param channel Radio channel. Default is channel 76 * @param dataRate Radio transmission speed. Default RF24_1MBPS */ - void begin(void (* msgCallback)(const MyMessage &)=NULL, uint8_t nodeId=AUTO, boolean repeaterMode=false, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); + void begin(void (* msgCallback)(const MyMessage &)=NULL, uint8_t nodeId=AUTO, boolean repeaterMode=false, uint8_t parentNodeId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); /** * Return the nodes nodeId. @@ -237,6 +238,7 @@ class MySensor : public RF24 NodeConfig nc; // Essential settings for node to work ControllerConfig cc; // Configuration coming from controller bool repeaterMode; + bool autoFindParent; bool isGateway; MyMessage msg; // Buffer for incoming messages. MyMessage ack; // Buffer for ack messages. From 0cd56e88b2be9d9963bceb278e073fd7a141e76f Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 10 Jul 2014 23:47:23 +0200 Subject: [PATCH 266/326] Changed faulty comment. --- .../RelayWithButtonActuator/RelayWithButtonActuator.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino index 40187c4..e8b4317 100644 --- a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino +++ b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino @@ -1,6 +1,6 @@ // Example sketch för a "light switch" where you can control light or something // else from both vera and a local physical button (connected between digital -// pin 3 and +5V). +// pin 3 and GND). // This node also works as a repeader for other nodes #include From ef91d9b9cc00e17b11ec8648529e7d112f9c8130 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sat, 12 Jul 2014 23:49:27 +0200 Subject: [PATCH 267/326] New ack bit in header indicating of message is an actual ack of a message. Use msg.getAck() to check in incoming message callback. --- libraries/MySensors/MyGateway.cpp | 3 ++- libraries/MySensors/MyMessage.cpp | 5 ++++ libraries/MySensors/MyMessage.h | 42 +++++++++++++++++++++---------- libraries/MySensors/MySensor.cpp | 10 +++++--- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index 9040bbc..f9ce0e9 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -160,7 +160,8 @@ void MyGateway::parseAndSend(char *commandBuffer) { msg.sensor = sensor; msg.type = type; mSetCommand(msg,command); - mSetAck(msg,ack==0?false:true); + mSetRequestAck(msg,ack==0?false:true); + mSetAck(msg,false); msg.set(value); ok = sendRoute(msg); if (!ok) { diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 96ae96e..ed394b6 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -14,6 +14,11 @@ MyMessage::MyMessage(uint8_t _sensor, uint8_t _type) { } +bool MyMessage::isAck() const { + return miGetAck(); +} + + /* Getters for payload converted to desired form */ void* MyMessage::getCustom() const { return (void *)data; diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index c3ae7e3..2c5dc21 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -74,28 +74,40 @@ typedef enum { // Insert a new bitfield value x into y. #define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) -// Getters/setters for special fields in header +// Getters/setters for special bit fields in header #define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 3) -#define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) - -#define mSetCommand(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 0, 3) -#define mSetAck(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 3, 1) -#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 4, 4) - #define mGetVersion(_msg) BF_GET(_msg.version_length, 0, 3) + +#define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) #define mGetLength(_msg) BF_GET(_msg.version_length, 3, 5) +#define mSetCommand(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 0, 3) #define mGetCommand(_msg) BF_GET(_msg.command_ack_payload, 0, 3) -#define mGetAck(_msg) BF_GET(_msg.command_ack_payload, 3, 1) -#define mGetPayloadType(_msg) BF_GET(_msg.command_ack_payload, 4, 4) + +#define mSetRequestAck(_msg,_rack) BF_SET(_msg.command_ack_payload, _rack, 3, 1) +#define mGetRequestAck(_msg) BF_GET(_msg.command_ack_payload, 3, 1) + +#define mSetAck(_msg,_ack) BF_SET(_msg.command_ack_payload, _ack, 4, 1) +#define mGetAck(_msg) BF_GET(_msg.command_ack_payload, 4, 1) + +#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 5, 3) +#define mGetPayloadType(_msg) BF_GET(_msg.command_ack_payload, 5, 3) // internal access for special fields +#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 4, 4) #define miGetPayloadType() BF_GET(command_ack_payload, 4, 4) -#define miGetLength() BF_GET(version_length, 3, 5) #define miSetLength(_length) BF_SET(version_length, _length, 3, 5) -#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 4, 4) +#define miGetLength() BF_GET(version_length, 3, 5) + +#define miSetRequestAck(_rack) BF_SET(command_ack_payload, _rack, 3, 1) +#define miGetRequestAck() BF_GET(command_ack_payload, 3, 1) + +#define miSetAck(_ack) BF_SET(command_ack_payload, _ack, 4, 1) +#define miGetAck() BF_GET(command_ack_payload, 4, 1) + + class MyMessage @@ -122,6 +134,9 @@ public: int getInt() const; unsigned int getUInt() const; + // Getter for ack-flag. True if this is an ack message. + bool isAck() const; + // Setters for building message "on the fly" MyMessage& setType(uint8_t type); MyMessage& setSensor(uint8_t sensor); @@ -141,8 +156,9 @@ public: uint8_t version_length; // 3 bit - Protocol version // 5 bit - Length of payload uint8_t command_ack_payload; // 3 bit - Command type - // 1 bit - Indicator that receiver should send an ack back. - // 4 bit - Payload data type + // 1 bit - Request an ack - Indicator that receiver should send an ack back. + // 1 bit - Is ack messsage - Indicator that this is the actual ack message. + // 3 bit - Payload data type uint8_t sender; // 8 bit - Id of sender node uint8_t last; // 8 bit - Id of last node this message passed uint8_t destination; // 8 bit - Id of destination node diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index a0ddcf2..ad342b2 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -22,7 +22,8 @@ inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, ui msg.sensor = sensor; msg.type = type; mSetCommand(msg,command); - mSetAck(msg,enableAck); + mSetRequestAck(msg,enableAck); + mSetAck(msg,false); return msg; } @@ -226,7 +227,7 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) { bool MySensor::send(MyMessage &message, bool enableAck) { message.sender = nc.nodeId; mSetCommand(message,C_SET); - mSetAck(message,enableAck); + mSetRequestAck(message,enableAck); return sendRoute(message); } @@ -293,10 +294,11 @@ boolean MySensor::process() { return false; } else if (destination == nc.nodeId) { // Check if sender requests an ack back. - if (mGetAck(msg)) { + if (mGetRequestAck(msg)) { // Copy message ack = msg; - mSetAck(ack,false); // Reply without ack flag (otherwise we would end up in an eternal loop) + mSetRequestAck(ack,false); // Reply without ack flag (otherwise we would end up in an eternal loop) + mSetAck(ack,true); ack.sender = nc.nodeId; ack.destination = msg.sender; sendRoute(ack); From 767fedca5b84d4e33f51b2540f5c3d11718985a1 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 13 Jul 2014 00:02:20 +0200 Subject: [PATCH 268/326] Make sure to terminate strings correctly. --- libraries/MySensors/MyMessage.cpp | 3 ++- libraries/MySensors/MySensor.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index ed394b6..89addeb 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -37,7 +37,8 @@ char* MyMessage::getString(char *buffer) const { uint8_t payloadType = miGetPayloadType(); if (payloadType == P_STRING) { - strcpy(buffer, data); + strncpy(buffer, data, miGetLength()); + buffer[miGetLength()] = '\0'; return buffer; } else if (buffer != NULL) { switch(payloadType) { diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index ad342b2..f8a49bd 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -269,7 +269,7 @@ boolean MySensor::process() { RF24::read(&msg, len); RF24::writeAckPayload(pipe,&pipe, 1 ); - // Add termination, good for string messages. + // Add string termination, good if we later would want to print it. msg.data[mGetLength(msg)] = '\0'; debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d:%s\n"), msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), msg.getString(convBuf)); From 8442132ed624fe628055ea42392ca6931ba3a576 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 18 Jul 2014 21:21:54 +0200 Subject: [PATCH 269/326] Fix problem with sleeptime reported by @marceltrapman. --- libraries/MySensors/MySensor.cpp | 6 +++--- libraries/MySensors/MySensor.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index f8a49bd..7c2933f 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -472,7 +472,7 @@ void wakeUp() //place to send the interrupts continueTimer = false; } -void MySensor::internalSleep(int ms) { +void MySensor::internalSleep(unsigned long ms) { while (continueTimer && ms >= 8000) { LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); ms -= 8000; } if (continueTimer && ms >= 4000) { LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); ms -= 4000; } if (continueTimer && ms >= 2000) { LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); ms -= 2000; } @@ -485,7 +485,7 @@ void MySensor::internalSleep(int ms) { if (continueTimer && ms >= 16) { LowPower.powerDown(SLEEP_15Ms, ADC_OFF, BOD_OFF); ms -= 15; } } -void MySensor::sleep(int ms) { +void MySensor::sleep(unsigned long ms) { // Let serial prints finish (debug, log etc) Serial.flush(); RF24::powerDown(); @@ -493,7 +493,7 @@ void MySensor::sleep(int ms) { internalSleep(ms); } -bool MySensor::sleep(int interrupt, int mode, int ms) { +bool MySensor::sleep(int interrupt, int mode, unsigned long ms) { // Let serial prints finish (debug, log etc) bool pinTriggeredWakeup = true; Serial.flush(); diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 6e16071..38f16e4 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -203,7 +203,7 @@ class MySensor : public RF24 * Sleep (PowerDownMode) the Arduino and radio. Wake up on timer. * @param ms Number of milliseconds to sleep. */ - void sleep(int ms); + void sleep(unsigned long ms); /** * Sleep (PowerDownMode) the Arduino and radio. Wake up on timer or pin change. @@ -214,7 +214,7 @@ class MySensor : public RF24 * @param ms Number of milliseconds to sleep or 0 to sleep forever * @return true if wake up was triggered by pin change and false means timer woke it up. */ - bool sleep(int interrupt, int mode, int ms=0); + bool sleep(int interrupt, int mode, unsigned long ms=0); /** * getInternalTemp @@ -264,7 +264,7 @@ class MySensor : public RF24 uint8_t getChildRoute(uint8_t childId); void addChildRoute(uint8_t childId, uint8_t route); void removeChildRoute(uint8_t childId); - void internalSleep(int ms); + void internalSleep(unsigned long ms); }; #endif From a845691b8b79848d0dfe2bcb9658a6fee69c6b37 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Tue, 22 Jul 2014 08:26:57 +0200 Subject: [PATCH 270/326] Fix a few sketch problems found. --- EthernetGateway/EthernetGateway.ino | 1 - .../examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino | 2 +- .../examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/EthernetGateway/EthernetGateway.ino b/EthernetGateway/EthernetGateway.ino index 9059a07..1144227 100644 --- a/EthernetGateway/EthernetGateway.ino +++ b/EthernetGateway/EthernetGateway.ino @@ -49,7 +49,6 @@ #include #include -#include #include #include #include diff --git a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino index 7833171..4049473 100644 --- a/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino +++ b/libraries/MySensors/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino @@ -55,7 +55,7 @@ void loop() gw.process(); unsigned long now = millis(); // Only send values at a maximum frequency or woken up from sleep - bool sendTime = now - lastSend > 1000*SEND_FREQUENCY; + bool sendTime = now - lastSend > SEND_FREQUENCY; if (pcReceived && (SLEEP_MODE || sendTime)) { // New watt value has been calculated if (!SLEEP_MODE && watt != oldWatt) { diff --git a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino index 93caa68..5905e8c 100644 --- a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino +++ b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino @@ -66,7 +66,7 @@ void loop() currentTime = millis(); // Only send values at a maximum frequency or woken up from sleep - bool sendTime = currentTime - lastSend > 1000*SEND_FREQUENCY; + bool sendTime = currentTime - lastSend > SEND_FREQUENCY; if (pcReceived && (SLEEP_MODE || sendTime)) { // New flow value has been calculated if (!SLEEP_MODE && flow != oldflow) { From 10e2d31a2f38bf15f6e6f447d45c1f9aac0b4f02 Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Thu, 24 Jul 2014 13:29:35 +0200 Subject: [PATCH 271/326] Misc fixes - changed HEADER_SIZE constant from 8 to 7 to reflect message structure - removed duplicate utility folder in UIPEthernet that prevented proper compilation - removed unnecessary includes in EthernetGateway that prevented proper compilation --- EthernetGateway/EthernetGateway.ino | 2 - libraries/MySensors/MyMessage.h | 2 +- .../UIPEthernet/utility/Enc28J60Network.cpp | 554 ----- .../UIPEthernet/utility/Enc28J60Network.h | 86 - libraries/UIPEthernet/utility/clock-arch.c | 28 - libraries/UIPEthernet/utility/clock-arch.h | 27 - libraries/UIPEthernet/utility/enc28j60.h | 257 --- libraries/UIPEthernet/utility/mempool.cpp | 164 -- libraries/UIPEthernet/utility/mempool.h | 71 - libraries/UIPEthernet/utility/mempool_conf.h | 28 - libraries/UIPEthernet/utility/uip-conf.h | 185 -- libraries/UIPEthernet/utility/uip-neighbor.c | 158 -- libraries/UIPEthernet/utility/uip-neighbor.h | 61 - libraries/UIPEthernet/utility/uip.c | 1897 ----------------- libraries/UIPEthernet/utility/uip.h | 1601 -------------- libraries/UIPEthernet/utility/uip_arch.h | 138 -- libraries/UIPEthernet/utility/uip_arp.c | 423 ---- libraries/UIPEthernet/utility/uip_arp.h | 144 -- libraries/UIPEthernet/utility/uip_clock.h | 88 - libraries/UIPEthernet/utility/uip_debug.cpp | 170 -- libraries/UIPEthernet/utility/uip_debug.h | 18 - libraries/UIPEthernet/utility/uip_timer.c | 127 -- libraries/UIPEthernet/utility/uip_timer.h | 86 - .../UIPEthernet/utility/uipethernet-conf.h | 18 - libraries/UIPEthernet/utility/uipopt.h | 543 ----- libraries/UIPEthernet/utility/util.h | 13 - 26 files changed, 1 insertion(+), 6888 deletions(-) delete mode 100644 libraries/UIPEthernet/utility/Enc28J60Network.cpp delete mode 100644 libraries/UIPEthernet/utility/Enc28J60Network.h delete mode 100644 libraries/UIPEthernet/utility/clock-arch.c delete mode 100644 libraries/UIPEthernet/utility/clock-arch.h delete mode 100644 libraries/UIPEthernet/utility/enc28j60.h delete mode 100644 libraries/UIPEthernet/utility/mempool.cpp delete mode 100644 libraries/UIPEthernet/utility/mempool.h delete mode 100644 libraries/UIPEthernet/utility/mempool_conf.h delete mode 100644 libraries/UIPEthernet/utility/uip-conf.h delete mode 100644 libraries/UIPEthernet/utility/uip-neighbor.c delete mode 100644 libraries/UIPEthernet/utility/uip-neighbor.h delete mode 100644 libraries/UIPEthernet/utility/uip.c delete mode 100644 libraries/UIPEthernet/utility/uip.h delete mode 100644 libraries/UIPEthernet/utility/uip_arch.h delete mode 100644 libraries/UIPEthernet/utility/uip_arp.c delete mode 100644 libraries/UIPEthernet/utility/uip_arp.h delete mode 100644 libraries/UIPEthernet/utility/uip_clock.h delete mode 100644 libraries/UIPEthernet/utility/uip_debug.cpp delete mode 100644 libraries/UIPEthernet/utility/uip_debug.h delete mode 100644 libraries/UIPEthernet/utility/uip_timer.c delete mode 100644 libraries/UIPEthernet/utility/uip_timer.h delete mode 100644 libraries/UIPEthernet/utility/uipethernet-conf.h delete mode 100644 libraries/UIPEthernet/utility/uipopt.h delete mode 100644 libraries/UIPEthernet/utility/util.h diff --git a/EthernetGateway/EthernetGateway.ino b/EthernetGateway/EthernetGateway.ino index 9059a07..ddafef1 100644 --- a/EthernetGateway/EthernetGateway.ino +++ b/EthernetGateway/EthernetGateway.ino @@ -48,8 +48,6 @@ #include -#include -#include #include #include #include diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index c3ae7e3..34deb03 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -16,7 +16,7 @@ #define PROTOCOL_VERSION 2 #define MAX_MESSAGE_LENGTH 32 -#define HEADER_SIZE 8 +#define HEADER_SIZE 7 #define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) // Message types diff --git a/libraries/UIPEthernet/utility/Enc28J60Network.cpp b/libraries/UIPEthernet/utility/Enc28J60Network.cpp deleted file mode 100644 index baf5b11..0000000 --- a/libraries/UIPEthernet/utility/Enc28J60Network.cpp +++ /dev/null @@ -1,554 +0,0 @@ -/* - Enc28J60Network.h - UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. - - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - based on enc28j60.c file from the AVRlib library by Pascal Stang. - For AVRlib See http://www.procyonengineering.com/ - - 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 . - */ - -#include "Enc28J60Network.h" -#include "Arduino.h" - -extern "C" { -#include -#include "enc28j60.h" -#include "uip.h" -} - -#ifdef ENC28J60DEBUG -#include "HardwareSerial.h" -#endif - -// set CS to 0 = active -#define CSACTIVE digitalWrite(ENC28J60_CONTROL_CS, LOW) -// set CS to 1 = passive -#define CSPASSIVE digitalWrite(ENC28J60_CONTROL_CS, HIGH) -// -#define waitspi() while(!(SPSR&(1< ip checksum for theses bytes=f7f9 - // in binary these poitions are:11 0000 0011 1111 - // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 - //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN! - writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); - writeRegPair(EPMM0, 0x303f); - writeRegPair(EPMCSL, 0xf7f9); - // - // - // do bank 2 stuff - // enable MAC receive - // and bring MAC out of reset (writes 0x00 to MACON2) - writeRegPair(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); - // enable automatic padding to 60bytes and CRC operations - writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); - // set inter-frame gap (non-back-to-back) - writeRegPair(MAIPGL, 0x0C12); - // set inter-frame gap (back-to-back) - writeReg(MABBIPG, 0x12); - // Set the maximum packet size which the controller will accept - // Do not send packets longer than MAX_FRAMELEN: - writeRegPair(MAMXFLL, MAX_FRAMELEN); - // do bank 3 stuff - // write MAC address - // NOTE: MAC address in ENC28J60 is byte-backward - writeReg(MAADR5, macaddr[0]); - writeReg(MAADR4, macaddr[1]); - writeReg(MAADR3, macaddr[2]); - writeReg(MAADR2, macaddr[3]); - writeReg(MAADR1, macaddr[4]); - writeReg(MAADR0, macaddr[5]); - // no loopback of transmitted frames - phyWrite(PHCON2, PHCON2_HDLDIS); - // switch to bank 0 - setBank(ECON1); - // enable interrutps - writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); - // enable packet reception - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); - //Configure leds - phyWrite(PHLCON,0x476); -} - -memhandle -Enc28J60Network::receivePacket() -{ - uint16_t rxstat; - uint16_t len; - // check if a packet has been received and buffered - //if( !(readReg(EIR) & EIR_PKTIF) ){ - // The above does not work. See Rev. B4 Silicon Errata point 6. - if (readReg(EPKTCNT) != 0) - { - uint16_t readPtr = nextPacketPtr+6; - // Set the read pointer to the start of the received packet - writeRegPair(ERDPTL, nextPacketPtr); - // read the next packet pointer - nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); - nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - // read the packet length (see datasheet page 43) - len = readOp(ENC28J60_READ_BUF_MEM, 0); - len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - len -= 4; //remove the CRC count - // read the receive status (see datasheet page 43) - rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); - rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - // decrement the packet counter indicate we are done with this packet - writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - // check CRC and symbol errors (see datasheet page 44, table 7-3): - // The ERXFCON.CRCEN is set by default. Normally we should not - // need to check this. - if ((rxstat & 0x80) != 0) - { - receivePkt.begin = readPtr; - receivePkt.size = len; - return UIP_RECEIVEBUFFERHANDLE; - } - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out - setERXRDPT(); - } - return (NOBLOCK); -} - -void -Enc28J60Network::setERXRDPT() -{ - writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr-1); -} - -memaddress -Enc28J60Network::blockSize(memhandle handle) -{ - return handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; -} - -void -Enc28J60Network::sendPacket(memhandle handle) -{ - memblock *packet = &blocks[handle]; - uint16_t start = packet->begin-1; - uint16_t end = start + packet->size; - - // backup data at control-byte position - uint8_t data = readByte(start); - // write control-byte (if not 0 anyway) - if (data) - writeByte(start, 0); - -#ifdef ENC28J60DEBUG - Serial.print("sendPacket("); - Serial.print(handle); - Serial.print(") ["); - Serial.print(start); - Serial.print("-"); - Serial.print(end); - Serial.println("]:"); - for (uint16_t i=start; i<=end; i++) - { - Serial.print(readByte(i),HEX); - Serial.print(" "); - } - Serial.println(); -#endif - - // TX start - writeRegPair(ETXSTL, start); - // Set the TXND pointer to correspond to the packet size given - writeRegPair(ETXNDL, end); - // send the contents of the transmit buffer onto the network - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); - // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. - if( (readReg(EIR) & EIR_TXERIF) ) - { - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); - } - - //restore data on control-byte position - if (data) - writeByte(start, data); -} - -uint16_t -Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) -{ - memblock *packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; - memaddress start = packet->begin + position; - - writeRegPair(ERDPTL, start); - - if (len > packet->size - position) - len = packet->size - position; - return len; -} - -uint16_t -Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) -{ - len = setReadPtr(handle, position, len); - readBuffer(len, buffer); - return len; -} - -uint16_t -Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) -{ - memblock *packet = &blocks[handle]; - uint16_t start = packet->begin + position; - - writeRegPair(EWRPTL, start); - - if (len > packet->size - position) - len = packet->size - position; - writeBuffer(len, buffer); - return len; -} - -uint8_t Enc28J60Network::readByte(uint16_t addr) -{ - writeRegPair(ERDPTL, addr); - - CSACTIVE; - // issue read command - SPDR = ENC28J60_READ_BUF_MEM; - waitspi(); - // read data - SPDR = 0x00; - waitspi(); - CSPASSIVE; - return (SPDR); -} - -void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) -{ - writeRegPair(EWRPTL, addr); - - CSACTIVE; - // issue write command - SPDR = ENC28J60_WRITE_BUF_MEM; - waitspi(); - // write data - SPDR = data; - waitspi(); - CSPASSIVE; -} - -void -Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) -{ - memblock *dest = &blocks[dest_pkt]; - memblock *src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; - memblock_mv_cb(dest->begin+dest_pos,src->begin+src_pos,len); - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out - setERXRDPT(); -} - -void -Enc28J60Network::memblock_mv_cb(uint16_t dest, uint16_t src, uint16_t len) -{ - //as ENC28J60 DMA is unable to copy single bytes: - if (len == 1) - { - writeByte(dest,readByte(src)); - } - else - { - // calculate address of last byte - len += src - 1; - - /* 1. Appropriately program the EDMAST, EDMAND - and EDMADST register pairs. The EDMAST - registers should point to the first byte to copy - from, the EDMAND registers should point to the - last byte to copy and the EDMADST registers - should point to the first byte in the destination - range. The destination range will always be - linear, never wrapping at any values except from - 8191 to 0 (the 8-Kbyte memory boundary). - Extreme care should be taken when - programming the start and end pointers to - prevent a never ending DMA operation which - would overwrite the entire 8-Kbyte buffer. - */ - writeRegPair(EDMASTL, src); - writeRegPair(EDMADSTL, dest); - - if ((src <= RXSTOP_INIT)&& (len > RXSTOP_INIT))len -= (RXSTOP_INIT-RXSTART_INIT); - writeRegPair(EDMANDL, len); - - /* - 2. If an interrupt at the end of the copy process is - desired, set EIE.DMAIE and EIE.INTIE and - clear EIR.DMAIF. - - 3. Verify that ECON1.CSUMEN is clear. */ - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); - - /* 4. Start the DMA copy by setting ECON1.DMAST. */ - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); - - // wait until runnig DMA is completed - while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); - } -} - -void -Enc28J60Network::freePacket() -{ - setERXRDPT(); -} - -uint8_t -Enc28J60Network::readOp(uint8_t op, uint8_t address) -{ - CSACTIVE; - // issue read command - SPDR = op | (address & ADDR_MASK); - waitspi(); - // read data - SPDR = 0x00; - waitspi(); - // do dummy read if needed (for mac and mii, see datasheet page 29) - if(address & 0x80) - { - SPDR = 0x00; - waitspi(); - } - // release CS - CSPASSIVE; - return(SPDR); -} - -void -Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) -{ - CSACTIVE; - // issue write command - SPDR = op | (address & ADDR_MASK); - waitspi(); - // write data - SPDR = data; - waitspi(); - CSPASSIVE; -} - -void -Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) -{ - CSACTIVE; - // issue read command - SPDR = ENC28J60_READ_BUF_MEM; - waitspi(); - while(len) - { - len--; - // read data - SPDR = 0x00; - waitspi(); - *data = SPDR; - data++; - } - //*data='\0'; - CSPASSIVE; -} - -void -Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) -{ - CSACTIVE; - // issue write command - SPDR = ENC28J60_WRITE_BUF_MEM; - waitspi(); - while(len) - { - len--; - // write data - SPDR = *data; - data++; - waitspi(); - } - CSPASSIVE; -} - -void -Enc28J60Network::setBank(uint8_t address) -{ - // set the bank (if needed) - if((address & BANK_MASK) != bank) - { - // set the bank - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); - bank = (address & BANK_MASK); - } -} - -uint8_t -Enc28J60Network::readReg(uint8_t address) -{ - // set the bank - setBank(address); - // do the read - return readOp(ENC28J60_READ_CTRL_REG, address); -} - -void -Enc28J60Network::writeReg(uint8_t address, uint8_t data) -{ - // set the bank - setBank(address); - // do the write - writeOp(ENC28J60_WRITE_CTRL_REG, address, data); -} - -void -Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) -{ - // set the bank - setBank(address); - // do the write - writeOp(ENC28J60_WRITE_CTRL_REG, address, (data&0xFF)); - writeOp(ENC28J60_WRITE_CTRL_REG, address+1, (data) >> 8); -} - -void -Enc28J60Network::phyWrite(uint8_t address, uint16_t data) -{ - // set the PHY register address - writeReg(MIREGADR, address); - // write the PHY data - writeRegPair(MIWRL, data); - // wait until the PHY write completes - while(readReg(MISTAT) & MISTAT_BUSY){ - delayMicroseconds(15); - } -} - -void -Enc28J60Network::clkout(uint8_t clk) -{ - //setup clkout: 2 is 12.5MHz: - writeReg(ECOCON, clk & 0x7); -} - -// read the revision of the chip: -uint8_t -Enc28J60Network::getrev(void) -{ - return(readReg(EREVID)); -} - -uint16_t -Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) -{ - uint16_t t; - len = setReadPtr(handle, pos, len)-1; - CSACTIVE; - // issue read command - SPDR = ENC28J60_READ_BUF_MEM; - waitspi(); - uint16_t i; - for (i = 0; i < len; i+=2) - { - // read data - SPDR = 0x00; - waitspi(); - t = SPDR << 8; - SPDR = 0x00; - waitspi(); - t += SPDR; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - } - if(i == len) { - SPDR = 0x00; - waitspi(); - t = (SPDR << 8) + 0; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - } - CSPASSIVE; - - /* Return sum in host byte order. */ - return sum; -} diff --git a/libraries/UIPEthernet/utility/Enc28J60Network.h b/libraries/UIPEthernet/utility/Enc28J60Network.h deleted file mode 100644 index 96f5600..0000000 --- a/libraries/UIPEthernet/utility/Enc28J60Network.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - Enc28J60Network.h - UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. - - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - inspired by enc28j60.c file from the AVRlib library by Pascal Stang. - For AVRlib See http://www.procyonengineering.com/ - - 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 . - */ - -#ifndef ENC28J60NETWORK_H_ -#define ENC28J60NETWORK_H_ - -#include "mempool.h" - -#define ENC28J60_CONTROL_CS SS -#define SPI_MOSI MOSI -#define SPI_MISO MISO -#define SPI_SCK SCK -#define SPI_SS SS - -#define UIP_RECEIVEBUFFERHANDLE 0xff - -//#define ENC28J60DEBUG - -/* - * Empfangen von ip-header, arp etc... - * wenn tcp/udp -> tcp/udp-callback -> assign new packet to connection - */ - -class Enc28J60Network : public MemoryPool -{ - -private: - uint16_t nextPacketPtr; - uint8_t bank; - - struct memblock receivePkt; - - uint8_t readOp(uint8_t op, uint8_t address); - void writeOp(uint8_t op, uint8_t address, uint8_t data); - uint16_t setReadPtr(memhandle handle, memaddress position, uint16_t len); - void setERXRDPT(); - void readBuffer(uint16_t len, uint8_t* data); - void writeBuffer(uint16_t len, uint8_t* data); - uint8_t readByte(uint16_t addr); - void writeByte(uint16_t addr, uint8_t data); - void setBank(uint8_t address); - uint8_t readReg(uint8_t address); - void writeReg(uint8_t address, uint8_t data); - void writeRegPair(uint8_t address, uint16_t data); - void phyWrite(uint8_t address, uint16_t data); - void clkout(uint8_t clk); - uint8_t getrev(void); - -protected: - void memblock_mv_cb(memaddress dest, memaddress src, memaddress size); - -public: - Enc28J60Network(); - void init(uint8_t* macaddr); - memhandle receivePacket(); - void freePacket(); - memaddress blockSize(memhandle handle); - void sendPacket(memhandle handle); - uint16_t readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); - uint16_t writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); - void copyPacket(memhandle dest, memaddress dest_pos, memhandle src, memaddress src_pos, uint16_t len); - uint16_t chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len); -}; - -#endif /* ENC28J60NETWORK_H_ */ diff --git a/libraries/UIPEthernet/utility/clock-arch.c b/libraries/UIPEthernet/utility/clock-arch.c deleted file mode 100644 index 13b886d..0000000 --- a/libraries/UIPEthernet/utility/clock-arch.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - clock-arch.c - Arduino implementation of uIP clock device. - Copyright (c) 2010 Adam Nielsen - All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "clock-arch.h" - -clock_time_t -clock_time(void) -{ - return (clock_time_t) millis(); -} diff --git a/libraries/UIPEthernet/utility/clock-arch.h b/libraries/UIPEthernet/utility/clock-arch.h deleted file mode 100644 index d7a828b..0000000 --- a/libraries/UIPEthernet/utility/clock-arch.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - clock-arch.h - Arduino implementation of uIP clock device. - Copyright (c) 2010 Adam Nielsen - All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef clock_h_ -#define clock_h_ - -typedef unsigned long clock_time_t; -#define CLOCK_CONF_SECOND 1000 - -#endif diff --git a/libraries/UIPEthernet/utility/enc28j60.h b/libraries/UIPEthernet/utility/enc28j60.h deleted file mode 100644 index 3a45c59..0000000 --- a/libraries/UIPEthernet/utility/enc28j60.h +++ /dev/null @@ -1,257 +0,0 @@ -/***************************************************************************** -* -* Title : Microchip ENC28J60 Ethernet Interface Driver -* Author : Pascal Stang (c)2005 -* Modified by Norbert Truchsess -* Copyright: GPL V2 -* -*This driver provides initialization and transmit/receive -*functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. -*This chip is novel in that it is a full MAC+PHY interface all in a 28-pin -*chip, using an SPI interface to the host processor. -* -* -*****************************************************************************/ - -#ifndef ENC28J60_H -#define ENC28J60_H -#include - -// ENC28J60 Control Registers -// Control register definitions are a combination of address, -// bank number, and Ethernet/MAC/PHY indicator bits. -// - Register address (bits 0-4) -// - Bank number (bits 5-6) -// - MAC/PHY indicator (bit 7) -#define ADDR_MASK 0x1F -#define BANK_MASK 0x60 -#define SPRD_MASK 0x80 -// All-bank registers -#define EIE 0x1B -#define EIR 0x1C -#define ESTAT 0x1D -#define ECON2 0x1E -#define ECON1 0x1F -// Bank 0 registers -#define ERDPTL (0x00|0x00) -#define ERDPTH (0x01|0x00) -#define EWRPTL (0x02|0x00) -#define EWRPTH (0x03|0x00) -#define ETXSTL (0x04|0x00) -#define ETXSTH (0x05|0x00) -#define ETXNDL (0x06|0x00) -#define ETXNDH (0x07|0x00) -#define ERXSTL (0x08|0x00) -#define ERXSTH (0x09|0x00) -#define ERXNDL (0x0A|0x00) -#define ERXNDH (0x0B|0x00) -#define ERXRDPTL (0x0C|0x00) -#define ERXRDPTH (0x0D|0x00) -#define ERXWRPTL (0x0E|0x00) -#define ERXWRPTH (0x0F|0x00) -#define EDMASTL (0x10|0x00) -#define EDMASTH (0x11|0x00) -#define EDMANDL (0x12|0x00) -#define EDMANDH (0x13|0x00) -#define EDMADSTL (0x14|0x00) -#define EDMADSTH (0x15|0x00) -#define EDMACSL (0x16|0x00) -#define EDMACSH (0x17|0x00) -// Bank 1 registers -#define EHT0 (0x00|0x20) -#define EHT1 (0x01|0x20) -#define EHT2 (0x02|0x20) -#define EHT3 (0x03|0x20) -#define EHT4 (0x04|0x20) -#define EHT5 (0x05|0x20) -#define EHT6 (0x06|0x20) -#define EHT7 (0x07|0x20) -#define EPMM0 (0x08|0x20) -#define EPMM1 (0x09|0x20) -#define EPMM2 (0x0A|0x20) -#define EPMM3 (0x0B|0x20) -#define EPMM4 (0x0C|0x20) -#define EPMM5 (0x0D|0x20) -#define EPMM6 (0x0E|0x20) -#define EPMM7 (0x0F|0x20) -#define EPMCSL (0x10|0x20) -#define EPMCSH (0x11|0x20) -#define EPMOL (0x14|0x20) -#define EPMOH (0x15|0x20) -#define EWOLIE (0x16|0x20) -#define EWOLIR (0x17|0x20) -#define ERXFCON (0x18|0x20) -#define EPKTCNT (0x19|0x20) -// Bank 2 registers -#define MACON1 (0x00|0x40|0x80) -#define MACON2 (0x01|0x40|0x80) -#define MACON3 (0x02|0x40|0x80) -#define MACON4 (0x03|0x40|0x80) -#define MABBIPG (0x04|0x40|0x80) -#define MAIPGL (0x06|0x40|0x80) -#define MAIPGH (0x07|0x40|0x80) -#define MACLCON1 (0x08|0x40|0x80) -#define MACLCON2 (0x09|0x40|0x80) -#define MAMXFLL (0x0A|0x40|0x80) -#define MAMXFLH (0x0B|0x40|0x80) -#define MAPHSUP (0x0D|0x40|0x80) -#define MICON (0x11|0x40|0x80) -#define MICMD (0x12|0x40|0x80) -#define MIREGADR (0x14|0x40|0x80) -#define MIWRL (0x16|0x40|0x80) -#define MIWRH (0x17|0x40|0x80) -#define MIRDL (0x18|0x40|0x80) -#define MIRDH (0x19|0x40|0x80) -// Bank 3 registers -#define MAADR1 (0x00|0x60|0x80) -#define MAADR0 (0x01|0x60|0x80) -#define MAADR3 (0x02|0x60|0x80) -#define MAADR2 (0x03|0x60|0x80) -#define MAADR5 (0x04|0x60|0x80) -#define MAADR4 (0x05|0x60|0x80) -#define EBSTSD (0x06|0x60) -#define EBSTCON (0x07|0x60) -#define EBSTCSL (0x08|0x60) -#define EBSTCSH (0x09|0x60) -#define MISTAT (0x0A|0x60|0x80) -#define EREVID (0x12|0x60) -#define ECOCON (0x15|0x60) -#define EFLOCON (0x17|0x60) -#define EPAUSL (0x18|0x60) -#define EPAUSH (0x19|0x60) -// PHY registers -#define PHCON1 0x00 -#define PHSTAT1 0x01 -#define PHHID1 0x02 -#define PHHID2 0x03 -#define PHCON2 0x10 -#define PHSTAT2 0x11 -#define PHIE 0x12 -#define PHIR 0x13 -#define PHLCON 0x14 - -// ENC28J60 ERXFCON Register Bit Definitions -#define ERXFCON_UCEN 0x80 -#define ERXFCON_ANDOR 0x40 -#define ERXFCON_CRCEN 0x20 -#define ERXFCON_PMEN 0x10 -#define ERXFCON_MPEN 0x08 -#define ERXFCON_HTEN 0x04 -#define ERXFCON_MCEN 0x02 -#define ERXFCON_BCEN 0x01 -// ENC28J60 EIE Register Bit Definitions -#define EIE_INTIE 0x80 -#define EIE_PKTIE 0x40 -#define EIE_DMAIE 0x20 -#define EIE_LINKIE 0x10 -#define EIE_TXIE 0x08 -#define EIE_WOLIE 0x04 -#define EIE_TXERIE 0x02 -#define EIE_RXERIE 0x01 -// ENC28J60 EIR Register Bit Definitions -#define EIR_PKTIF 0x40 -#define EIR_DMAIF 0x20 -#define EIR_LINKIF 0x10 -#define EIR_TXIF 0x08 -#define EIR_WOLIF 0x04 -#define EIR_TXERIF 0x02 -#define EIR_RXERIF 0x01 -// ENC28J60 ESTAT Register Bit Definitions -#define ESTAT_INT 0x80 -#define ESTAT_LATECOL 0x10 -#define ESTAT_RXBUSY 0x04 -#define ESTAT_TXABRT 0x02 -#define ESTAT_CLKRDY 0x01 -// ENC28J60 ECON2 Register Bit Definitions -#define ECON2_AUTOINC 0x80 -#define ECON2_PKTDEC 0x40 -#define ECON2_PWRSV 0x20 -#define ECON2_VRPS 0x08 -// ENC28J60 ECON1 Register Bit Definitions -#define ECON1_TXRST 0x80 -#define ECON1_RXRST 0x40 -#define ECON1_DMAST 0x20 -#define ECON1_CSUMEN 0x10 -#define ECON1_TXRTS 0x08 -#define ECON1_RXEN 0x04 -#define ECON1_BSEL1 0x02 -#define ECON1_BSEL0 0x01 -// ENC28J60 MACON1 Register Bit Definitions -#define MACON1_LOOPBK 0x10 -#define MACON1_TXPAUS 0x08 -#define MACON1_RXPAUS 0x04 -#define MACON1_PASSALL 0x02 -#define MACON1_MARXEN 0x01 -// ENC28J60 MACON2 Register Bit Definitions -#define MACON2_MARST 0x80 -#define MACON2_RNDRST 0x40 -#define MACON2_MARXRST 0x08 -#define MACON2_RFUNRST 0x04 -#define MACON2_MATXRST 0x02 -#define MACON2_TFUNRST 0x01 -// ENC28J60 MACON3 Register Bit Definitions -#define MACON3_PADCFG2 0x80 -#define MACON3_PADCFG1 0x40 -#define MACON3_PADCFG0 0x20 -#define MACON3_TXCRCEN 0x10 -#define MACON3_PHDRLEN 0x08 -#define MACON3_HFRMLEN 0x04 -#define MACON3_FRMLNEN 0x02 -#define MACON3_FULDPX 0x01 -// ENC28J60 MICMD Register Bit Definitions -#define MICMD_MIISCAN 0x02 -#define MICMD_MIIRD 0x01 -// ENC28J60 MISTAT Register Bit Definitions -#define MISTAT_NVALID 0x04 -#define MISTAT_SCAN 0x02 -#define MISTAT_BUSY 0x01 -// ENC28J60 PHY PHCON1 Register Bit Definitions -#define PHCON1_PRST 0x8000 -#define PHCON1_PLOOPBK 0x4000 -#define PHCON1_PPWRSV 0x0800 -#define PHCON1_PDPXMD 0x0100 -// ENC28J60 PHY PHSTAT1 Register Bit Definitions -#define PHSTAT1_PFDPX 0x1000 -#define PHSTAT1_PHDPX 0x0800 -#define PHSTAT1_LLSTAT 0x0004 -#define PHSTAT1_JBSTAT 0x0002 -// ENC28J60 PHY PHCON2 Register Bit Definitions -#define PHCON2_FRCLINK 0x4000 -#define PHCON2_TXDIS 0x2000 -#define PHCON2_JABBER 0x0400 -#define PHCON2_HDLDIS 0x0100 - -// ENC28J60 Packet Control Byte Bit Definitions -#define PKTCTRL_PHUGEEN 0x08 -#define PKTCTRL_PPADEN 0x04 -#define PKTCTRL_PCRCEN 0x02 -#define PKTCTRL_POVERRIDE 0x01 - -// SPI operation codes -#define ENC28J60_READ_CTRL_REG 0x00 -#define ENC28J60_READ_BUF_MEM 0x3A -#define ENC28J60_WRITE_CTRL_REG 0x40 -#define ENC28J60_WRITE_BUF_MEM 0x7A -#define ENC28J60_BIT_FIELD_SET 0x80 -#define ENC28J60_BIT_FIELD_CLR 0xA0 -#define ENC28J60_SOFT_RESET 0xFF - - -// The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata -// buffer boundaries applied to internal 8K ram -// the entire available packet buffer space is allocated -// -// start with recbuf at 0/ -#define RXSTART_INIT 0x0 -// receive buffer end. make sure this is an odd value ( See Rev. B1,B4,B5,B7 Silicon Errata 'Memory (Ethernet Buffer)') -#define RXSTOP_INIT (0x1FFF-0x1800) -// start TX buffer RXSTOP_INIT+1 -#define TXSTART_INIT (RXSTOP_INIT+1) -// stp TX buffer at end of mem -#define TXSTOP_INIT 0x1FFF -// -// max frame length which the conroller will accept: -#define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) -//#define MAX_FRAMELEN 600 - -#endif diff --git a/libraries/UIPEthernet/utility/mempool.cpp b/libraries/UIPEthernet/utility/mempool.cpp deleted file mode 100644 index 01f17fb..0000000 --- a/libraries/UIPEthernet/utility/mempool.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - mempool.cpp - sleek implementation of a memory pool - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - 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 . - */ - -#include "mempool.h" -#include - -#define POOLOFFSET 1 - -MemoryPool::MemoryPool(memaddress start, memaddress size) -{ - memset(&blocks[0], 0, sizeof(blocks)); - blocks[POOLSTART].begin = start; - blocks[POOLSTART].size = 0; - blocks[POOLSTART].nextblock = NOBLOCK; - poolsize = size; -} - -memhandle -MemoryPool::allocBlock(memaddress size) -{ - memblock* best = NULL; - memhandle cur = POOLSTART; - memblock* block = &blocks[POOLSTART]; - memaddress bestsize = poolsize + 1; - - do - { - memhandle next = block->nextblock; - memaddress freesize = ( next == NOBLOCK ? blocks[POOLSTART].begin + poolsize : blocks[next].begin) - block->begin - block->size; - if (freesize == size) - { - best = &blocks[cur]; - goto found; - } - if (freesize > size && freesize < bestsize) - { - bestsize = freesize; - best = &blocks[cur]; - } - if (next == NOBLOCK) - { - if (best) - goto found; - else - goto collect; - } - block = &blocks[next]; - cur = next; - } - while (true); - - collect: - { - cur = POOLSTART; - block = &blocks[POOLSTART]; - memhandle next; - while ((next = block->nextblock) != NOBLOCK) - { - memaddress dest = block->begin + block->size; - memblock* nextblock = &blocks[next]; - memaddress* src = &nextblock->begin; - if (dest != *src) - { -#ifdef MEMBLOCK_MV - memblock_mv_cb(dest,*src,nextblock->size); -#endif - *src = dest; - } - block = nextblock; - } - if (blocks[POOLSTART].begin + poolsize - block->begin - block->size >= size) - best = block; - else - goto notfound; - } - - found: - { - block = &blocks[POOLOFFSET]; - for (cur = POOLOFFSET; cur < NUM_MEMBLOCKS + POOLOFFSET; cur++) - { - if (block->size) - { - block++; - continue; - } - memaddress address = best->begin + best->size; -#ifdef MEMBLOCK_ALLOC - MEMBLOCK_ALLOC(address,size); -#endif - block->begin = address; - block->size = size; - block->nextblock = best->nextblock; - best->nextblock = cur; - return cur; - } - } - - notfound: return NOBLOCK; -} - -void -MemoryPool::freeBlock(memhandle handle) -{ - memblock *b = &blocks[POOLSTART]; - - do - { - memhandle next = b->nextblock; - if (next == handle) - { - memblock *f = &blocks[next]; -#ifdef MEMBLOCK_FREE - MEMBLOCK_FREE(f->begin,f->size); -#endif - b->nextblock = f->nextblock; - f->size = 0; - f->nextblock = NOBLOCK; - return; - } - if (next == NOBLOCK) - return; - b = &blocks[next]; - } - while (true); -} - -void -MemoryPool::resizeBlock(memhandle handle, memaddress position) -{ - memblock * block = &blocks[handle]; - block->begin += position; - block->size -= position; -} - -void -MemoryPool::resizeBlock(memhandle handle, memaddress position, memaddress size) -{ - memblock * block = &blocks[handle]; - block->begin += position; - block->size = size; -} - -memaddress -MemoryPool::blockSize(memhandle handle) -{ - return blocks[handle].size; -} diff --git a/libraries/UIPEthernet/utility/mempool.h b/libraries/UIPEthernet/utility/mempool.h deleted file mode 100644 index ea79473..0000000 --- a/libraries/UIPEthernet/utility/mempool.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - mempool.h - sleek implementation of a memory pool - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - 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 . - */ - -#ifndef MEMPOOL_H -#define MEMPOOL_H - -#include - -#define POOLSTART 0 -#define NOBLOCK 0 - -#include "mempool_conf.h" - -//#ifdef MEMBLOCK_MV -//#define memblock_mv_cb(dest,src,size) MEMBLOCK_MV(dest,src,size) -//#endif - -#ifdef MEMBLOCK_ALLOC -#define memblock_alloc_cb(address,size) MEMBLOCK_ALLOC(address,size) -#endif - -#ifdef MEMBLOCK_FREE -#define memblock_free_cb(address,size) MEMBLOCK_FREE(address,size) -#endif - -struct memblock -{ - memaddress begin; - memaddress size; - memhandle nextblock; -}; - -class MemoryPool -{ -#ifdef MEMPOOLTEST_H - friend class MemoryPoolTest; -#endif - -protected: - memaddress poolsize; - struct memblock blocks[NUM_MEMBLOCKS+1]; -#ifdef MEMBLOCK_MV - virtual void memblock_mv_cb(memaddress dest, memaddress src, memaddress size) = 0; -#endif - -public: - MemoryPool(memaddress start, memaddress size); - memhandle - allocBlock(memaddress); - void freeBlock(memhandle); - void resizeBlock(memhandle handle, memaddress position); - void resizeBlock(memhandle handle, memaddress position, memaddress size); - memaddress blockSize(memhandle); -}; -#endif diff --git a/libraries/UIPEthernet/utility/mempool_conf.h b/libraries/UIPEthernet/utility/mempool_conf.h deleted file mode 100644 index bd5a726..0000000 --- a/libraries/UIPEthernet/utility/mempool_conf.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MEMPOOLCONF_H -#define MEMPOOLCONF_H -#include "uipethernet-conf.h" -extern "C" { - #include "uipopt.h" -} -#include - -typedef uint16_t memaddress; -typedef uint8_t memhandle; - -#if UIP_SOCKET_NUMPACKETS and UIP_CONNS -#define NUM_TCP_MEMBLOCKS (UIP_SOCKET_NUMPACKETS*2)*UIP_CONNS -#else -#define NUM_TCP_MEMBLOCKS 0 -#endif - -#if UIP_UDP and UIP_UDP_NUMPACKETS and UIP_UDP_CONNS -#define NUM_UDP_MEMBLOCKS (UIP_UDP_NUMPACKETS+1)*UIP_UDP_CONNS -#else -#define NUM_UDP_MEMBLOCKS 0 -#endif - -#define NUM_MEMBLOCKS (NUM_TCP_MEMBLOCKS+NUM_UDP_MEMBLOCKS) - -#define MEMBLOCK_MV - -#endif diff --git a/libraries/UIPEthernet/utility/uip-conf.h b/libraries/UIPEthernet/utility/uip-conf.h deleted file mode 100644 index 814aeb9..0000000 --- a/libraries/UIPEthernet/utility/uip-conf.h +++ /dev/null @@ -1,185 +0,0 @@ -/** - * UIPEthernet Project-specific configuration options - * Copyright (c) 2013 Norbert Truchsess - * @{ - * - * uIP has a number of configuration options that can be overridden - * for each project. These are kept in a project-specific uip-conf.h - * file and all configuration names have the prefix UIP_CONF. - */ - -/* - * Copyright (c) 2006, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack - * - */ - -#ifndef __UIP_CONF_H__ -#define __UIP_CONF_H__ - -#include -#include "uipethernet-conf.h" - -/** - * 8 bit datatype - * - * This typedef defines the 8-bit type used throughout uIP. - * - * \hideinitializer - */ -typedef uint8_t u8_t; - -/** - * 16 bit datatype - * - * This typedef defines the 16-bit type used throughout uIP. - * - * \hideinitializer - */ -typedef uint16_t u16_t; - -/** - * Statistics datatype - * - * This typedef defines the dataype used for keeping statistics in - * uIP. - * - * \hideinitializer - */ -typedef unsigned short uip_stats_t; - -/** - * Maximum number of TCP connections. - * (see uipethernet-conf.h) - * \hideinitializer - * - * #define UIP_CONF_MAX_CONNECTIONS 4 - */ - -/** - * Maximum number of listening TCP ports. - * - * \hideinitializer - */ -#define UIP_CONF_MAX_LISTENPORTS 4 - -/** - * uIP buffer size. - * - * \hideinitializer - */ -#define UIP_CONF_BUFFER_SIZE 98 -//#define UIP_CONF_BUFFER_SIZE 118 - -/** - * The TCP maximum segment size. - * - * This is should not be to set to more than - * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. - */ - -#define UIP_CONF_TCP_MSS 512 - -/** - * The size of the advertised receiver's window. - * - * Should be set low (i.e., to the size of the uip_buf buffer) is the - * application is slow to process incoming data, or high (32768 bytes) - * if the application processes data quickly. - * - * \hideinitializer - */ -#define UIP_CONF_RECEIVE_WINDOW 512 - -/** - * CPU byte order. - * - * \hideinitializer - */ -#define UIP_CONF_BYTE_ORDER LITTLE_ENDIAN - -/** - * Logging on or off - * - * \hideinitializer - */ -#define UIP_CONF_LOGGING 0 - -/** - * UDP support on or off - * (see uipethernet-conf.h) - * \hideinitializer - * - * #define UIP_CONF_UDP 1 - * - * #define UIP_CONF_UDP_CONNS 4 - */ - -/** - * UDP checksums on or off - * - * \hideinitializer - */ -#define UIP_CONF_UDP_CHECKSUMS 1 - -/** - * UDP Broadcast (receive) on or off - * (see uipethernet-conf.h) - * \hideinitializer - * #define UIP_CONF_BROADCAST 1 - */ - - -/** - * uIP statistics on or off - * - * \hideinitializer - */ -#define UIP_CONF_STATISTICS 0 - -// SLIP -//#define UIP_CONF_LLH_LEN 0 - -typedef void* uip_tcp_appstate_t; - -void uipclient_appcall(void); - -#define UIP_APPCALL uipclient_appcall - -typedef void* uip_udp_appstate_t; - -void uipudp_appcall(void); - -#define UIP_UDP_APPCALL uipudp_appcall - -#define CC_REGISTER_ARG register - -#define UIP_ARCH_CHKSUM 1 - -#endif /* __UIP_CONF_H__ */ diff --git a/libraries/UIPEthernet/utility/uip-neighbor.c b/libraries/UIPEthernet/utility/uip-neighbor.c deleted file mode 100644 index 739c03e..0000000 --- a/libraries/UIPEthernet/utility/uip-neighbor.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2006, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack - * - * $Id: uip-neighbor.c,v 1.2 2006/06/12 08:00:30 adam Exp $ - */ - -/** - * \file - * Database of link-local neighbors, used by IPv6 code and - * to be used by a future ARP code rewrite. - * \author - * Adam Dunkels - */ - -#include "uip-neighbor.h" - -#include - -#define MAX_TIME 128 - -#ifdef UIP_NEIGHBOR_CONF_ENTRIES -#define ENTRIES UIP_NEIGHBOR_CONF_ENTRIES -#else /* UIP_NEIGHBOR_CONF_ENTRIES */ -#define ENTRIES 8 -#endif /* UIP_NEIGHBOR_CONF_ENTRIES */ - -struct neighbor_entry { - uip_ipaddr_t ipaddr; - struct uip_neighbor_addr addr; - u8_t time; -}; -static struct neighbor_entry entries[ENTRIES]; - -/*---------------------------------------------------------------------------*/ -void -uip_neighbor_init(void) -{ - int i; - - for(i = 0; i < ENTRIES; ++i) { - entries[i].time = MAX_TIME; - } -} -/*---------------------------------------------------------------------------*/ -void -uip_neighbor_periodic(void) -{ - int i; - - for(i = 0; i < ENTRIES; ++i) { - if(entries[i].time < MAX_TIME) { - entries[i].time++; - } - } -} -/*---------------------------------------------------------------------------*/ -void -uip_neighbor_add(uip_ipaddr_t ipaddr, struct uip_neighbor_addr *addr) -{ - int i, oldest; - u8_t oldest_time; - - printf("Adding neighbor with link address %02x:%02x:%02x:%02x:%02x:%02x\n", - addr->addr.addr[0], addr->addr.addr[1], addr->addr.addr[2], addr->addr.addr[3], - addr->addr.addr[4], addr->addr.addr[5]); - - /* Find the first unused entry or the oldest used entry. */ - oldest_time = 0; - oldest = 0; - for(i = 0; i < ENTRIES; ++i) { - if(entries[i].time == MAX_TIME) { - oldest = i; - break; - } - if(uip_ipaddr_cmp(entries[i].ipaddr, addr)) { - oldest = i; - break; - } - if(entries[i].time > oldest_time) { - oldest = i; - oldest_time = entries[i].time; - } - } - - /* Use the oldest or first free entry (either pointed to by the - "oldest" variable). */ - entries[oldest].time = 0; - uip_ipaddr_copy(entries[oldest].ipaddr, ipaddr); - memcpy(&entries[oldest].addr, addr, sizeof(struct uip_neighbor_addr)); -} -/*---------------------------------------------------------------------------*/ -static struct neighbor_entry * -find_entry(uip_ipaddr_t ipaddr) -{ - int i; - - for(i = 0; i < ENTRIES; ++i) { - if(uip_ipaddr_cmp(entries[i].ipaddr, ipaddr)) { - return &entries[i]; - } - } - return NULL; -} -/*---------------------------------------------------------------------------*/ -void -uip_neighbor_update(uip_ipaddr_t ipaddr) -{ - struct neighbor_entry *e; - - e = find_entry(ipaddr); - if(e != NULL) { - e->time = 0; - } -} -/*---------------------------------------------------------------------------*/ -struct uip_neighbor_addr * -uip_neighbor_lookup(uip_ipaddr_t ipaddr) -{ - struct neighbor_entry *e; - - e = find_entry(ipaddr); - if(e != NULL) { - /* printf("Lookup neighbor with link address %02x:%02x:%02x:%02x:%02x:%02x\n", - e->addr.addr.addr[0], e->addr.addr.addr[1], e->addr.addr.addr[2], e->addr.addr.addr[3], - e->addr.addr.addr[4], e->addr.addr.addr[5]);*/ - - return &e->addr; - } - return NULL; -} -/*---------------------------------------------------------------------------*/ diff --git a/libraries/UIPEthernet/utility/uip-neighbor.h b/libraries/UIPEthernet/utility/uip-neighbor.h deleted file mode 100644 index d3b351c..0000000 --- a/libraries/UIPEthernet/utility/uip-neighbor.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2006, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack - * - * $Id: uip-neighbor.h,v 1.2 2006/06/12 08:00:30 adam Exp $ - */ - -/** - * \file - * Header file for database of link-local neighbors, used by - * IPv6 code and to be used by future ARP code. - * \author - * Adam Dunkels - */ - -#ifndef __UIP_NEIGHBOR_H__ -#define __UIP_NEIGHBOR_H__ - -#include "uip.h" - -struct uip_neighbor_addr { -#if UIP_NEIGHBOR_CONF_ADDRTYPE - UIP_NEIGHBOR_CONF_ADDRTYPE addr; -#else - struct uip_eth_addr addr; -#endif -}; - -void uip_neighbor_init(void); -void uip_neighbor_add(uip_ipaddr_t ipaddr, struct uip_neighbor_addr *addr); -void uip_neighbor_update(uip_ipaddr_t ipaddr); -struct uip_neighbor_addr *uip_neighbor_lookup(uip_ipaddr_t ipaddr); -void uip_neighbor_periodic(void); - -#endif /* __UIP-NEIGHBOR_H__ */ diff --git a/libraries/UIPEthernet/utility/uip.c b/libraries/UIPEthernet/utility/uip.c deleted file mode 100644 index 84ea8c3..0000000 --- a/libraries/UIPEthernet/utility/uip.c +++ /dev/null @@ -1,1897 +0,0 @@ -#define DEBUG_PRINTF(...) /*printf(__VA_ARGS__)*/ - -/** - * \defgroup uip The uIP TCP/IP stack - * @{ - * - * uIP is an implementation of the TCP/IP protocol stack intended for - * small 8-bit and 16-bit microcontrollers. - * - * uIP provides the necessary protocols for Internet communication, - * with a very small code footprint and RAM requirements - the uIP - * code size is on the order of a few kilobytes and RAM usage is on - * the order of a few hundred bytes. - */ - -/** - * \file - * The uIP TCP/IP stack code. - * \author Adam Dunkels - */ - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip.c,v 1.65 2006/06/11 21:46:39 adam Exp $ - * - */ - -/* - * uIP is a small implementation of the IP, UDP and TCP protocols (as - * well as some basic ICMP stuff). The implementation couples the IP, - * UDP, TCP and the application layers very tightly. To keep the size - * of the compiled code down, this code frequently uses the goto - * statement. While it would be possible to break the uip_process() - * function into many smaller functions, this would increase the code - * size because of the overhead of parameter passing and the fact that - * the optimier would not be as efficient. - * - * The principle is that we have a small buffer, called the uip_buf, - * in which the device driver puts an incoming packet. The TCP/IP - * stack parses the headers in the packet, and calls the - * application. If the remote host has sent data to the application, - * this data is present in the uip_buf and the application read the - * data from there. It is up to the application to put this data into - * a byte stream if needed. The application will not be fed with data - * that is out of sequence. - * - * If the application whishes to send data to the peer, it should put - * its data into the uip_buf. The uip_appdata pointer points to the - * first available byte. The TCP/IP stack will calculate the - * checksums, and fill in the necessary header fields and finally send - * the packet back to the peer. -*/ - -#include "uip.h" -#include "uipopt.h" -#include "uip_arch.h" - -#if UIP_CONF_IPV6 -#include "uip-neighbor.h" -#endif /* UIP_CONF_IPV6 */ - -#include - -/*---------------------------------------------------------------------------*/ -/* Variable definitions. */ - - -/* The IP address of this host. If it is defined to be fixed (by - setting UIP_FIXEDADDR to 1 in uipopt.h), the address is set - here. Otherwise, the address */ -#if UIP_FIXEDADDR > 0 -const uip_ipaddr_t uip_hostaddr = - {HTONS((UIP_IPADDR0 << 8) | UIP_IPADDR1), - HTONS((UIP_IPADDR2 << 8) | UIP_IPADDR3)}; -const uip_ipaddr_t uip_draddr = - {HTONS((UIP_DRIPADDR0 << 8) | UIP_DRIPADDR1), - HTONS((UIP_DRIPADDR2 << 8) | UIP_DRIPADDR3)}; -const uip_ipaddr_t uip_netmask = - {HTONS((UIP_NETMASK0 << 8) | UIP_NETMASK1), - HTONS((UIP_NETMASK2 << 8) | UIP_NETMASK3)}; -#else -uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask; -#endif /* UIP_FIXEDADDR */ - -static const uip_ipaddr_t all_ones_addr = -#if UIP_CONF_IPV6 - {0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff}; -#else /* UIP_CONF_IPV6 */ - {0xffff,0xffff}; -#endif /* UIP_CONF_IPV6 */ -static const uip_ipaddr_t all_zeroes_addr = -#if UIP_CONF_IPV6 - {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; -#else /* UIP_CONF_IPV6 */ - {0x0000,0x0000}; -#endif /* UIP_CONF_IPV6 */ - - -#if UIP_FIXEDETHADDR -const struct uip_eth_addr uip_ethaddr = {{UIP_ETHADDR0, - UIP_ETHADDR1, - UIP_ETHADDR2, - UIP_ETHADDR3, - UIP_ETHADDR4, - UIP_ETHADDR5}}; -#else -struct uip_eth_addr uip_ethaddr = {{0,0,0,0,0,0}}; -#endif - -#ifndef UIP_CONF_EXTERNAL_BUFFER -u8_t uip_buf[UIP_BUFSIZE + 2]; /* The packet buffer that contains - incoming packets. */ -#endif /* UIP_CONF_EXTERNAL_BUFFER */ - -void *uip_appdata; /* The uip_appdata pointer points to - application data. */ -void *uip_sappdata; /* The uip_appdata pointer points to - the application data which is to - be sent. */ -#if UIP_URGDATA > 0 -void *uip_urgdata; /* The uip_urgdata pointer points to - urgent data (out-of-band data), if - present. */ -u16_t uip_urglen, uip_surglen; -#endif /* UIP_URGDATA > 0 */ - -u16_t uip_len, uip_slen; - /* The uip_len is either 8 or 16 bits, - depending on the maximum packet - size. */ - -u8_t uip_flags; /* The uip_flags variable is used for - communication between the TCP/IP stack - and the application program. */ -struct uip_conn *uip_conn; /* uip_conn always points to the current - connection. */ - -struct uip_conn uip_conns[UIP_CONNS]; - /* The uip_conns array holds all TCP - connections. */ -u16_t uip_listenports[UIP_LISTENPORTS]; - /* The uip_listenports list all currently - listning ports. */ -#if UIP_UDP -struct uip_udp_conn *uip_udp_conn; -struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; -#endif /* UIP_UDP */ - -static u16_t ipid; /* Ths ipid variable is an increasing - number that is used for the IP ID - field. */ - -void uip_setipid(u16_t id) { ipid = id; } - -static u8_t iss[4]; /* The iss variable is used for the TCP - initial sequence number. */ - -#if UIP_ACTIVE_OPEN -static u16_t lastport; /* Keeps track of the last port used for - a new connection. */ -#endif /* UIP_ACTIVE_OPEN */ - -/* Temporary variables. */ -u8_t uip_acc32[4]; -static u8_t c, opt; -static u16_t tmp16; - -/* Structures and definitions. */ -#define TCP_FIN 0x01 -#define TCP_SYN 0x02 -#define TCP_RST 0x04 -#define TCP_PSH 0x08 -#define TCP_ACK 0x10 -#define TCP_URG 0x20 -#define TCP_CTL 0x3f - -#define TCP_OPT_END 0 /* End of TCP options list */ -#define TCP_OPT_NOOP 1 /* "No-operation" TCP option */ -#define TCP_OPT_MSS 2 /* Maximum segment size TCP option */ - -#define TCP_OPT_MSS_LEN 4 /* Length of TCP MSS option. */ - -#define ICMP_ECHO_REPLY 0 -#define ICMP_ECHO 8 - -#define ICMP6_ECHO_REPLY 129 -#define ICMP6_ECHO 128 -#define ICMP6_NEIGHBOR_SOLICITATION 135 -#define ICMP6_NEIGHBOR_ADVERTISEMENT 136 - -#define ICMP6_FLAG_S (1 << 6) - -#define ICMP6_OPTION_SOURCE_LINK_ADDRESS 1 -#define ICMP6_OPTION_TARGET_LINK_ADDRESS 2 - - -/* Macros. */ -#define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define FBUF ((struct uip_tcpip_hdr *)&uip_reassbuf[0]) -#define ICMPBUF ((struct uip_icmpip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UDPBUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]) - - -#if UIP_STATISTICS == 1 -struct uip_stats uip_stat; -#define UIP_STAT(s) s -#else -#define UIP_STAT(s) -#endif /* UIP_STATISTICS == 1 */ - -#if UIP_LOGGING == 1 -#include -void uip_log(char *msg); -#define UIP_LOG(m) uip_log(m) -#else -#define UIP_LOG(m) -#endif /* UIP_LOGGING == 1 */ - -#if ! UIP_ARCH_ADD32 -void -uip_add32(u8_t *op32, u16_t op16) -{ - uip_acc32[3] = op32[3] + (op16 & 0xff); - uip_acc32[2] = op32[2] + (op16 >> 8); - uip_acc32[1] = op32[1]; - uip_acc32[0] = op32[0]; - - if(uip_acc32[2] < (op16 >> 8)) { - ++uip_acc32[1]; - if(uip_acc32[1] == 0) { - ++uip_acc32[0]; - } - } - - - if(uip_acc32[3] < (op16 & 0xff)) { - ++uip_acc32[2]; - if(uip_acc32[2] == 0) { - ++uip_acc32[1]; - if(uip_acc32[1] == 0) { - ++uip_acc32[0]; - } - } - } -} - -#endif /* UIP_ARCH_ADD32 */ - -#if ! UIP_ARCH_CHKSUM -/*---------------------------------------------------------------------------*/ -static u16_t -chksum(u16_t sum, const u8_t *data, u16_t len) -{ - u16_t t; - const u8_t *dataptr; - const u8_t *last_byte; - - dataptr = data; - last_byte = data + len - 1; - - while(dataptr < last_byte) { /* At least two more bytes */ - t = (dataptr[0] << 8) + dataptr[1]; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - dataptr += 2; - } - - if(dataptr == last_byte) { - t = (dataptr[0] << 8) + 0; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - } - - /* Return sum in host byte order. */ - return sum; -} -/*---------------------------------------------------------------------------*/ -u16_t -uip_chksum(u16_t *data, u16_t len) -{ - return htons(chksum(0, (u8_t *)data, len)); -} -/*---------------------------------------------------------------------------*/ -#ifndef UIP_ARCH_IPCHKSUM -u16_t -uip_ipchksum(void) -{ - u16_t sum; - - sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); - DEBUG_PRINTF("uip_ipchksum: sum 0x%04x\n", sum); - return (sum == 0) ? 0xffff : htons(sum); -} -#endif -/*---------------------------------------------------------------------------*/ -static u16_t -upper_layer_chksum(u8_t proto) -{ - u16_t upper_layer_len; - u16_t sum; - -#if UIP_CONF_IPV6 - upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]); -#else /* UIP_CONF_IPV6 */ - upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN; -#endif /* UIP_CONF_IPV6 */ - - /* First sum pseudoheader. */ - - /* IP protocol and length fields. This addition cannot carry. */ - sum = upper_layer_len + proto; - /* Sum IP source and destination addresses. */ - sum = chksum(sum, (u8_t *)&BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t)); - - /* Sum TCP header and data. */ - sum = chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], - upper_layer_len); - - return (sum == 0) ? 0xffff : htons(sum); -} -/*---------------------------------------------------------------------------*/ -#if UIP_CONF_IPV6 -u16_t -uip_icmp6chksum(void) -{ - return upper_layer_chksum(UIP_PROTO_ICMP6); - -} -#endif /* UIP_CONF_IPV6 */ -/*---------------------------------------------------------------------------*/ -u16_t -uip_tcpchksum(void) -{ - return upper_layer_chksum(UIP_PROTO_TCP); -} -/*---------------------------------------------------------------------------*/ -#if UIP_UDP_CHECKSUMS -u16_t -uip_udpchksum(void) -{ - return upper_layer_chksum(UIP_PROTO_UDP); -} -#endif /* UIP_UDP_CHECKSUMS */ -#endif /* UIP_ARCH_CHKSUM */ -/*---------------------------------------------------------------------------*/ -void -uip_init(void) -{ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - uip_listenports[c] = 0; - } - for(c = 0; c < UIP_CONNS; ++c) { - uip_conns[c].tcpstateflags = UIP_CLOSED; - } -#if UIP_ACTIVE_OPEN - lastport = 1024; -#endif /* UIP_ACTIVE_OPEN */ - -#if UIP_UDP - for(c = 0; c < UIP_UDP_CONNS; ++c) { - uip_udp_conns[c].lport = 0; - } -#endif /* UIP_UDP */ - - - /* IPv4 initialization. */ -#if UIP_FIXEDADDR == 0 - /* uip_hostaddr[0] = uip_hostaddr[1] = 0;*/ -#endif /* UIP_FIXEDADDR */ - -} -/*---------------------------------------------------------------------------*/ -#if UIP_ACTIVE_OPEN -struct uip_conn * -uip_connect(uip_ipaddr_t *ripaddr, u16_t rport) -{ - register struct uip_conn *conn, *cconn; - - /* Find an unused local port. */ - again: - ++lastport; - - if(lastport >= 32000) { - lastport = 4096; - } - - /* Check if this port is already in use, and if so try to find - another one. */ - for(c = 0; c < UIP_CONNS; ++c) { - conn = &uip_conns[c]; - if(conn->tcpstateflags != UIP_CLOSED && - conn->lport == htons(lastport)) { - goto again; - } - } - - conn = 0; - for(c = 0; c < UIP_CONNS; ++c) { - cconn = &uip_conns[c]; - if(cconn->tcpstateflags == UIP_CLOSED) { - conn = cconn; - break; - } - if(cconn->tcpstateflags == UIP_TIME_WAIT) { - if(conn == 0 || - cconn->timer > conn->timer) { - conn = cconn; - } - } - } - - if(conn == 0) { - return 0; - } - - conn->tcpstateflags = UIP_SYN_SENT; - - conn->snd_nxt[0] = iss[0]; - conn->snd_nxt[1] = iss[1]; - conn->snd_nxt[2] = iss[2]; - conn->snd_nxt[3] = iss[3]; - - conn->initialmss = conn->mss = UIP_TCP_MSS; - - conn->len = 1; /* TCP length of the SYN is one. */ - conn->nrtx = 0; - conn->timer = 1; /* Send the SYN next time around. */ - conn->rto = UIP_RTO; - conn->sa = 0; - conn->sv = 16; /* Initial value of the RTT variance. */ - conn->lport = htons(lastport); - conn->rport = rport; - uip_ipaddr_copy(&conn->ripaddr, ripaddr); - - return conn; -} -#endif /* UIP_ACTIVE_OPEN */ -/*---------------------------------------------------------------------------*/ -#if UIP_UDP -struct uip_udp_conn * -uip_udp_new(uip_ipaddr_t *ripaddr, u16_t rport) -{ - register struct uip_udp_conn *conn; - - /* Find an unused local port. */ - again: - ++lastport; - - if(lastport >= 32000) { - lastport = 4096; - } - - for(c = 0; c < UIP_UDP_CONNS; ++c) { - if(uip_udp_conns[c].lport == htons(lastport)) { - goto again; - } - } - - - conn = 0; - for(c = 0; c < UIP_UDP_CONNS; ++c) { - if(uip_udp_conns[c].lport == 0) { - conn = &uip_udp_conns[c]; - break; - } - } - - if(conn == 0) { - return 0; - } - - conn->lport = HTONS(lastport); - conn->rport = rport; - if(ripaddr == NULL) { - memset(conn->ripaddr, 0, sizeof(uip_ipaddr_t)); - } else { - uip_ipaddr_copy(&conn->ripaddr, ripaddr); - } - conn->ttl = UIP_TTL; - - return conn; -} -#endif /* UIP_UDP */ -/*---------------------------------------------------------------------------*/ -void -uip_unlisten(u16_t port) -{ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - if(uip_listenports[c] == port) { - uip_listenports[c] = 0; - return; - } - } -} -/*---------------------------------------------------------------------------*/ -void -uip_listen(u16_t port) -{ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - if(uip_listenports[c] == 0) { - uip_listenports[c] = port; - return; - } - } -} -/*---------------------------------------------------------------------------*/ -/* XXX: IP fragment reassembly: not well-tested. */ - -#if UIP_REASSEMBLY && !UIP_CONF_IPV6 -#define UIP_REASS_BUFSIZE (UIP_BUFSIZE - UIP_LLH_LEN) -static u8_t uip_reassbuf[UIP_REASS_BUFSIZE]; -static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)]; -static const u8_t bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f, - 0x0f, 0x07, 0x03, 0x01}; -static u16_t uip_reasslen; -static u8_t uip_reassflags; -#define UIP_REASS_FLAG_LASTFRAG 0x01 -static u8_t uip_reasstmr; - -#define IP_MF 0x20 - -static u8_t -uip_reass(void) -{ - u16_t offset, len; - u16_t i; - - /* If ip_reasstmr is zero, no packet is present in the buffer, so we - write the IP header of the fragment into the reassembly - buffer. The timer is updated with the maximum age. */ - if(uip_reasstmr == 0) { - memcpy(uip_reassbuf, &BUF->vhl, UIP_IPH_LEN); - uip_reasstmr = UIP_REASS_MAXAGE; - uip_reassflags = 0; - /* Clear the bitmap. */ - memset(uip_reassbitmap, 0, sizeof(uip_reassbitmap)); - } - - /* Check if the incoming fragment matches the one currently present - in the reasembly buffer. If so, we proceed with copying the - fragment into the buffer. */ - if(BUF->srcipaddr[0] == FBUF->srcipaddr[0] && - BUF->srcipaddr[1] == FBUF->srcipaddr[1] && - BUF->destipaddr[0] == FBUF->destipaddr[0] && - BUF->destipaddr[1] == FBUF->destipaddr[1] && - BUF->ipid[0] == FBUF->ipid[0] && - BUF->ipid[1] == FBUF->ipid[1]) { - - len = (BUF->len[0] << 8) + BUF->len[1] - (BUF->vhl & 0x0f) * 4; - offset = (((BUF->ipoffset[0] & 0x3f) << 8) + BUF->ipoffset[1]) * 8; - - /* If the offset or the offset + fragment length overflows the - reassembly buffer, we discard the entire packet. */ - if(offset > UIP_REASS_BUFSIZE || - offset + len > UIP_REASS_BUFSIZE) { - uip_reasstmr = 0; - goto nullreturn; - } - - /* Copy the fragment into the reassembly buffer, at the right - offset. */ - memcpy(&uip_reassbuf[UIP_IPH_LEN + offset], - (char *)BUF + (int)((BUF->vhl & 0x0f) * 4), - len); - - /* Update the bitmap. */ - if(offset / (8 * 8) == (offset + len) / (8 * 8)) { - /* If the two endpoints are in the same byte, we only update - that byte. */ - - uip_reassbitmap[offset / (8 * 8)] |= - bitmap_bits[(offset / 8 ) & 7] & - ~bitmap_bits[((offset + len) / 8 ) & 7]; - } else { - /* If the two endpoints are in different bytes, we update the - bytes in the endpoints and fill the stuff inbetween with - 0xff. */ - uip_reassbitmap[offset / (8 * 8)] |= - bitmap_bits[(offset / 8 ) & 7]; - for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) { - uip_reassbitmap[i] = 0xff; - } - uip_reassbitmap[(offset + len) / (8 * 8)] |= - ~bitmap_bits[((offset + len) / 8 ) & 7]; - } - - /* If this fragment has the More Fragments flag set to zero, we - know that this is the last fragment, so we can calculate the - size of the entire packet. We also set the - IP_REASS_FLAG_LASTFRAG flag to indicate that we have received - the final fragment. */ - - if((BUF->ipoffset[0] & IP_MF) == 0) { - uip_reassflags |= UIP_REASS_FLAG_LASTFRAG; - uip_reasslen = offset + len; - } - - /* Finally, we check if we have a full packet in the buffer. We do - this by checking if we have the last fragment and if all bits - in the bitmap are set. */ - if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) { - /* Check all bytes up to and including all but the last byte in - the bitmap. */ - for(i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) { - if(uip_reassbitmap[i] != 0xff) { - goto nullreturn; - } - } - /* Check the last byte in the bitmap. It should contain just the - right amount of bits. */ - if(uip_reassbitmap[uip_reasslen / (8 * 8)] != - (u8_t)~bitmap_bits[uip_reasslen / 8 & 7]) { - goto nullreturn; - } - - /* If we have come this far, we have a full packet in the - buffer, so we allocate a pbuf and copy the packet into it. We - also reset the timer. */ - uip_reasstmr = 0; - memcpy(BUF, FBUF, uip_reasslen); - - /* Pretend to be a "normal" (i.e., not fragmented) IP packet - from now on. */ - BUF->ipoffset[0] = BUF->ipoffset[1] = 0; - BUF->len[0] = uip_reasslen >> 8; - BUF->len[1] = uip_reasslen & 0xff; - BUF->ipchksum = 0; - BUF->ipchksum = ~(uip_ipchksum()); - - return uip_reasslen; - } - } - - nullreturn: - return 0; -} -#endif /* UIP_REASSEMBLY */ -/*---------------------------------------------------------------------------*/ -static void -uip_add_rcv_nxt(u16_t n) -{ - uip_add32(uip_conn->rcv_nxt, n); - uip_conn->rcv_nxt[0] = uip_acc32[0]; - uip_conn->rcv_nxt[1] = uip_acc32[1]; - uip_conn->rcv_nxt[2] = uip_acc32[2]; - uip_conn->rcv_nxt[3] = uip_acc32[3]; -} -/*---------------------------------------------------------------------------*/ -void -uip_process(u8_t flag) -{ - register struct uip_conn *uip_connr = uip_conn; - -#if UIP_UDP - if(flag == UIP_UDP_SEND_CONN) { - goto udp_send; - } -#endif /* UIP_UDP */ - - uip_sappdata = uip_appdata = &uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN]; - - /* Check if we were invoked because of a poll request for a - particular connection. */ - if(flag == UIP_POLL_REQUEST) { - if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED && - !uip_outstanding(uip_connr)) { - uip_flags = UIP_POLL; - UIP_APPCALL(); - goto appsend; - } - goto drop; - - /* Check if we were invoked because of the perodic timer fireing. */ - } else if(flag == UIP_TIMER) { -#if UIP_REASSEMBLY - if(uip_reasstmr != 0) { - --uip_reasstmr; - } -#endif /* UIP_REASSEMBLY */ - /* Increase the initial sequence number. */ - if(++iss[3] == 0) { - if(++iss[2] == 0) { - if(++iss[1] == 0) { - ++iss[0]; - } - } - } - - /* Reset the length variables. */ - uip_len = 0; - uip_slen = 0; - - /* Check if the connection is in a state in which we simply wait - for the connection to time out. If so, we increase the - connection's timer and remove the connection if it times - out. */ - if(uip_connr->tcpstateflags == UIP_TIME_WAIT || - uip_connr->tcpstateflags == UIP_FIN_WAIT_2) { - ++(uip_connr->timer); - if(uip_connr->timer == UIP_TIME_WAIT_TIMEOUT) { - uip_connr->tcpstateflags = UIP_CLOSED; - } - } else if(uip_connr->tcpstateflags != UIP_CLOSED) { - /* If the connection has outstanding data, we increase the - connection's timer and see if it has reached the RTO value - in which case we retransmit. */ - if(uip_outstanding(uip_connr)) { - if(uip_connr->timer-- == 0) { - if(uip_connr->nrtx == UIP_MAXRTX || - ((uip_connr->tcpstateflags == UIP_SYN_SENT || - uip_connr->tcpstateflags == UIP_SYN_RCVD) && - uip_connr->nrtx == UIP_MAXSYNRTX)) { - uip_connr->tcpstateflags = UIP_CLOSED; - - /* We call UIP_APPCALL() with uip_flags set to - UIP_TIMEDOUT to inform the application that the - connection has timed out. */ - uip_flags = UIP_TIMEDOUT; - UIP_APPCALL(); - - /* We also send a reset packet to the remote host. */ - BUF->flags = TCP_RST | TCP_ACK; - goto tcp_send_nodata; - } - - /* Exponential backoff. */ - uip_connr->timer = UIP_RTO << (uip_connr->nrtx > 4? - 4: - uip_connr->nrtx); - ++(uip_connr->nrtx); - - /* Ok, so we need to retransmit. We do this differently - depending on which state we are in. In ESTABLISHED, we - call upon the application so that it may prepare the - data for the retransmit. In SYN_RCVD, we resend the - SYNACK that we sent earlier and in LAST_ACK we have to - retransmit our FINACK. */ - UIP_STAT(++uip_stat.tcp.rexmit); - switch(uip_connr->tcpstateflags & UIP_TS_MASK) { - case UIP_SYN_RCVD: - /* In the SYN_RCVD state, we should retransmit our - SYNACK. */ - goto tcp_send_synack; - -#if UIP_ACTIVE_OPEN - case UIP_SYN_SENT: - /* In the SYN_SENT state, we retransmit out SYN. */ - BUF->flags = 0; - goto tcp_send_syn; -#endif /* UIP_ACTIVE_OPEN */ - - case UIP_ESTABLISHED: - /* In the ESTABLISHED state, we call upon the application - to do the actual retransmit after which we jump into - the code for sending out the packet (the apprexmit - label). */ - uip_flags = UIP_REXMIT; - UIP_APPCALL(); - goto apprexmit; - - case UIP_FIN_WAIT_1: - case UIP_CLOSING: - case UIP_LAST_ACK: - /* In all these states we should retransmit a FINACK. */ - goto tcp_send_finack; - - } - } - } else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) { - /* If there was no need for a retransmission, we poll the - application for new data. */ - uip_flags = UIP_POLL; - UIP_APPCALL(); - goto appsend; - } - } - goto drop; - } -#if UIP_UDP - if(flag == UIP_UDP_TIMER) { - if(uip_udp_conn->lport != 0) { - uip_conn = NULL; - uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; - uip_len = uip_slen = 0; - uip_flags = UIP_POLL; - UIP_UDP_APPCALL(); - goto udp_send; - } else { - goto drop; - } - } -#endif - - /* This is where the input processing starts. */ - UIP_STAT(++uip_stat.ip.recv); - - /* Start of IP input header processing code. */ - -#if UIP_CONF_IPV6 - /* Check validity of the IP header. */ - if((BUF->vtc & 0xf0) != 0x60) { /* IP version and header length. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.vhlerr); - UIP_LOG("ipv6: invalid version."); - goto drop; - } -#else /* UIP_CONF_IPV6 */ - /* Check validity of the IP header. */ - if(BUF->vhl != 0x45) { /* IP version and header length. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.vhlerr); - UIP_LOG("ip: invalid version or header length."); - goto drop; - } -#endif /* UIP_CONF_IPV6 */ - - /* Check the size of the packet. If the size reported to us in - uip_len is smaller the size reported in the IP header, we assume - that the packet has been corrupted in transit. If the size of - uip_len is larger than the size reported in the IP packet header, - the packet has been padded and we set uip_len to the correct - value.. */ - - if((BUF->len[0] << 8) + BUF->len[1] <= uip_len) { - uip_len = (BUF->len[0] << 8) + BUF->len[1]; -#if UIP_CONF_IPV6 - uip_len += 40; /* The length reported in the IPv6 header is the - length of the payload that follows the - header. However, uIP uses the uip_len variable - for holding the size of the entire packet, - including the IP header. For IPv4 this is not a - problem as the length field in the IPv4 header - contains the length of the entire packet. But - for IPv6 we need to add the size of the IPv6 - header (40 bytes). */ -#endif /* UIP_CONF_IPV6 */ - } else { - UIP_LOG("ip: packet shorter than reported in IP header."); - goto drop; - } - -#if !UIP_CONF_IPV6 - /* Check the fragment flag. */ - if((BUF->ipoffset[0] & 0x3f) != 0 || - BUF->ipoffset[1] != 0) { -#if UIP_REASSEMBLY - uip_len = uip_reass(); - if(uip_len == 0) { - goto drop; - } -#else /* UIP_REASSEMBLY */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.fragerr); - UIP_LOG("ip: fragment dropped."); - goto drop; -#endif /* UIP_REASSEMBLY */ - } -#endif /* UIP_CONF_IPV6 */ - - if(uip_ipaddr_cmp(uip_hostaddr, all_zeroes_addr)) { - /* If we are configured to use ping IP address configuration and - hasn't been assigned an IP address yet, we accept all ICMP - packets. */ -#if UIP_PINGADDRCONF && !UIP_CONF_IPV6 - if(BUF->proto == UIP_PROTO_ICMP) { - UIP_LOG("ip: possible ping config packet received."); - goto icmp_input; - } else { - UIP_LOG("ip: packet dropped since no address assigned."); - goto drop; - } -#endif /* UIP_PINGADDRCONF */ - - } else { - /* If IP broadcast support is configured, we check for a broadcast - UDP packet, which may be destined to us. */ -#if UIP_BROADCAST - DEBUG_PRINTF("UDP IP checksum 0x%04x\n", uip_ipchksum()); - if(BUF->proto == UIP_PROTO_UDP && - uip_ipaddr_cmp(BUF->destipaddr, all_ones_addr) - /*&& - uip_ipchksum() == 0xffff*/) { - goto udp_input; - } -#endif /* UIP_BROADCAST */ - - /* Check if the packet is destined for our IP address. */ -#if !UIP_CONF_IPV6 - if(!uip_ipaddr_cmp(BUF->destipaddr, uip_hostaddr)) { - UIP_STAT(++uip_stat.ip.drop); - goto drop; - } -#else /* UIP_CONF_IPV6 */ - /* For IPv6, packet reception is a little trickier as we need to - make sure that we listen to certain multicast addresses (all - hosts multicast address, and the solicited-node multicast - address) as well. However, we will cheat here and accept all - multicast packets that are sent to the ff02::/16 addresses. */ - if(!uip_ipaddr_cmp(BUF->destipaddr, uip_hostaddr) && - BUF->destipaddr[0] != HTONS(0xff02)) { - UIP_STAT(++uip_stat.ip.drop); - goto drop; - } -#endif /* UIP_CONF_IPV6 */ - } - -#if !UIP_CONF_IPV6 - if(uip_ipchksum() != 0xffff) { /* Compute and check the IP header - checksum. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.chkerr); - UIP_LOG("ip: bad checksum."); - goto drop; - } -#endif /* UIP_CONF_IPV6 */ - - if(BUF->proto == UIP_PROTO_TCP) { /* Check for TCP packet. If so, - proceed with TCP input - processing. */ - goto tcp_input; - } - -#if UIP_UDP - if(BUF->proto == UIP_PROTO_UDP) { - goto udp_input; - } -#endif /* UIP_UDP */ - -#if !UIP_CONF_IPV6 - /* ICMPv4 processing code follows. */ - if(BUF->proto != UIP_PROTO_ICMP) { /* We only allow ICMP packets from - here. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.protoerr); - UIP_LOG("ip: neither tcp nor icmp."); - goto drop; - } - -#if UIP_PINGADDRCONF - icmp_input: -#endif /* UIP_PINGADDRCONF */ - UIP_STAT(++uip_stat.icmp.recv); - - /* ICMP echo (i.e., ping) processing. This is simple, we only change - the ICMP type from ECHO to ECHO_REPLY and adjust the ICMP - checksum before we return the packet. */ - if(ICMPBUF->type != ICMP_ECHO) { - UIP_STAT(++uip_stat.icmp.drop); - UIP_STAT(++uip_stat.icmp.typeerr); - UIP_LOG("icmp: not icmp echo."); - goto drop; - } - - /* If we are configured to use ping IP address assignment, we use - the destination IP address of this ping packet and assign it to - ourself. */ -#if UIP_PINGADDRCONF - if((uip_hostaddr[0] | uip_hostaddr[1]) == 0) { - uip_hostaddr[0] = BUF->destipaddr[0]; - uip_hostaddr[1] = BUF->destipaddr[1]; - } -#endif /* UIP_PINGADDRCONF */ - - ICMPBUF->type = ICMP_ECHO_REPLY; - - if(ICMPBUF->icmpchksum >= HTONS(0xffff - (ICMP_ECHO << 8))) { - ICMPBUF->icmpchksum += HTONS(ICMP_ECHO << 8) + 1; - } else { - ICMPBUF->icmpchksum += HTONS(ICMP_ECHO << 8); - } - - /* Swap IP addresses. */ - uip_ipaddr_copy(BUF->destipaddr, BUF->srcipaddr); - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - - UIP_STAT(++uip_stat.icmp.sent); - goto send; - - /* End of IPv4 input header processing code. */ -#else /* !UIP_CONF_IPV6 */ - - /* This is IPv6 ICMPv6 processing code. */ - DEBUG_PRINTF("icmp6_input: length %d\n", uip_len); - - if(BUF->proto != UIP_PROTO_ICMP6) { /* We only allow ICMPv6 packets from - here. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.protoerr); - UIP_LOG("ip: neither tcp nor icmp6."); - goto drop; - } - - UIP_STAT(++uip_stat.icmp.recv); - - /* If we get a neighbor solicitation for our address we should send - a neighbor advertisement message back. */ - if(ICMPBUF->type == ICMP6_NEIGHBOR_SOLICITATION) { - if(uip_ipaddr_cmp(ICMPBUF->icmp6data, uip_hostaddr)) { - - if(ICMPBUF->options[0] == ICMP6_OPTION_SOURCE_LINK_ADDRESS) { - /* Save the sender's address in our neighbor list. */ - uip_neighbor_add(ICMPBUF->srcipaddr, &(ICMPBUF->options[2])); - } - - /* We should now send a neighbor advertisement back to where the - neighbor solicication came from. */ - ICMPBUF->type = ICMP6_NEIGHBOR_ADVERTISEMENT; - ICMPBUF->flags = ICMP6_FLAG_S; /* Solicited flag. */ - - ICMPBUF->reserved1 = ICMPBUF->reserved2 = ICMPBUF->reserved3 = 0; - - uip_ipaddr_copy(ICMPBUF->destipaddr, ICMPBUF->srcipaddr); - uip_ipaddr_copy(ICMPBUF->srcipaddr, uip_hostaddr); - ICMPBUF->options[0] = ICMP6_OPTION_TARGET_LINK_ADDRESS; - ICMPBUF->options[1] = 1; /* Options length, 1 = 8 bytes. */ - memcpy(&(ICMPBUF->options[2]), &uip_ethaddr, sizeof(uip_ethaddr)); - ICMPBUF->icmpchksum = 0; - ICMPBUF->icmpchksum = ~uip_icmp6chksum(); - goto send; - - } - goto drop; - } else if(ICMPBUF->type == ICMP6_ECHO) { - /* ICMP echo (i.e., ping) processing. This is simple, we only - change the ICMP type from ECHO to ECHO_REPLY and update the - ICMP checksum before we return the packet. */ - - ICMPBUF->type = ICMP6_ECHO_REPLY; - - uip_ipaddr_copy(BUF->destipaddr, BUF->srcipaddr); - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - ICMPBUF->icmpchksum = 0; - ICMPBUF->icmpchksum = ~uip_icmp6chksum(); - - UIP_STAT(++uip_stat.icmp.sent); - goto send; - } else { - DEBUG_PRINTF("Unknown icmp6 message type %d\n", ICMPBUF->type); - UIP_STAT(++uip_stat.icmp.drop); - UIP_STAT(++uip_stat.icmp.typeerr); - UIP_LOG("icmp: unknown ICMP message."); - goto drop; - } - - /* End of IPv6 ICMP processing. */ - -#endif /* !UIP_CONF_IPV6 */ - -#if UIP_UDP - /* UDP input processing. */ - udp_input: - /* UDP processing is really just a hack. We don't do anything to the - UDP/IP headers, but let the UDP application do all the hard - work. If the application sets uip_slen, it has a packet to - send. */ -#if UIP_UDP_CHECKSUMS - uip_len = uip_len - UIP_IPUDPH_LEN; - uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; - if(UDPBUF->udpchksum != 0 && uip_udpchksum() != 0xffff) { - UIP_STAT(++uip_stat.udp.drop); - UIP_STAT(++uip_stat.udp.chkerr); - UIP_LOG("udp: bad checksum."); - goto drop; - } -#else /* UIP_UDP_CHECKSUMS */ - uip_len = uip_len - UIP_IPUDPH_LEN; -#endif /* UIP_UDP_CHECKSUMS */ - - /* Demultiplex this UDP packet between the UDP "connections". */ - for(uip_udp_conn = &uip_udp_conns[0]; - uip_udp_conn < &uip_udp_conns[UIP_UDP_CONNS]; - ++uip_udp_conn) { - /* If the local UDP port is non-zero, the connection is considered - to be used. If so, the local port number is checked against the - destination port number in the received packet. If the two port - numbers match, the remote port number is checked if the - connection is bound to a remote port. Finally, if the - connection is bound to a remote IP address, the source IP - address of the packet is checked. */ - if(uip_udp_conn->lport != 0 && - UDPBUF->destport == uip_udp_conn->lport && - (uip_udp_conn->rport == 0 || - UDPBUF->srcport == uip_udp_conn->rport) && - (uip_ipaddr_cmp(uip_udp_conn->ripaddr, all_zeroes_addr) || - uip_ipaddr_cmp(uip_udp_conn->ripaddr, all_ones_addr) || - uip_ipaddr_cmp(BUF->srcipaddr, uip_udp_conn->ripaddr))) { - goto udp_found; - } - } - UIP_LOG("udp: no matching connection found"); - goto drop; - - udp_found: - uip_conn = NULL; - uip_flags = UIP_NEWDATA; - uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; - uip_slen = 0; - UIP_UDP_APPCALL(); - udp_send: - if(uip_slen == 0) { - goto drop; - } - uip_len = uip_slen + UIP_IPUDPH_LEN; - -#if UIP_CONF_IPV6 - /* For IPv6, the IP length field does not include the IPv6 IP header - length. */ - BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); - BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); -#else /* UIP_CONF_IPV6 */ - BUF->len[0] = (uip_len >> 8); - BUF->len[1] = (uip_len & 0xff); -#endif /* UIP_CONF_IPV6 */ - - BUF->ttl = uip_udp_conn->ttl; - BUF->proto = UIP_PROTO_UDP; - - UDPBUF->udplen = HTONS(uip_slen + UIP_UDPH_LEN); - UDPBUF->udpchksum = 0; - - BUF->srcport = uip_udp_conn->lport; - BUF->destport = uip_udp_conn->rport; - - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - uip_ipaddr_copy(BUF->destipaddr, uip_udp_conn->ripaddr); - - uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPTCPH_LEN]; - -#if UIP_UDP_CHECKSUMS - /* Calculate UDP checksum. */ - UDPBUF->udpchksum = ~(uip_udpchksum()); - if(UDPBUF->udpchksum == 0) { - UDPBUF->udpchksum = 0xffff; - } -#endif /* UIP_UDP_CHECKSUMS */ - - goto ip_send_nolen; -#endif /* UIP_UDP */ - - /* TCP input processing. */ - tcp_input: - UIP_STAT(++uip_stat.tcp.recv); - - /* Start of TCP input header processing code. */ - - if(uip_tcpchksum() != 0xffff) { /* Compute and check the TCP - checksum. */ - UIP_STAT(++uip_stat.tcp.drop); - UIP_STAT(++uip_stat.tcp.chkerr); - UIP_LOG("tcp: bad checksum."); - goto drop; - } - - - /* Demultiplex this segment. */ - /* First check any active connections. */ - for(uip_connr = &uip_conns[0]; uip_connr <= &uip_conns[UIP_CONNS - 1]; - ++uip_connr) { - if(uip_connr->tcpstateflags != UIP_CLOSED && - BUF->destport == uip_connr->lport && - BUF->srcport == uip_connr->rport && - uip_ipaddr_cmp(BUF->srcipaddr, uip_connr->ripaddr)) { - goto found; - } - } - - /* If we didn't find and active connection that expected the packet, - either this packet is an old duplicate, or this is a SYN packet - destined for a connection in LISTEN. If the SYN flag isn't set, - it is an old packet and we send a RST. */ - if((BUF->flags & TCP_CTL) != TCP_SYN) { - goto reset; - } - - tmp16 = BUF->destport; - /* Next, check listening connections. */ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - if(tmp16 == uip_listenports[c]) - goto found_listen; - } - - /* No matching connection found, so we send a RST packet. */ - UIP_STAT(++uip_stat.tcp.synrst); - reset: - - /* We do not send resets in response to resets. */ - if(BUF->flags & TCP_RST) { - goto drop; - } - - UIP_STAT(++uip_stat.tcp.rst); - - BUF->flags = TCP_RST | TCP_ACK; - uip_len = UIP_IPTCPH_LEN; - BUF->tcpoffset = 5 << 4; - - /* Flip the seqno and ackno fields in the TCP header. */ - c = BUF->seqno[3]; - BUF->seqno[3] = BUF->ackno[3]; - BUF->ackno[3] = c; - - c = BUF->seqno[2]; - BUF->seqno[2] = BUF->ackno[2]; - BUF->ackno[2] = c; - - c = BUF->seqno[1]; - BUF->seqno[1] = BUF->ackno[1]; - BUF->ackno[1] = c; - - c = BUF->seqno[0]; - BUF->seqno[0] = BUF->ackno[0]; - BUF->ackno[0] = c; - - /* We also have to increase the sequence number we are - acknowledging. If the least significant byte overflowed, we need - to propagate the carry to the other bytes as well. */ - if(++BUF->ackno[3] == 0) { - if(++BUF->ackno[2] == 0) { - if(++BUF->ackno[1] == 0) { - ++BUF->ackno[0]; - } - } - } - - /* Swap port numbers. */ - tmp16 = BUF->srcport; - BUF->srcport = BUF->destport; - BUF->destport = tmp16; - - /* Swap IP addresses. */ - uip_ipaddr_copy(BUF->destipaddr, BUF->srcipaddr); - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - - /* And send out the RST packet! */ - goto tcp_send_noconn; - - /* This label will be jumped to if we matched the incoming packet - with a connection in LISTEN. In that case, we should create a new - connection and send a SYNACK in return. */ - found_listen: - /* First we check if there are any connections avaliable. Unused - connections are kept in the same table as used connections, but - unused ones have the tcpstate set to CLOSED. Also, connections in - TIME_WAIT are kept track of and we'll use the oldest one if no - CLOSED connections are found. Thanks to Eddie C. Dost for a very - nice algorithm for the TIME_WAIT search. */ - uip_connr = 0; - for(c = 0; c < UIP_CONNS; ++c) { - if(uip_conns[c].tcpstateflags == UIP_CLOSED) { - uip_connr = &uip_conns[c]; - break; - } - if(uip_conns[c].tcpstateflags == UIP_TIME_WAIT) { - if(uip_connr == 0 || - uip_conns[c].timer > uip_connr->timer) { - uip_connr = &uip_conns[c]; - } - } - } - - if(uip_connr == 0) { - /* All connections are used already, we drop packet and hope that - the remote end will retransmit the packet at a time when we - have more spare connections. */ - UIP_STAT(++uip_stat.tcp.syndrop); - UIP_LOG("tcp: found no unused connections."); - goto drop; - } - uip_conn = uip_connr; - - /* Fill in the necessary fields for the new connection. */ - uip_connr->rto = uip_connr->timer = UIP_RTO; - uip_connr->sa = 0; - uip_connr->sv = 4; - uip_connr->nrtx = 0; - uip_connr->lport = BUF->destport; - uip_connr->rport = BUF->srcport; - uip_ipaddr_copy(uip_connr->ripaddr, BUF->srcipaddr); - uip_connr->tcpstateflags = UIP_SYN_RCVD; - - uip_connr->snd_nxt[0] = iss[0]; - uip_connr->snd_nxt[1] = iss[1]; - uip_connr->snd_nxt[2] = iss[2]; - uip_connr->snd_nxt[3] = iss[3]; - uip_connr->len = 1; - - /* rcv_nxt should be the seqno from the incoming packet + 1. */ - uip_connr->rcv_nxt[3] = BUF->seqno[3]; - uip_connr->rcv_nxt[2] = BUF->seqno[2]; - uip_connr->rcv_nxt[1] = BUF->seqno[1]; - uip_connr->rcv_nxt[0] = BUF->seqno[0]; - uip_add_rcv_nxt(1); - - /* Parse the TCP MSS option, if present. */ - if((BUF->tcpoffset & 0xf0) > 0x50) { - for(c = 0; c < ((BUF->tcpoffset >> 4) - 5) << 2 ;) { - opt = uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + c]; - if(opt == TCP_OPT_END) { - /* End of options. */ - break; - } else if(opt == TCP_OPT_NOOP) { - ++c; - /* NOP option. */ - } else if(opt == TCP_OPT_MSS && - uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { - /* An MSS option with the right option length. */ - tmp16 = ((u16_t)uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | - (u16_t)uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + 3 + c]; - uip_connr->initialmss = uip_connr->mss = - tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; - - /* And we are done processing options. */ - break; - } else { - /* All other options have a length field, so that we easily - can skip past them. */ - if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { - /* If the length field is zero, the options are malformed - and we don't process them further. */ - break; - } - c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; - } - } - } - - /* Our response will be a SYNACK. */ -#if UIP_ACTIVE_OPEN - tcp_send_synack: - BUF->flags = TCP_ACK; - - tcp_send_syn: - BUF->flags |= TCP_SYN; -#else /* UIP_ACTIVE_OPEN */ - tcp_send_synack: - BUF->flags = TCP_SYN | TCP_ACK; -#endif /* UIP_ACTIVE_OPEN */ - - /* We send out the TCP Maximum Segment Size option with our - SYNACK. */ - BUF->optdata[0] = TCP_OPT_MSS; - BUF->optdata[1] = TCP_OPT_MSS_LEN; - BUF->optdata[2] = (UIP_TCP_MSS) / 256; - BUF->optdata[3] = (UIP_TCP_MSS) & 255; - uip_len = UIP_IPTCPH_LEN + TCP_OPT_MSS_LEN; - BUF->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4; - goto tcp_send; - - /* This label will be jumped to if we found an active connection. */ - found: - uip_conn = uip_connr; - uip_flags = 0; - /* We do a very naive form of TCP reset processing; we just accept - any RST and kill our connection. We should in fact check if the - sequence number of this reset is wihtin our advertised window - before we accept the reset. */ - if(BUF->flags & TCP_RST) { - uip_connr->tcpstateflags = UIP_CLOSED; - UIP_LOG("tcp: got reset, aborting connection."); - uip_flags = UIP_ABORT; - UIP_APPCALL(); - goto drop; - } - /* Calculated the length of the data, if the application has sent - any data to us. */ - c = (BUF->tcpoffset >> 4) << 2; - /* uip_len will contain the length of the actual TCP data. This is - calculated by subtracing the length of the TCP header (in - c) and the length of the IP header (20 bytes). */ - uip_len = uip_len - c - UIP_IPH_LEN; - - /* First, check if the sequence number of the incoming packet is - what we're expecting next. If not, we send out an ACK with the - correct numbers in. */ - if(!(((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) && - ((BUF->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)))) { - if((uip_len > 0 || ((BUF->flags & (TCP_SYN | TCP_FIN)) != 0)) && - (BUF->seqno[0] != uip_connr->rcv_nxt[0] || - BUF->seqno[1] != uip_connr->rcv_nxt[1] || - BUF->seqno[2] != uip_connr->rcv_nxt[2] || - BUF->seqno[3] != uip_connr->rcv_nxt[3])) { - goto tcp_send_ack; - } - } - - /* Next, check if the incoming segment acknowledges any outstanding - data. If so, we update the sequence number, reset the length of - the outstanding data, calculate RTT estimations, and reset the - retransmission timer. */ - if((BUF->flags & TCP_ACK) && uip_outstanding(uip_connr)) { - uip_add32(uip_connr->snd_nxt, uip_connr->len); - - if(BUF->ackno[0] == uip_acc32[0] && - BUF->ackno[1] == uip_acc32[1] && - BUF->ackno[2] == uip_acc32[2] && - BUF->ackno[3] == uip_acc32[3]) { - /* Update sequence number. */ - uip_connr->snd_nxt[0] = uip_acc32[0]; - uip_connr->snd_nxt[1] = uip_acc32[1]; - uip_connr->snd_nxt[2] = uip_acc32[2]; - uip_connr->snd_nxt[3] = uip_acc32[3]; - - - /* Do RTT estimation, unless we have done retransmissions. */ - if(uip_connr->nrtx == 0) { - signed char m; - m = uip_connr->rto - uip_connr->timer; - /* This is taken directly from VJs original code in his paper */ - m = m - (uip_connr->sa >> 3); - uip_connr->sa += m; - if(m < 0) { - m = -m; - } - m = m - (uip_connr->sv >> 2); - uip_connr->sv += m; - uip_connr->rto = (uip_connr->sa >> 3) + uip_connr->sv; - - } - /* Set the acknowledged flag. */ - uip_flags = UIP_ACKDATA; - /* Reset the retransmission timer. */ - uip_connr->timer = uip_connr->rto; - - /* Reset length of outstanding data. */ - uip_connr->len = 0; - } - - } - - /* Do different things depending on in what state the connection is. */ - switch(uip_connr->tcpstateflags & UIP_TS_MASK) { - /* CLOSED and LISTEN are not handled here. CLOSE_WAIT is not - implemented, since we force the application to close when the - peer sends a FIN (hence the application goes directly from - ESTABLISHED to LAST_ACK). */ - case UIP_SYN_RCVD: - /* In SYN_RCVD we have sent out a SYNACK in response to a SYN, and - we are waiting for an ACK that acknowledges the data we sent - out the last time. Therefore, we want to have the UIP_ACKDATA - flag set. If so, we enter the ESTABLISHED state. */ - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_ESTABLISHED; - uip_flags = UIP_CONNECTED; - uip_connr->len = 0; - if(uip_len > 0) { - uip_flags |= UIP_NEWDATA; - uip_add_rcv_nxt(uip_len); - } - uip_slen = 0; - UIP_APPCALL(); - goto appsend; - } - goto drop; -#if UIP_ACTIVE_OPEN - case UIP_SYN_SENT: - /* In SYN_SENT, we wait for a SYNACK that is sent in response to - our SYN. The rcv_nxt is set to sequence number in the SYNACK - plus one, and we send an ACK. We move into the ESTABLISHED - state. */ - if((uip_flags & UIP_ACKDATA) && - (BUF->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)) { - - /* Parse the TCP MSS option, if present. */ - if((BUF->tcpoffset & 0xf0) > 0x50) { - for(c = 0; c < ((BUF->tcpoffset >> 4) - 5) << 2 ;) { - opt = uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + c]; - if(opt == TCP_OPT_END) { - /* End of options. */ - break; - } else if(opt == TCP_OPT_NOOP) { - ++c; - /* NOP option. */ - } else if(opt == TCP_OPT_MSS && - uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { - /* An MSS option with the right option length. */ - tmp16 = (uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | - uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 3 + c]; - uip_connr->initialmss = - uip_connr->mss = tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; - - /* And we are done processing options. */ - break; - } else { - /* All other options have a length field, so that we easily - can skip past them. */ - if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { - /* If the length field is zero, the options are malformed - and we don't process them further. */ - break; - } - c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; - } - } - } - uip_connr->tcpstateflags = UIP_ESTABLISHED; - uip_connr->rcv_nxt[0] = BUF->seqno[0]; - uip_connr->rcv_nxt[1] = BUF->seqno[1]; - uip_connr->rcv_nxt[2] = BUF->seqno[2]; - uip_connr->rcv_nxt[3] = BUF->seqno[3]; - uip_add_rcv_nxt(1); - uip_flags = UIP_CONNECTED | UIP_NEWDATA; - uip_connr->len = 0; - uip_len = 0; - uip_slen = 0; - UIP_APPCALL(); - goto appsend; - } - /* Inform the application that the connection failed */ - uip_flags = UIP_ABORT; - UIP_APPCALL(); - /* The connection is closed after we send the RST */ - uip_conn->tcpstateflags = UIP_CLOSED; - goto reset; -#endif /* UIP_ACTIVE_OPEN */ - - case UIP_ESTABLISHED: - /* In the ESTABLISHED state, we call upon the application to feed - data into the uip_buf. If the UIP_ACKDATA flag is set, the - application should put new data into the buffer, otherwise we are - retransmitting an old segment, and the application should put that - data into the buffer. - - If the incoming packet is a FIN, we should close the connection on - this side as well, and we send out a FIN and enter the LAST_ACK - state. We require that there is no outstanding data; otherwise the - sequence numbers will be screwed up. */ - - if(BUF->flags & TCP_FIN && !(uip_connr->tcpstateflags & UIP_STOPPED)) { - if(uip_outstanding(uip_connr)) { - goto drop; - } - uip_add_rcv_nxt(1 + uip_len); - uip_flags |= UIP_CLOSE; - if(uip_len > 0) { - uip_flags |= UIP_NEWDATA; - } - UIP_APPCALL(); - uip_connr->len = 1; - uip_connr->tcpstateflags = UIP_LAST_ACK; - uip_connr->nrtx = 0; - tcp_send_finack: - BUF->flags = TCP_FIN | TCP_ACK; - goto tcp_send_nodata; - } - - /* Check the URG flag. If this is set, the segment carries urgent - data that we must pass to the application. */ - if((BUF->flags & TCP_URG) != 0) { -#if UIP_URGDATA > 0 - uip_urglen = (BUF->urgp[0] << 8) | BUF->urgp[1]; - if(uip_urglen > uip_len) { - /* There is more urgent data in the next segment to come. */ - uip_urglen = uip_len; - } - uip_add_rcv_nxt(uip_urglen); - uip_len -= uip_urglen; - uip_urgdata = uip_appdata; - uip_appdata += uip_urglen; - } else { - uip_urglen = 0; -#else /* UIP_URGDATA > 0 */ - uip_appdata = ((char *)uip_appdata) + ((BUF->urgp[0] << 8) | BUF->urgp[1]); - uip_len -= (BUF->urgp[0] << 8) | BUF->urgp[1]; -#endif /* UIP_URGDATA > 0 */ - } - - /* If uip_len > 0 we have TCP data in the packet, and we flag this - by setting the UIP_NEWDATA flag and update the sequence number - we acknowledge. If the application has stopped the dataflow - using uip_stop(), we must not accept any data packets from the - remote host. */ - if(uip_len > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) { - uip_flags |= UIP_NEWDATA; - uip_add_rcv_nxt(uip_len); - } - - /* Check if the available buffer space advertised by the other end - is smaller than the initial MSS for this connection. If so, we - set the current MSS to the window size to ensure that the - application does not send more data than the other end can - handle. - - If the remote host advertises a zero window, we set the MSS to - the initial MSS so that the application will send an entire MSS - of data. This data will not be acknowledged by the receiver, - and the application will retransmit it. This is called the - "persistent timer" and uses the retransmission mechanim. - */ - tmp16 = ((u16_t)BUF->wnd[0] << 8) + (u16_t)BUF->wnd[1]; - if(tmp16 > uip_connr->initialmss || - tmp16 == 0) { - tmp16 = uip_connr->initialmss; - } - uip_connr->mss = tmp16; - - /* If this packet constitutes an ACK for outstanding data (flagged - by the UIP_ACKDATA flag, we should call the application since it - might want to send more data. If the incoming packet had data - from the peer (as flagged by the UIP_NEWDATA flag), the - application must also be notified. - - When the application is called, the global variable uip_len - contains the length of the incoming data. The application can - access the incoming data through the global pointer - uip_appdata, which usually points UIP_IPTCPH_LEN + UIP_LLH_LEN - bytes into the uip_buf array. - - If the application wishes to send any data, this data should be - put into the uip_appdata and the length of the data should be - put into uip_len. If the application don't have any data to - send, uip_len must be set to 0. */ - if(uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) { - uip_slen = 0; - UIP_APPCALL(); - - appsend: - - if(uip_flags & UIP_ABORT) { - uip_slen = 0; - uip_connr->tcpstateflags = UIP_CLOSED; - BUF->flags = TCP_RST | TCP_ACK; - goto tcp_send_nodata; - } - - if(uip_flags & UIP_CLOSE) { - uip_slen = 0; - uip_connr->len = 1; - uip_connr->tcpstateflags = UIP_FIN_WAIT_1; - uip_connr->nrtx = 0; - BUF->flags = TCP_FIN | TCP_ACK; - goto tcp_send_nodata; - } - - /* If uip_slen > 0, the application has data to be sent. */ - if(uip_slen > 0) { - - /* If the connection has acknowledged data, the contents of - the ->len variable should be discarded. */ - if((uip_flags & UIP_ACKDATA) != 0) { - uip_connr->len = 0; - } - - /* If the ->len variable is non-zero the connection has - already data in transit and cannot send anymore right - now. */ - if(uip_connr->len == 0) { - - /* The application cannot send more than what is allowed by - the mss (the minumum of the MSS and the available - window). */ - if(uip_slen > uip_connr->mss) { - uip_slen = uip_connr->mss; - } - - /* Remember how much data we send out now so that we know - when everything has been acknowledged. */ - uip_connr->len = uip_slen; - } else { - - /* If the application already had unacknowledged data, we - make sure that the application does not send (i.e., - retransmit) out more than it previously sent out. */ - uip_slen = uip_connr->len; - } - } - uip_connr->nrtx = 0; - apprexmit: - uip_appdata = uip_sappdata; - - /* If the application has data to be sent, or if the incoming - packet had new data in it, we must send out a packet. */ - if(uip_slen > 0 && uip_connr->len > 0) { - /* Add the length of the IP and TCP headers. */ - uip_len = uip_connr->len + UIP_TCPIP_HLEN; - /* We always set the ACK flag in response packets. */ - BUF->flags = TCP_ACK | TCP_PSH; - /* Send the packet. */ - goto tcp_send_noopts; - } - /* If there is no data to send, just send out a pure ACK if - there is newdata. */ - if(uip_flags & UIP_NEWDATA) { - uip_len = UIP_TCPIP_HLEN; - BUF->flags = TCP_ACK; - goto tcp_send_noopts; - } - } - goto drop; - case UIP_LAST_ACK: - /* We can close this connection if the peer has acknowledged our - FIN. This is indicated by the UIP_ACKDATA flag. */ - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_CLOSED; - uip_flags = UIP_CLOSE; - UIP_APPCALL(); - } - break; - - case UIP_FIN_WAIT_1: - /* The application has closed the connection, but the remote host - hasn't closed its end yet. Thus we do nothing but wait for a - FIN from the other side. */ - if(uip_len > 0) { - uip_add_rcv_nxt(uip_len); - } - if(BUF->flags & TCP_FIN) { - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_TIME_WAIT; - uip_connr->timer = 0; - uip_connr->len = 0; - } else { - uip_connr->tcpstateflags = UIP_CLOSING; - } - uip_add_rcv_nxt(1); - uip_flags = UIP_CLOSE; - UIP_APPCALL(); - goto tcp_send_ack; - } else if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_FIN_WAIT_2; - uip_connr->len = 0; - goto drop; - } - if(uip_len > 0) { - goto tcp_send_ack; - } - goto drop; - - case UIP_FIN_WAIT_2: - if(uip_len > 0) { - uip_add_rcv_nxt(uip_len); - } - if(BUF->flags & TCP_FIN) { - uip_connr->tcpstateflags = UIP_TIME_WAIT; - uip_connr->timer = 0; - uip_add_rcv_nxt(1); - uip_flags = UIP_CLOSE; - UIP_APPCALL(); - goto tcp_send_ack; - } - if(uip_len > 0) { - goto tcp_send_ack; - } - goto drop; - - case UIP_TIME_WAIT: - goto tcp_send_ack; - - case UIP_CLOSING: - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_TIME_WAIT; - uip_connr->timer = 0; - } - } - goto drop; - - - /* We jump here when we are ready to send the packet, and just want - to set the appropriate TCP sequence numbers in the TCP header. */ - tcp_send_ack: - BUF->flags = TCP_ACK; - tcp_send_nodata: - uip_len = UIP_IPTCPH_LEN; - tcp_send_noopts: - BUF->tcpoffset = (UIP_TCPH_LEN / 4) << 4; - tcp_send: - /* We're done with the input processing. We are now ready to send a - reply. Our job is to fill in all the fields of the TCP and IP - headers before calculating the checksum and finally send the - packet. */ - BUF->ackno[0] = uip_connr->rcv_nxt[0]; - BUF->ackno[1] = uip_connr->rcv_nxt[1]; - BUF->ackno[2] = uip_connr->rcv_nxt[2]; - BUF->ackno[3] = uip_connr->rcv_nxt[3]; - - BUF->seqno[0] = uip_connr->snd_nxt[0]; - BUF->seqno[1] = uip_connr->snd_nxt[1]; - BUF->seqno[2] = uip_connr->snd_nxt[2]; - BUF->seqno[3] = uip_connr->snd_nxt[3]; - - BUF->proto = UIP_PROTO_TCP; - - BUF->srcport = uip_connr->lport; - BUF->destport = uip_connr->rport; - - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - uip_ipaddr_copy(BUF->destipaddr, uip_connr->ripaddr); - - if(uip_connr->tcpstateflags & UIP_STOPPED) { - /* If the connection has issued uip_stop(), we advertise a zero - window so that the remote host will stop sending data. */ - BUF->wnd[0] = BUF->wnd[1] = 0; - } else { - BUF->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8); - BUF->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff); - } - - tcp_send_noconn: - BUF->ttl = UIP_TTL; -#if UIP_CONF_IPV6 - /* For IPv6, the IP length field does not include the IPv6 IP header - length. */ - BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); - BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); -#else /* UIP_CONF_IPV6 */ - BUF->len[0] = (uip_len >> 8); - BUF->len[1] = (uip_len & 0xff); -#endif /* UIP_CONF_IPV6 */ - - BUF->urgp[0] = BUF->urgp[1] = 0; - - /* Calculate TCP checksum. */ - BUF->tcpchksum = 0; - BUF->tcpchksum = ~(uip_tcpchksum()); - - ip_send_nolen: - -#if UIP_CONF_IPV6 - BUF->vtc = 0x60; - BUF->tcflow = 0x00; - BUF->flow = 0x00; -#else /* UIP_CONF_IPV6 */ - BUF->vhl = 0x45; - BUF->tos = 0; - BUF->ipoffset[0] = BUF->ipoffset[1] = 0; - ++ipid; - BUF->ipid[0] = ipid >> 8; - BUF->ipid[1] = ipid & 0xff; - /* Calculate IP checksum. */ - BUF->ipchksum = 0; - BUF->ipchksum = ~(uip_ipchksum()); - DEBUG_PRINTF("uip ip_send_nolen: chkecum 0x%04x\n", uip_ipchksum()); -#endif /* UIP_CONF_IPV6 */ - - UIP_STAT(++uip_stat.tcp.sent); - send: - DEBUG_PRINTF("Sending packet with length %d (%d)\n", uip_len, - (BUF->len[0] << 8) | BUF->len[1]); - - UIP_STAT(++uip_stat.ip.sent); - /* Return and let the caller do the actual transmission. */ - uip_flags = 0; - return; - drop: - uip_len = 0; - uip_flags = 0; - return; -} -/*---------------------------------------------------------------------------*/ -u16_t -htons(u16_t val) -{ - return HTONS(val); -} -/*---------------------------------------------------------------------------*/ -void -uip_send(const void *data, int len) -{ - uip_slen = len; - if(len > 0) { - if(data != uip_sappdata) { - memcpy(uip_sappdata, (data), uip_slen); - } - } -} -/** @} */ diff --git a/libraries/UIPEthernet/utility/uip.h b/libraries/UIPEthernet/utility/uip.h deleted file mode 100644 index 4fc0452..0000000 --- a/libraries/UIPEthernet/utility/uip.h +++ /dev/null @@ -1,1601 +0,0 @@ - -/** - * \addtogroup uip - * @{ - */ - -/** - * \file - * Header file for the uIP TCP/IP stack. - * \author Adam Dunkels - * - * The uIP TCP/IP stack header file contains definitions for a number - * of C macros that are used by uIP programs as well as internal uIP - * structures, TCP/IP header structures and function declarations. - * - */ - - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip.h,v 1.40 2006/06/08 07:12:07 adam Exp $ - * - */ - -#ifndef __UIP_H__ -#define __UIP_H__ - -#include "uipopt.h" - -/** - * Repressentation of an IP address. - * - */ -typedef u16_t uip_ip4addr_t[2]; -typedef u16_t uip_ip6addr_t[8]; -#if UIP_CONF_IPV6 -typedef uip_ip6addr_t uip_ipaddr_t; -#else /* UIP_CONF_IPV6 */ -typedef uip_ip4addr_t uip_ipaddr_t; -#endif /* UIP_CONF_IPV6 */ - -/*---------------------------------------------------------------------------*/ -/* First, the functions that should be called from the - * system. Initialization, the periodic timer and incoming packets are - * handled by the following three functions. - */ - -/** - * \defgroup uipconffunc uIP configuration functions - * @{ - * - * The uIP configuration functions are used for setting run-time - * parameters in uIP such as IP addresses. - */ - -/** - * Set the IP address of this host. - * - * The IP address is represented as a 4-byte array where the first - * octet of the IP address is put in the first member of the 4-byte - * array. - * - * Example: - \code - - uip_ipaddr_t addr; - - uip_ipaddr(&addr, 192,168,1,2); - uip_sethostaddr(&addr); - - \endcode - * \param addr A pointer to an IP address of type uip_ipaddr_t; - * - * \sa uip_ipaddr() - * - * \hideinitializer - */ -#define uip_sethostaddr(addr) uip_ipaddr_copy(uip_hostaddr, (addr)) - -/** - * Get the IP address of this host. - * - * The IP address is represented as a 4-byte array where the first - * octet of the IP address is put in the first member of the 4-byte - * array. - * - * Example: - \code - uip_ipaddr_t hostaddr; - - uip_gethostaddr(&hostaddr); - \endcode - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the currently configured IP address. - * - * \hideinitializer - */ -#define uip_gethostaddr(addr) uip_ipaddr_copy((addr), uip_hostaddr) - -/** - * Set the default router's IP address. - * - * \param addr A pointer to a uip_ipaddr_t variable containing the IP - * address of the default router. - * - * \sa uip_ipaddr() - * - * \hideinitializer - */ -#define uip_setdraddr(addr) uip_ipaddr_copy(uip_draddr, (addr)) - -/** - * Set the netmask. - * - * \param addr A pointer to a uip_ipaddr_t variable containing the IP - * address of the netmask. - * - * \sa uip_ipaddr() - * - * \hideinitializer - */ -#define uip_setnetmask(addr) uip_ipaddr_copy(uip_netmask, (addr)) - - -/** - * Get the default router's IP address. - * - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the IP address of the default router. - * - * \hideinitializer - */ -#define uip_getdraddr(addr) uip_ipaddr_copy((addr), uip_draddr) - -/** - * Get the netmask. - * - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the value of the netmask. - * - * \hideinitializer - */ -#define uip_getnetmask(addr) uip_ipaddr_copy((addr), uip_netmask) - -/** @} */ - -/** - * \defgroup uipinit uIP initialization functions - * @{ - * - * The uIP initialization functions are used for booting uIP. - */ - -/** - * uIP initialization function. - * - * This function should be called at boot up to initilize the uIP - * TCP/IP stack. - */ -void uip_init(void); - -/** - * uIP initialization function. - * - * This function may be used at boot time to set the initial ip_id. - */ -void uip_setipid(u16_t id); - -/** @} */ - -/** - * \defgroup uipdevfunc uIP device driver functions - * @{ - * - * These functions are used by a network device driver for interacting - * with uIP. - */ - -/** - * Process an incoming packet. - * - * This function should be called when the device driver has received - * a packet from the network. The packet from the device driver must - * be present in the uip_buf buffer, and the length of the packet - * should be placed in the uip_len variable. - * - * When the function returns, there may be an outbound packet placed - * in the uip_buf packet buffer. If so, the uip_len variable is set to - * the length of the packet. If no packet is to be sent out, the - * uip_len variable is set to 0. - * - * The usual way of calling the function is presented by the source - * code below. - \code - uip_len = devicedriver_poll(); - if(uip_len > 0) { - uip_input(); - if(uip_len > 0) { - devicedriver_send(); - } - } - \endcode - * - * \note If you are writing a uIP device driver that needs ARP - * (Address Resolution Protocol), e.g., when running uIP over - * Ethernet, you will need to call the uIP ARP code before calling - * this function: - \code - #define BUF ((struct uip_eth_hdr *)&uip_buf[0]) - uip_len = ethernet_devicedrver_poll(); - if(uip_len > 0) { - if(BUF->type == HTONS(UIP_ETHTYPE_IP)) { - uip_arp_ipin(); - uip_input(); - if(uip_len > 0) { - uip_arp_out(); - ethernet_devicedriver_send(); - } - } else if(BUF->type == HTONS(UIP_ETHTYPE_ARP)) { - uip_arp_arpin(); - if(uip_len > 0) { - ethernet_devicedriver_send(); - } - } - \endcode - * - * \hideinitializer - */ -#define uip_input() uip_process(UIP_DATA) - -/** - * Periodic processing for a connection identified by its number. - * - * This function does the necessary periodic processing (timers, - * polling) for a uIP TCP conneciton, and should be called when the - * periodic uIP timer goes off. It should be called for every - * connection, regardless of whether they are open of closed. - * - * When the function returns, it may have an outbound packet waiting - * for service in the uIP packet buffer, and if so the uip_len - * variable is set to a value larger than zero. The device driver - * should be called to send out the packet. - * - * The ususal way of calling the function is through a for() loop like - * this: - \code - for(i = 0; i < UIP_CONNS; ++i) { - uip_periodic(i); - if(uip_len > 0) { - devicedriver_send(); - } - } - \endcode - * - * \note If you are writing a uIP device driver that needs ARP - * (Address Resolution Protocol), e.g., when running uIP over - * Ethernet, you will need to call the uip_arp_out() function before - * calling the device driver: - \code - for(i = 0; i < UIP_CONNS; ++i) { - uip_periodic(i); - if(uip_len > 0) { - uip_arp_out(); - ethernet_devicedriver_send(); - } - } - \endcode - * - * \param conn The number of the connection which is to be periodically polled. - * - * \hideinitializer - */ -#define uip_periodic(conn) do { uip_conn = &uip_conns[conn]; \ - uip_process(UIP_TIMER); } while (0) - -/** - * - * - */ -#define uip_conn_active(conn) (uip_conns[conn].tcpstateflags != UIP_CLOSED) - -/** - * Perform periodic processing for a connection identified by a pointer - * to its structure. - * - * Same as uip_periodic() but takes a pointer to the actual uip_conn - * struct instead of an integer as its argument. This function can be - * used to force periodic processing of a specific connection. - * - * \param conn A pointer to the uip_conn struct for the connection to - * be processed. - * - * \hideinitializer - */ -#define uip_periodic_conn(conn) do { uip_conn = conn; \ - uip_process(UIP_TIMER); } while (0) - -/** - * Reuqest that a particular connection should be polled. - * - * Similar to uip_periodic_conn() but does not perform any timer - * processing. The application is polled for new data. - * - * \param conn A pointer to the uip_conn struct for the connection to - * be processed. - * - * \hideinitializer - */ -#define uip_poll_conn(conn) do { uip_conn = conn; \ - uip_process(UIP_POLL_REQUEST); } while (0) - - -#if UIP_UDP -/** - * Periodic processing for a UDP connection identified by its number. - * - * This function is essentially the same as uip_periodic(), but for - * UDP connections. It is called in a similar fashion as the - * uip_periodic() function: - \code - for(i = 0; i < UIP_UDP_CONNS; i++) { - uip_udp_periodic(i); - if(uip_len > 0) { - devicedriver_send(); - } - } - \endcode - * - * \note As for the uip_periodic() function, special care has to be - * taken when using uIP together with ARP and Ethernet: - \code - for(i = 0; i < UIP_UDP_CONNS; i++) { - uip_udp_periodic(i); - if(uip_len > 0) { - uip_arp_out(); - ethernet_devicedriver_send(); - } - } - \endcode - * - * \param conn The number of the UDP connection to be processed. - * - * \hideinitializer - */ -#define uip_udp_periodic(conn) do { uip_udp_conn = &uip_udp_conns[conn]; \ - uip_process(UIP_UDP_TIMER); } while (0) - -/** - * Periodic processing for a UDP connection identified by a pointer to - * its structure. - * - * Same as uip_udp_periodic() but takes a pointer to the actual - * uip_conn struct instead of an integer as its argument. This - * function can be used to force periodic processing of a specific - * connection. - * - * \param conn A pointer to the uip_udp_conn struct for the connection - * to be processed. - * - * \hideinitializer - */ -#define uip_udp_periodic_conn(conn) do { uip_udp_conn = conn; \ - uip_process(UIP_UDP_TIMER); } while (0) - - -#endif /* UIP_UDP */ - -/** - * The uIP packet buffer. - * - * The uip_buf array is used to hold incoming and outgoing - * packets. The device driver should place incoming data into this - * buffer. When sending data, the device driver should read the link - * level headers and the TCP/IP headers from this buffer. The size of - * the link level headers is configured by the UIP_LLH_LEN define. - * - * \note The application data need not be placed in this buffer, so - * the device driver must read it from the place pointed to by the - * uip_appdata pointer as illustrated by the following example: - \code - void - devicedriver_send(void) - { - hwsend(&uip_buf[0], UIP_LLH_LEN); - if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) { - hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN); - } else { - hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN); - hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN); - } - } - \endcode - */ -extern u8_t uip_buf[UIP_BUFSIZE+2]; - -/** @} */ - -/*---------------------------------------------------------------------------*/ -/* Functions that are used by the uIP application program. Opening and - * closing connections, sending and receiving data, etc. is all - * handled by the functions below. -*/ -/** - * \defgroup uipappfunc uIP application functions - * @{ - * - * Functions used by an application running of top of uIP. - */ - -/** - * Start listening to the specified port. - * - * \note Since this function expects the port number in network byte - * order, a conversion using HTONS() or htons() is necessary. - * - \code - uip_listen(HTONS(80)); - \endcode - * - * \param port A 16-bit port number in network byte order. - */ -void uip_listen(u16_t port); - -/** - * Stop listening to the specified port. - * - * \note Since this function expects the port number in network byte - * order, a conversion using HTONS() or htons() is necessary. - * - \code - uip_unlisten(HTONS(80)); - \endcode - * - * \param port A 16-bit port number in network byte order. - */ -void uip_unlisten(u16_t port); - -/** - * Connect to a remote host using TCP. - * - * This function is used to start a new connection to the specified - * port on the specied host. It allocates a new connection identifier, - * sets the connection to the SYN_SENT state and sets the - * retransmission timer to 0. This will cause a TCP SYN segment to be - * sent out the next time this connection is periodically processed, - * which usually is done within 0.5 seconds after the call to - * uip_connect(). - * - * \note This function is avaliable only if support for active open - * has been configured by defining UIP_ACTIVE_OPEN to 1 in uipopt.h. - * - * \note Since this function requires the port number to be in network - * byte order, a conversion using HTONS() or htons() is necessary. - * - \code - uip_ipaddr_t ipaddr; - - uip_ipaddr(&ipaddr, 192,168,1,2); - uip_connect(&ipaddr, HTONS(80)); - \endcode - * - * \param ripaddr The IP address of the remote hot. - * - * \param port A 16-bit port number in network byte order. - * - * \return A pointer to the uIP connection identifier for the new connection, - * or NULL if no connection could be allocated. - * - */ -struct uip_conn *uip_connect(uip_ipaddr_t *ripaddr, u16_t port); - - - -/** - * \internal - * - * Check if a connection has outstanding (i.e., unacknowledged) data. - * - * \param conn A pointer to the uip_conn structure for the connection. - * - * \hideinitializer - */ -#define uip_outstanding(conn) ((conn)->len) - -/** - * Send data on the current connection. - * - * This function is used to send out a single segment of TCP - * data. Only applications that have been invoked by uIP for event - * processing can send data. - * - * The amount of data that actually is sent out after a call to this - * funcion is determined by the maximum amount of data TCP allows. uIP - * will automatically crop the data so that only the appropriate - * amount of data is sent. The function uip_mss() can be used to query - * uIP for the amount of data that actually will be sent. - * - * \note This function does not guarantee that the sent data will - * arrive at the destination. If the data is lost in the network, the - * application will be invoked with the uip_rexmit() event being - * set. The application will then have to resend the data using this - * function. - * - * \param data A pointer to the data which is to be sent. - * - * \param len The maximum amount of data bytes to be sent. - * - * \hideinitializer - */ -void uip_send(const void *data, int len); - -/** - * The length of any incoming data that is currently avaliable (if avaliable) - * in the uip_appdata buffer. - * - * The test function uip_data() must first be used to check if there - * is any data available at all. - * - * \hideinitializer - */ -/*void uip_datalen(void);*/ -#define uip_datalen() uip_len - -/** - * The length of any out-of-band data (urgent data) that has arrived - * on the connection. - * - * \note The configuration parameter UIP_URGDATA must be set for this - * function to be enabled. - * - * \hideinitializer - */ -#define uip_urgdatalen() uip_urglen - -/** - * Close the current connection. - * - * This function will close the current connection in a nice way. - * - * \hideinitializer - */ -#define uip_close() (uip_flags = UIP_CLOSE) - -/** - * Abort the current connection. - * - * This function will abort (reset) the current connection, and is - * usually used when an error has occured that prevents using the - * uip_close() function. - * - * \hideinitializer - */ -#define uip_abort() (uip_flags = UIP_ABORT) - -/** - * Tell the sending host to stop sending data. - * - * This function will close our receiver's window so that we stop - * receiving data for the current connection. - * - * \hideinitializer - */ -#define uip_stop() (uip_conn->tcpstateflags |= UIP_STOPPED) - -/** - * Find out if the current connection has been previously stopped with - * uip_stop(). - * - * \hideinitializer - */ -#define uip_stopped(conn) ((conn)->tcpstateflags & UIP_STOPPED) - -/** - * Restart the current connection, if is has previously been stopped - * with uip_stop(). - * - * This function will open the receiver's window again so that we - * start receiving data for the current connection. - * - * \hideinitializer - */ -#define uip_restart() do { uip_flags |= UIP_NEWDATA; \ - uip_conn->tcpstateflags &= ~UIP_STOPPED; \ - } while(0) - - -/* uIP tests that can be made to determine in what state the current - connection is, and what the application function should do. */ - -/** - * Is the current connection a UDP connection? - * - * This function checks whether the current connection is a UDP connection. - * - * \hideinitializer - * - */ -#define uip_udpconnection() (uip_conn == NULL) - -/** - * Is new incoming data available? - * - * Will reduce to non-zero if there is new data for the application - * present at the uip_appdata pointer. The size of the data is - * avaliable through the uip_len variable. - * - * \hideinitializer - */ -#define uip_newdata() (uip_flags & UIP_NEWDATA) - -/** - * Has previously sent data been acknowledged? - * - * Will reduce to non-zero if the previously sent data has been - * acknowledged by the remote host. This means that the application - * can send new data. - * - * \hideinitializer - */ -#define uip_acked() (uip_flags & UIP_ACKDATA) - -/** - * Has the connection just been connected? - * - * Reduces to non-zero if the current connection has been connected to - * a remote host. This will happen both if the connection has been - * actively opened (with uip_connect()) or passively opened (with - * uip_listen()). - * - * \hideinitializer - */ -#define uip_connected() (uip_flags & UIP_CONNECTED) - -/** - * Has the connection been closed by the other end? - * - * Is non-zero if the connection has been closed by the remote - * host. The application may then do the necessary clean-ups. - * - * \hideinitializer - */ -#define uip_closed() (uip_flags & UIP_CLOSE) - -/** - * Has the connection been aborted by the other end? - * - * Non-zero if the current connection has been aborted (reset) by the - * remote host. - * - * \hideinitializer - */ -#define uip_aborted() (uip_flags & UIP_ABORT) - -/** - * Has the connection timed out? - * - * Non-zero if the current connection has been aborted due to too many - * retransmissions. - * - * \hideinitializer - */ -#define uip_timedout() (uip_flags & UIP_TIMEDOUT) - -/** - * Do we need to retransmit previously data? - * - * Reduces to non-zero if the previously sent data has been lost in - * the network, and the application should retransmit it. The - * application should send the exact same data as it did the last - * time, using the uip_send() function. - * - * \hideinitializer - */ -#define uip_rexmit() (uip_flags & UIP_REXMIT) - -/** - * Is the connection being polled by uIP? - * - * Is non-zero if the reason the application is invoked is that the - * current connection has been idle for a while and should be - * polled. - * - * The polling event can be used for sending data without having to - * wait for the remote host to send data. - * - * \hideinitializer - */ -#define uip_poll() (uip_flags & UIP_POLL) - -/** - * Get the initial maxium segment size (MSS) of the current - * connection. - * - * \hideinitializer - */ -#define uip_initialmss() (uip_conn->initialmss) - -/** - * Get the current maxium segment size that can be sent on the current - * connection. - * - * The current maxiumum segment size that can be sent on the - * connection is computed from the receiver's window and the MSS of - * the connection (which also is available by calling - * uip_initialmss()). - * - * \hideinitializer - */ -#define uip_mss() (uip_conn->mss) - -/** - * Set up a new UDP connection. - * - * This function sets up a new UDP connection. The function will - * automatically allocate an unused local port for the new - * connection. However, another port can be chosen by using the - * uip_udp_bind() call, after the uip_udp_new() function has been - * called. - * - * Example: - \code - uip_ipaddr_t addr; - struct uip_udp_conn *c; - - uip_ipaddr(&addr, 192,168,2,1); - c = uip_udp_new(&addr, HTONS(12345)); - if(c != NULL) { - uip_udp_bind(c, HTONS(12344)); - } - \endcode - * \param ripaddr The IP address of the remote host. - * - * \param rport The remote port number in network byte order. - * - * \return The uip_udp_conn structure for the new connection or NULL - * if no connection could be allocated. - */ -struct uip_udp_conn *uip_udp_new(uip_ipaddr_t *ripaddr, u16_t rport); - -/** - * Removed a UDP connection. - * - * \param conn A pointer to the uip_udp_conn structure for the connection. - * - * \hideinitializer - */ -#define uip_udp_remove(conn) (conn)->lport = 0 - -/** - * Bind a UDP connection to a local port. - * - * \param conn A pointer to the uip_udp_conn structure for the - * connection. - * - * \param port The local port number, in network byte order. - * - * \hideinitializer - */ -#define uip_udp_bind(conn, port) (conn)->lport = port - -/** - * Send a UDP datagram of length len on the current connection. - * - * This function can only be called in response to a UDP event (poll - * or newdata). The data must be present in the uip_buf buffer, at the - * place pointed to by the uip_appdata pointer. - * - * \param len The length of the data in the uip_buf buffer. - * - * \hideinitializer - */ -#define uip_udp_send(len) uip_send((char *)uip_appdata, len) - -/** @} */ - -/* uIP convenience and converting functions. */ - -/** - * \defgroup uipconvfunc uIP conversion functions - * @{ - * - * These functions can be used for converting between different data - * formats used by uIP. - */ - -/** - * Construct an IP address from four bytes. - * - * This function constructs an IP address of the type that uIP handles - * internally from four bytes. The function is handy for specifying IP - * addresses to use with e.g. the uip_connect() function. - * - * Example: - \code - uip_ipaddr_t ipaddr; - struct uip_conn *c; - - uip_ipaddr(&ipaddr, 192,168,1,2); - c = uip_connect(&ipaddr, HTONS(80)); - \endcode - * - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the IP address. - * - * \param addr0 The first octet of the IP address. - * \param addr1 The second octet of the IP address. - * \param addr2 The third octet of the IP address. - * \param addr3 The forth octet of the IP address. - * - * \hideinitializer - */ -#define uip_ipaddr(addr, addr0,addr1,addr2,addr3) do { \ - ((u16_t *)(addr))[0] = HTONS(((addr0) << 8) | (addr1)); \ - ((u16_t *)(addr))[1] = HTONS(((addr2) << 8) | (addr3)); \ - } while(0) - -/** - * Construct an IPv6 address from eight 16-bit words. - * - * This function constructs an IPv6 address. - * - * \hideinitializer - */ -#define uip_ip6addr(addr, addr0,addr1,addr2,addr3,addr4,addr5,addr6,addr7) do { \ - ((u16_t *)(addr))[0] = HTONS((addr0)); \ - ((u16_t *)(addr))[1] = HTONS((addr1)); \ - ((u16_t *)(addr))[2] = HTONS((addr2)); \ - ((u16_t *)(addr))[3] = HTONS((addr3)); \ - ((u16_t *)(addr))[4] = HTONS((addr4)); \ - ((u16_t *)(addr))[5] = HTONS((addr5)); \ - ((u16_t *)(addr))[6] = HTONS((addr6)); \ - ((u16_t *)(addr))[7] = HTONS((addr7)); \ - } while(0) - -/** - * Copy an IP address to another IP address. - * - * Copies an IP address from one place to another. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2; - - uip_ipaddr(&ipaddr1, 192,16,1,2); - uip_ipaddr_copy(&ipaddr2, &ipaddr1); - \endcode - * - * \param dest The destination for the copy. - * \param src The source from where to copy. - * - * \hideinitializer - */ -#if !UIP_CONF_IPV6 -#define uip_ipaddr_copy(dest, src) do { \ - ((u16_t *)dest)[0] = ((u16_t *)src)[0]; \ - ((u16_t *)dest)[1] = ((u16_t *)src)[1]; \ - } while(0) -#else /* !UIP_CONF_IPV6 */ -#define uip_ipaddr_copy(dest, src) memcpy(dest, src, sizeof(uip_ip6addr_t)) -#endif /* !UIP_CONF_IPV6 */ - -/** - * Compare two IP addresses - * - * Compares two IP addresses. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2; - - uip_ipaddr(&ipaddr1, 192,16,1,2); - if(uip_ipaddr_cmp(&ipaddr2, &ipaddr1)) { - printf("They are the same"); - } - \endcode - * - * \param addr1 The first IP address. - * \param addr2 The second IP address. - * - * \hideinitializer - */ -#if !UIP_CONF_IPV6 -#define uip_ipaddr_cmp(addr1, addr2) (((u16_t *)addr1)[0] == ((u16_t *)addr2)[0] && \ - ((u16_t *)addr1)[1] == ((u16_t *)addr2)[1]) -#else /* !UIP_CONF_IPV6 */ -#define uip_ipaddr_cmp(addr1, addr2) (memcmp(addr1, addr2, sizeof(uip_ip6addr_t)) == 0) -#endif /* !UIP_CONF_IPV6 */ - -/** - * Compare two IP addresses with netmasks - * - * Compares two IP addresses with netmasks. The masks are used to mask - * out the bits that are to be compared. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2, mask; - - uip_ipaddr(&mask, 255,255,255,0); - uip_ipaddr(&ipaddr1, 192,16,1,2); - uip_ipaddr(&ipaddr2, 192,16,1,3); - if(uip_ipaddr_maskcmp(&ipaddr1, &ipaddr2, &mask)) { - printf("They are the same"); - } - \endcode - * - * \param addr1 The first IP address. - * \param addr2 The second IP address. - * \param mask The netmask. - * - * \hideinitializer - */ -#define uip_ipaddr_maskcmp(addr1, addr2, mask) \ - (((((u16_t *)addr1)[0] & ((u16_t *)mask)[0]) == \ - (((u16_t *)addr2)[0] & ((u16_t *)mask)[0])) && \ - ((((u16_t *)addr1)[1] & ((u16_t *)mask)[1]) == \ - (((u16_t *)addr2)[1] & ((u16_t *)mask)[1]))) - - -/** - * Mask out the network part of an IP address. - * - * Masks out the network part of an IP address, given the address and - * the netmask. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2, netmask; - - uip_ipaddr(&ipaddr1, 192,16,1,2); - uip_ipaddr(&netmask, 255,255,255,0); - uip_ipaddr_mask(&ipaddr2, &ipaddr1, &netmask); - \endcode - * - * In the example above, the variable "ipaddr2" will contain the IP - * address 192.168.1.0. - * - * \param dest Where the result is to be placed. - * \param src The IP address. - * \param mask The netmask. - * - * \hideinitializer - */ -#define uip_ipaddr_mask(dest, src, mask) do { \ - ((u16_t *)dest)[0] = ((u16_t *)src)[0] & ((u16_t *)mask)[0]; \ - ((u16_t *)dest)[1] = ((u16_t *)src)[1] & ((u16_t *)mask)[1]; \ - } while(0) - -/** - * Pick the first octet of an IP address. - * - * Picks out the first octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr1(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 1. - * - * \hideinitializer - */ -#define uip_ipaddr1(addr) (htons(((u16_t *)(addr))[0]) >> 8) - -/** - * Pick the second octet of an IP address. - * - * Picks out the second octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr2(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 2. - * - * \hideinitializer - */ -#define uip_ipaddr2(addr) (htons(((u16_t *)(addr))[0]) & 0xff) - -/** - * Pick the third octet of an IP address. - * - * Picks out the third octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr3(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 3. - * - * \hideinitializer - */ -#define uip_ipaddr3(addr) (htons(((u16_t *)(addr))[1]) >> 8) - -/** - * Pick the fourth octet of an IP address. - * - * Picks out the fourth octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr4(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 4. - * - * \hideinitializer - */ -#define uip_ipaddr4(addr) (htons(((u16_t *)(addr))[1]) & 0xff) - -/** - * Convert 16-bit quantity from host byte order to network byte order. - * - * This macro is primarily used for converting constants from host - * byte order to network byte order. For converting variables to - * network byte order, use the htons() function instead. - * - * \hideinitializer - */ -#ifndef HTONS -# if UIP_BYTE_ORDER == UIP_BIG_ENDIAN -# define HTONS(n) (n) -# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ -# define HTONS(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) -# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ -#else -#error "HTONS already defined!" -#endif /* HTONS */ - -/** - * Convert 16-bit quantity from host byte order to network byte order. - * - * This function is primarily used for converting variables from host - * byte order to network byte order. For converting constants to - * network byte order, use the HTONS() macro instead. - */ -#ifndef htons -u16_t htons(u16_t val); -#endif /* htons */ -#ifndef ntohs -#define ntohs htons -#endif - -/** @} */ - -/** - * Pointer to the application data in the packet buffer. - * - * This pointer points to the application data when the application is - * called. If the application wishes to send data, the application may - * use this space to write the data into before calling uip_send(). - */ -extern void *uip_appdata; - -#if UIP_URGDATA > 0 -/* u8_t *uip_urgdata: - * - * This pointer points to any urgent data that has been received. Only - * present if compiled with support for urgent data (UIP_URGDATA). - */ -extern void *uip_urgdata; -#endif /* UIP_URGDATA > 0 */ - - -/** - * \defgroup uipdrivervars Variables used in uIP device drivers - * @{ - * - * uIP has a few global variables that are used in device drivers for - * uIP. - */ - -/** - * The length of the packet in the uip_buf buffer. - * - * The global variable uip_len holds the length of the packet in the - * uip_buf buffer. - * - * When the network device driver calls the uIP input function, - * uip_len should be set to the length of the packet in the uip_buf - * buffer. - * - * When sending packets, the device driver should use the contents of - * the uip_len variable to determine the length of the outgoing - * packet. - * - */ -extern u16_t uip_len; - -/** @} */ - -#if UIP_URGDATA > 0 -extern u16_t uip_urglen, uip_surglen; -#endif /* UIP_URGDATA > 0 */ - - -/** - * Representation of a uIP TCP connection. - * - * The uip_conn structure is used for identifying a connection. All - * but one field in the structure are to be considered read-only by an - * application. The only exception is the appstate field whos purpose - * is to let the application store application-specific state (e.g., - * file pointers) for the connection. The type of this field is - * configured in the "uipopt.h" header file. - */ -struct uip_conn { - uip_ipaddr_t ripaddr; /**< The IP address of the remote host. */ - - u16_t lport; /**< The local TCP port, in network byte order. */ - u16_t rport; /**< The local remote TCP port, in network byte - order. */ - - u8_t rcv_nxt[4]; /**< The sequence number that we expect to - receive next. */ - u8_t snd_nxt[4]; /**< The sequence number that was last sent by - us. */ - u16_t len; /**< Length of the data that was previously sent. */ - u16_t mss; /**< Current maximum segment size for the - connection. */ - u16_t initialmss; /**< Initial maximum segment size for the - connection. */ - u8_t sa; /**< Retransmission time-out calculation state - variable. */ - u8_t sv; /**< Retransmission time-out calculation state - variable. */ - u8_t rto; /**< Retransmission time-out. */ - u8_t tcpstateflags; /**< TCP state and flags. */ - u8_t timer; /**< The retransmission timer. */ - u8_t nrtx; /**< The number of retransmissions for the last - segment sent. */ - - /** The application state. */ - uip_tcp_appstate_t appstate; -}; - - -/** - * Pointer to the current TCP connection. - * - * The uip_conn pointer can be used to access the current TCP - * connection. - */ -extern struct uip_conn *uip_conn; -/* The array containing all uIP connections. */ -extern struct uip_conn uip_conns[UIP_CONNS]; -/** - * \addtogroup uiparch - * @{ - */ - -/** - * 4-byte array used for the 32-bit sequence number calculations. - */ -extern u8_t uip_acc32[4]; - -/** @} */ - - -#if UIP_UDP -/** - * Representation of a uIP UDP connection. - */ -struct uip_udp_conn { - uip_ipaddr_t ripaddr; /**< The IP address of the remote peer. */ - u16_t lport; /**< The local port number in network byte order. */ - u16_t rport; /**< The remote port number in network byte order. */ - u8_t ttl; /**< Default time-to-live. */ - - /** The application state. */ - uip_udp_appstate_t appstate; -}; - -/** - * The current UDP connection. - */ -extern struct uip_udp_conn *uip_udp_conn; -extern struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; -#endif /* UIP_UDP */ - -/** - * The structure holding the TCP/IP statistics that are gathered if - * UIP_STATISTICS is set to 1. - * - */ -struct uip_stats { - struct { - uip_stats_t drop; /**< Number of dropped packets at the IP - layer. */ - uip_stats_t recv; /**< Number of received packets at the IP - layer. */ - uip_stats_t sent; /**< Number of sent packets at the IP - layer. */ - uip_stats_t vhlerr; /**< Number of packets dropped due to wrong - IP version or header length. */ - uip_stats_t hblenerr; /**< Number of packets dropped due to wrong - IP length, high byte. */ - uip_stats_t lblenerr; /**< Number of packets dropped due to wrong - IP length, low byte. */ - uip_stats_t fragerr; /**< Number of packets dropped since they - were IP fragments. */ - uip_stats_t chkerr; /**< Number of packets dropped due to IP - checksum errors. */ - uip_stats_t protoerr; /**< Number of packets dropped since they - were neither ICMP, UDP nor TCP. */ - } ip; /**< IP statistics. */ - struct { - uip_stats_t drop; /**< Number of dropped ICMP packets. */ - uip_stats_t recv; /**< Number of received ICMP packets. */ - uip_stats_t sent; /**< Number of sent ICMP packets. */ - uip_stats_t typeerr; /**< Number of ICMP packets with a wrong - type. */ - } icmp; /**< ICMP statistics. */ - struct { - uip_stats_t drop; /**< Number of dropped TCP segments. */ - uip_stats_t recv; /**< Number of recived TCP segments. */ - uip_stats_t sent; /**< Number of sent TCP segments. */ - uip_stats_t chkerr; /**< Number of TCP segments with a bad - checksum. */ - uip_stats_t ackerr; /**< Number of TCP segments with a bad ACK - number. */ - uip_stats_t rst; /**< Number of recevied TCP RST (reset) segments. */ - uip_stats_t rexmit; /**< Number of retransmitted TCP segments. */ - uip_stats_t syndrop; /**< Number of dropped SYNs due to too few - connections was avaliable. */ - uip_stats_t synrst; /**< Number of SYNs for closed ports, - triggering a RST. */ - } tcp; /**< TCP statistics. */ -#if UIP_UDP - struct { - uip_stats_t drop; /**< Number of dropped UDP segments. */ - uip_stats_t recv; /**< Number of recived UDP segments. */ - uip_stats_t sent; /**< Number of sent UDP segments. */ - uip_stats_t chkerr; /**< Number of UDP segments with a bad - checksum. */ - } udp; /**< UDP statistics. */ -#endif /* UIP_UDP */ -}; - -/** - * The uIP TCP/IP statistics. - * - * This is the variable in which the uIP TCP/IP statistics are gathered. - */ -extern struct uip_stats uip_stat; - - -/*---------------------------------------------------------------------------*/ -/* All the stuff below this point is internal to uIP and should not be - * used directly by an application or by a device driver. - */ -/*---------------------------------------------------------------------------*/ -/* u8_t uip_flags: - * - * When the application is called, uip_flags will contain the flags - * that are defined in this file. Please read below for more - * infomation. - */ -extern u8_t uip_flags; - -/* The following flags may be set in the global variable uip_flags - before calling the application callback. The UIP_ACKDATA, - UIP_NEWDATA, and UIP_CLOSE flags may both be set at the same time, - whereas the others are mutualy exclusive. Note that these flags - should *NOT* be accessed directly, but only through the uIP - functions/macros. */ - -#define UIP_ACKDATA 1 /* Signifies that the outstanding data was - acked and the application should send - out new data instead of retransmitting - the last data. */ -#define UIP_NEWDATA 2 /* Flags the fact that the peer has sent - us new data. */ -#define UIP_REXMIT 4 /* Tells the application to retransmit the - data that was last sent. */ -#define UIP_POLL 8 /* Used for polling the application, to - check if the application has data that - it wants to send. */ -#define UIP_CLOSE 16 /* The remote host has closed the - connection, thus the connection has - gone away. Or the application signals - that it wants to close the - connection. */ -#define UIP_ABORT 32 /* The remote host has aborted the - connection, thus the connection has - gone away. Or the application signals - that it wants to abort the - connection. */ -#define UIP_CONNECTED 64 /* We have got a connection from a remote - host and have set up a new connection - for it, or an active connection has - been successfully established. */ - -#define UIP_TIMEDOUT 128 /* The connection has been aborted due to - too many retransmissions. */ - -/* uip_process(flag): - * - * The actual uIP function which does all the work. - */ -void uip_process(u8_t flag); - -/* The following flags are passed as an argument to the uip_process() - function. They are used to distinguish between the two cases where - uip_process() is called. It can be called either because we have - incoming data that should be processed, or because the periodic - timer has fired. These values are never used directly, but only in - the macrose defined in this file. */ - -#define UIP_DATA 1 /* Tells uIP that there is incoming - data in the uip_buf buffer. The - length of the data is stored in the - global variable uip_len. */ -#define UIP_TIMER 2 /* Tells uIP that the periodic timer - has fired. */ -#define UIP_POLL_REQUEST 3 /* Tells uIP that a connection should - be polled. */ -#define UIP_UDP_SEND_CONN 4 /* Tells uIP that a UDP datagram - should be constructed in the - uip_buf buffer. */ -#if UIP_UDP -#define UIP_UDP_TIMER 5 -#endif /* UIP_UDP */ - -/* The TCP states used in the uip_conn->tcpstateflags. */ -#define UIP_CLOSED 0 -#define UIP_SYN_RCVD 1 -#define UIP_SYN_SENT 2 -#define UIP_ESTABLISHED 3 -#define UIP_FIN_WAIT_1 4 -#define UIP_FIN_WAIT_2 5 -#define UIP_CLOSING 6 -#define UIP_TIME_WAIT 7 -#define UIP_LAST_ACK 8 -#define UIP_TS_MASK 15 - -#define UIP_STOPPED 16 - -/* The TCP and IP headers. */ -struct uip_tcpip_hdr { -#if UIP_CONF_IPV6 - /* IPv6 header. */ - u8_t vtc, - tcflow; - u16_t flow; - u8_t len[2]; - u8_t proto, ttl; - uip_ip6addr_t srcipaddr, destipaddr; -#else /* UIP_CONF_IPV6 */ - /* IPv4 header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -#endif /* UIP_CONF_IPV6 */ - - /* TCP header. */ - u16_t srcport, - destport; - u8_t seqno[4], - ackno[4], - tcpoffset, - flags, - wnd[2]; - u16_t tcpchksum; - u8_t urgp[2]; - u8_t optdata[4]; -}; - -/* The ICMP and IP headers. */ -struct uip_icmpip_hdr { -#if UIP_CONF_IPV6 - /* IPv6 header. */ - u8_t vtc, - tcf; - u16_t flow; - u8_t len[2]; - u8_t proto, ttl; - uip_ip6addr_t srcipaddr, destipaddr; -#else /* UIP_CONF_IPV6 */ - /* IPv4 header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -#endif /* UIP_CONF_IPV6 */ - - /* ICMP (echo) header. */ - u8_t type, icode; - u16_t icmpchksum; -#if !UIP_CONF_IPV6 - u16_t id, seqno; -#else /* !UIP_CONF_IPV6 */ - u8_t flags, reserved1, reserved2, reserved3; - u8_t icmp6data[16]; - u8_t options[1]; -#endif /* !UIP_CONF_IPV6 */ -}; - - -/* The UDP and IP headers. */ -struct uip_udpip_hdr { -#if UIP_CONF_IPV6 - /* IPv6 header. */ - u8_t vtc, - tcf; - u16_t flow; - u8_t len[2]; - u8_t proto, ttl; - uip_ip6addr_t srcipaddr, destipaddr; -#else /* UIP_CONF_IPV6 */ - /* IP header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -#endif /* UIP_CONF_IPV6 */ - - /* UDP header. */ - u16_t srcport, - destport; - u16_t udplen; - u16_t udpchksum; -}; - - - -/** - * The buffer size available for user data in the \ref uip_buf buffer. - * - * This macro holds the available size for user data in the \ref - * uip_buf buffer. The macro is intended to be used for checking - * bounds of available user data. - * - * Example: - \code - snprintf(uip_appdata, UIP_APPDATA_SIZE, "%u\n", i); - \endcode - * - * \hideinitializer - */ -#define UIP_APPDATA_SIZE (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) - - -#define UIP_PROTO_ICMP 1 -#define UIP_PROTO_TCP 6 -#define UIP_PROTO_UDP 17 -#define UIP_PROTO_ICMP6 58 - -/* Header sizes. */ -#if UIP_CONF_IPV6 -#define UIP_IPH_LEN 40 -#else /* UIP_CONF_IPV6 */ -#define UIP_IPH_LEN 20 /* Size of IP header */ -#endif /* UIP_CONF_IPV6 */ -#define UIP_UDPH_LEN 8 /* Size of UDP header */ -#define UIP_TCPH_LEN 20 /* Size of TCP header */ -#define UIP_IPUDPH_LEN (UIP_UDPH_LEN + UIP_IPH_LEN) /* Size of IP + - UDP - header */ -#define UIP_IPTCPH_LEN (UIP_TCPH_LEN + UIP_IPH_LEN) /* Size of IP + - TCP - header */ -#define UIP_TCPIP_HLEN UIP_IPTCPH_LEN - - -#if UIP_FIXEDADDR -extern const uip_ipaddr_t uip_hostaddr, uip_netmask, uip_draddr; -#else /* UIP_FIXEDADDR */ -extern uip_ipaddr_t uip_hostaddr, uip_netmask, uip_draddr; -#endif /* UIP_FIXEDADDR */ - - - -/** - * Representation of a 48-bit Ethernet address. - */ -struct uip_eth_addr { - u8_t addr[6]; -}; - -/** - * Calculate the Internet checksum over a buffer. - * - * The Internet checksum is the one's complement of the one's - * complement sum of all 16-bit words in the buffer. - * - * See RFC1071. - * - * \param buf A pointer to the buffer over which the checksum is to be - * computed. - * - * \param len The length of the buffer over which the checksum is to - * be computed. - * - * \return The Internet checksum of the buffer. - */ -u16_t uip_chksum(u16_t *buf, u16_t len); - -/** - * Calculate the IP header checksum of the packet header in uip_buf. - * - * The IP header checksum is the Internet checksum of the 20 bytes of - * the IP header. - * - * \return The IP header checksum of the IP header in the uip_buf - * buffer. - */ -u16_t uip_ipchksum(void); - -/** - * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. - * - * The TCP checksum is the Internet checksum of data contents of the - * TCP segment, and a pseudo-header as defined in RFC793. - * - * \return The TCP checksum of the TCP segment in uip_buf and pointed - * to by uip_appdata. - */ -u16_t uip_tcpchksum(void); - -/** - * Calculate the UDP checksum of the packet in uip_buf and uip_appdata. - * - * The UDP checksum is the Internet checksum of data contents of the - * UDP segment, and a pseudo-header as defined in RFC768. - * - * \return The UDP checksum of the UDP segment in uip_buf and pointed - * to by uip_appdata. - */ -u16_t uip_udpchksum(void); - - -#endif /* __UIP_H__ */ - - -/** @} */ diff --git a/libraries/UIPEthernet/utility/uip_arch.h b/libraries/UIPEthernet/utility/uip_arch.h deleted file mode 100644 index 71fd84b..0000000 --- a/libraries/UIPEthernet/utility/uip_arch.h +++ /dev/null @@ -1,138 +0,0 @@ -/** - * \addtogroup uip - * {@ - */ - -/** - * \defgroup uiparch Architecture specific uIP functions - * @{ - * - * The functions in the architecture specific module implement the IP - * check sum and 32-bit additions. - * - * The IP checksum calculation is the most computationally expensive - * operation in the TCP/IP stack and it therefore pays off to - * implement this in efficient assembler. The purpose of the uip-arch - * module is to let the checksum functions to be implemented in - * architecture specific assembler. - * - */ - -/** - * \file - * Declarations of architecture specific functions. - * \author Adam Dunkels - */ - -/* - * Copyright (c) 2001, Adam Dunkels. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip_arch.h,v 1.2 2006/06/07 09:15:19 adam Exp $ - * - */ - -#ifndef __UIP_ARCH_H__ -#define __UIP_ARCH_H__ - -#include "uip.h" - -/** - * Carry out a 32-bit addition. - * - * Because not all architectures for which uIP is intended has native - * 32-bit arithmetic, uIP uses an external C function for doing the - * required 32-bit additions in the TCP protocol processing. This - * function should add the two arguments and place the result in the - * global variable uip_acc32. - * - * \note The 32-bit integer pointed to by the op32 parameter and the - * result in the uip_acc32 variable are in network byte order (big - * endian). - * - * \param op32 A pointer to a 4-byte array representing a 32-bit - * integer in network byte order (big endian). - * - * \param op16 A 16-bit integer in host byte order. - */ -void uip_add32(u8_t *op32, u16_t op16); - -/** - * Calculate the Internet checksum over a buffer. - * - * The Internet checksum is the one's complement of the one's - * complement sum of all 16-bit words in the buffer. - * - * See RFC1071. - * - * \note This function is not called in the current version of uIP, - * but future versions might make use of it. - * - * \param buf A pointer to the buffer over which the checksum is to be - * computed. - * - * \param len The length of the buffer over which the checksum is to - * be computed. - * - * \return The Internet checksum of the buffer. - */ -u16_t uip_chksum(u16_t *buf, u16_t len); - -/** - * Calculate the IP header checksum of the packet header in uip_buf. - * - * The IP header checksum is the Internet checksum of the 20 bytes of - * the IP header. - * - * \return The IP header checksum of the IP header in the uip_buf - * buffer. - */ -u16_t uip_ipchksum(void); - -/** - * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. - * - * The TCP checksum is the Internet checksum of data contents of the - * TCP segment, and a pseudo-header as defined in RFC793. - * - * \note The uip_appdata pointer that points to the packet data may - * point anywhere in memory, so it is not possible to simply calculate - * the Internet checksum of the contents of the uip_buf buffer. - * - * \return The TCP checksum of the TCP segment in uip_buf and pointed - * to by uip_appdata. - */ -u16_t uip_tcpchksum(void); - -u16_t uip_udpchksum(void); - -/** @} */ -/** @} */ - -#endif /* __UIP_ARCH_H__ */ diff --git a/libraries/UIPEthernet/utility/uip_arp.c b/libraries/UIPEthernet/utility/uip_arp.c deleted file mode 100644 index 44ca7c4..0000000 --- a/libraries/UIPEthernet/utility/uip_arp.c +++ /dev/null @@ -1,423 +0,0 @@ -/** - * \addtogroup uip - * @{ - */ - -/** - * \defgroup uiparp uIP Address Resolution Protocol - * @{ - * - * The Address Resolution Protocol ARP is used for mapping between IP - * addresses and link level addresses such as the Ethernet MAC - * addresses. ARP uses broadcast queries to ask for the link level - * address of a known IP address and the host which is configured with - * the IP address for which the query was meant, will respond with its - * link level address. - * - * \note This ARP implementation only supports Ethernet. - */ - -/** - * \file - * Implementation of the ARP Address Resolution Protocol. - * \author Adam Dunkels - * - */ - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip_arp.c,v 1.8 2006/06/02 23:36:21 adam Exp $ - * - */ - - -#include "uip_arp.h" - -#include - -struct arp_hdr { - struct uip_eth_hdr ethhdr; - u16_t hwtype; - u16_t protocol; - u8_t hwlen; - u8_t protolen; - u16_t opcode; - struct uip_eth_addr shwaddr; - u16_t sipaddr[2]; - struct uip_eth_addr dhwaddr; - u16_t dipaddr[2]; -}; - -struct ethip_hdr { - struct uip_eth_hdr ethhdr; - /* IP header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -}; - -#define ARP_REQUEST 1 -#define ARP_REPLY 2 - -#define ARP_HWTYPE_ETH 1 - -struct arp_entry { - u16_t ipaddr[2]; - struct uip_eth_addr ethaddr; - u8_t time; -}; - -static const struct uip_eth_addr broadcast_ethaddr = - {{0xff,0xff,0xff,0xff,0xff,0xff}}; -static const u16_t broadcast_ipaddr[2] = {0xffff,0xffff}; - -static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; -static u16_t ipaddr[2]; -static u8_t i, c; - -static u8_t arptime; -static u8_t tmpage; - -#define BUF ((struct arp_hdr *)&uip_buf[0]) -#define IPBUF ((struct ethip_hdr *)&uip_buf[0]) -/*-----------------------------------------------------------------------------------*/ -/** - * Initialize the ARP module. - * - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_init(void) -{ - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - memset(arp_table[i].ipaddr, 0, 4); - } -} -/*-----------------------------------------------------------------------------------*/ -/** - * Periodic ARP processing function. - * - * This function performs periodic timer processing in the ARP module - * and should be called at regular intervals. The recommended interval - * is 10 seconds between the calls. - * - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_timer(void) -{ - struct arp_entry *tabptr; - - ++arptime; - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 && - arptime - tabptr->time >= UIP_ARP_MAXAGE) { - memset(tabptr->ipaddr, 0, 4); - } - } - -} -/*-----------------------------------------------------------------------------------*/ -static void -uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) -{ - register struct arp_entry *tabptr; - /* Walk through the ARP mapping table and try to find an entry to - update. If none is found, the IP -> MAC address mapping is - inserted in the ARP table. */ - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - - tabptr = &arp_table[i]; - /* Only check those entries that are actually in use. */ - if(tabptr->ipaddr[0] != 0 && - tabptr->ipaddr[1] != 0) { - - /* Check if the source IP address of the incoming packet matches - the IP address in this ARP table entry. */ - if(ipaddr[0] == tabptr->ipaddr[0] && - ipaddr[1] == tabptr->ipaddr[1]) { - - /* An old entry found, update this and return. */ - memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); - tabptr->time = arptime; - - return; - } - } - } - - /* If we get here, no existing ARP table entry was found, so we - create one. */ - - /* First, we try to find an unused entry in the ARP table. */ - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if(tabptr->ipaddr[0] == 0 && - tabptr->ipaddr[1] == 0) { - break; - } - } - - /* If no unused entry is found, we try to find the oldest entry and - throw it away. */ - if(i == UIP_ARPTAB_SIZE) { - tmpage = 0; - c = 0; - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if(arptime - tabptr->time > tmpage) { - tmpage = arptime - tabptr->time; - c = i; - } - } - i = c; - tabptr = &arp_table[i]; - } - - /* Now, i is the ARP table entry which we will fill with the new - information. */ - memcpy(tabptr->ipaddr, ipaddr, 4); - memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); - tabptr->time = arptime; -} -/*-----------------------------------------------------------------------------------*/ -/** - * ARP processing for incoming IP packets - * - * This function should be called by the device driver when an IP - * packet has been received. The function will check if the address is - * in the ARP cache, and if so the ARP cache entry will be - * refreshed. If no ARP cache entry was found, a new one is created. - * - * This function expects an IP packet with a prepended Ethernet header - * in the uip_buf[] buffer, and the length of the packet in the global - * variable uip_len. - */ -/*-----------------------------------------------------------------------------------*/ -//#if 0 -void -uip_arp_ipin(void) -{ - uip_len -= sizeof(struct uip_eth_hdr); - - /* Only insert/update an entry if the source IP address of the - incoming IP packet comes from a host on the local network. */ - if((IPBUF->srcipaddr[0] & uip_netmask[0]) != - (uip_hostaddr[0] & uip_netmask[0])) { - return; - } - if((IPBUF->srcipaddr[1] & uip_netmask[1]) != - (uip_hostaddr[1] & uip_netmask[1])) { - return; - } - uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src)); - - return; -} -//#endif /* 0 */ -/*-----------------------------------------------------------------------------------*/ -/** - * ARP processing for incoming ARP packets. - * - * This function should be called by the device driver when an ARP - * packet has been received. The function will act differently - * depending on the ARP packet type: if it is a reply for a request - * that we previously sent out, the ARP cache will be filled in with - * the values from the ARP reply. If the incoming ARP packet is an ARP - * request for our IP address, an ARP reply packet is created and put - * into the uip_buf[] buffer. - * - * When the function returns, the value of the global variable uip_len - * indicates whether the device driver should send out a packet or - * not. If uip_len is zero, no packet should be sent. If uip_len is - * non-zero, it contains the length of the outbound packet that is - * present in the uip_buf[] buffer. - * - * This function expects an ARP packet with a prepended Ethernet - * header in the uip_buf[] buffer, and the length of the packet in the - * global variable uip_len. - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_arpin(void) -{ - - if(uip_len < sizeof(struct arp_hdr)) { - uip_len = 0; - return; - } - uip_len = 0; - - switch(BUF->opcode) { - case HTONS(ARP_REQUEST): - /* ARP request. If it asked for our address, we send out a - reply. */ - if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) { - /* First, we register the one who made the request in our ARP - table, since it is likely that we will do more communication - with this host in the future. */ - uip_arp_update(BUF->sipaddr, &BUF->shwaddr); - - /* The reply opcode is 2. */ - BUF->opcode = HTONS(2); - - memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6); - memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); - memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); - memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6); - - BUF->dipaddr[0] = BUF->sipaddr[0]; - BUF->dipaddr[1] = BUF->sipaddr[1]; - BUF->sipaddr[0] = uip_hostaddr[0]; - BUF->sipaddr[1] = uip_hostaddr[1]; - - BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); - uip_len = sizeof(struct arp_hdr); - } - break; - case HTONS(ARP_REPLY): - /* ARP reply. We insert or update the ARP table if it was meant - for us. */ - if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) { - uip_arp_update(BUF->sipaddr, &BUF->shwaddr); - } - break; - } - - return; -} -/*-----------------------------------------------------------------------------------*/ -/** - * Prepend Ethernet header to an outbound IP packet and see if we need - * to send out an ARP request. - * - * This function should be called before sending out an IP packet. The - * function checks the destination IP address of the IP packet to see - * what Ethernet MAC address that should be used as a destination MAC - * address on the Ethernet. - * - * If the destination IP address is in the local network (determined - * by logical ANDing of netmask and our IP address), the function - * checks the ARP cache to see if an entry for the destination IP - * address is found. If so, an Ethernet header is prepended and the - * function returns. If no ARP cache entry is found for the - * destination IP address, the packet in the uip_buf[] is replaced by - * an ARP request packet for the IP address. The IP packet is dropped - * and it is assumed that they higher level protocols (e.g., TCP) - * eventually will retransmit the dropped packet. - * - * If the destination IP address is not on the local network, the IP - * address of the default router is used instead. - * - * When the function returns, a packet is present in the uip_buf[] - * buffer, and the length of the packet is in the global variable - * uip_len. - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_out(void) -{ - struct arp_entry *tabptr; - - /* Find the destination IP address in the ARP table and construct - the Ethernet header. If the destination IP addres isn't on the - local network, we use the default router's IP address instead. - - If not ARP table entry is found, we overwrite the original IP - packet with an ARP request for the IP address. */ - - /* First check if destination is a local broadcast. */ - if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr)) { - memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6); - } else { - /* Check if the destination address is on the local network. */ - if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask)) { - /* Destination address was not on the local network, so we need to - use the default router's IP address instead of the destination - address when determining the MAC address. */ - uip_ipaddr_copy(ipaddr, uip_draddr); - } else { - /* Else, we use the destination IP address. */ - uip_ipaddr_copy(ipaddr, IPBUF->destipaddr); - } - - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr)) { - break; - } - } - - if(i == UIP_ARPTAB_SIZE) { - /* The destination address was not in our ARP table, so we - overwrite the IP packet with an ARP request. */ - - memset(BUF->ethhdr.dest.addr, 0xff, 6); - memset(BUF->dhwaddr.addr, 0x00, 6); - memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); - memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); - - uip_ipaddr_copy(BUF->dipaddr, ipaddr); - uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr); - BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */ - BUF->hwtype = HTONS(ARP_HWTYPE_ETH); - BUF->protocol = HTONS(UIP_ETHTYPE_IP); - BUF->hwlen = 6; - BUF->protolen = 4; - BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); - - uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN]; - - uip_len = sizeof(struct arp_hdr); - return; - } - - /* Build an ethernet header. */ - memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6); - } - memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6); - - IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP); - - uip_len += sizeof(struct uip_eth_hdr); -} -/*-----------------------------------------------------------------------------------*/ - -/** @} */ -/** @} */ diff --git a/libraries/UIPEthernet/utility/uip_arp.h b/libraries/UIPEthernet/utility/uip_arp.h deleted file mode 100644 index ef93054..0000000 --- a/libraries/UIPEthernet/utility/uip_arp.h +++ /dev/null @@ -1,144 +0,0 @@ -/** - * \addtogroup uip - * @{ - */ - -/** - * \addtogroup uiparp - * @{ - */ - -/** - * \file - * Macros and definitions for the ARP module. - * \author Adam Dunkels - */ - - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip_arp.h,v 1.5 2006/06/11 21:46:39 adam Exp $ - * - */ - -#ifndef __UIP_ARP_H__ -#define __UIP_ARP_H__ - -#include "uip.h" - - -extern struct uip_eth_addr uip_ethaddr; - -/** - * The Ethernet header. - */ -struct uip_eth_hdr { - struct uip_eth_addr dest; - struct uip_eth_addr src; - u16_t type; -}; - -#define UIP_ETHTYPE_ARP 0x0806 -#define UIP_ETHTYPE_IP 0x0800 -#define UIP_ETHTYPE_IP6 0x86dd - - -/* The uip_arp_init() function must be called before any of the other - ARP functions. */ -void uip_arp_init(void); - -/* The uip_arp_ipin() function should be called whenever an IP packet - arrives from the Ethernet. This function refreshes the ARP table or - inserts a new mapping if none exists. The function assumes that an - IP packet with an Ethernet header is present in the uip_buf buffer - and that the length of the packet is in the uip_len variable. */ -void uip_arp_ipin(void); -//#define uip_arp_ipin() - -/* The uip_arp_arpin() should be called when an ARP packet is received - by the Ethernet driver. This function also assumes that the - Ethernet frame is present in the uip_buf buffer. When the - uip_arp_arpin() function returns, the contents of the uip_buf - buffer should be sent out on the Ethernet if the uip_len variable - is > 0. */ -void uip_arp_arpin(void); - -/* The uip_arp_out() function should be called when an IP packet - should be sent out on the Ethernet. This function creates an - Ethernet header before the IP header in the uip_buf buffer. The - Ethernet header will have the correct Ethernet MAC destination - address filled in if an ARP table entry for the destination IP - address (or the IP address of the default router) is present. If no - such table entry is found, the IP packet is overwritten with an ARP - request and we rely on TCP to retransmit the packet that was - overwritten. In any case, the uip_len variable holds the length of - the Ethernet frame that should be transmitted. */ -void uip_arp_out(void); - -/* The uip_arp_timer() function should be called every ten seconds. It - is responsible for flushing old entries in the ARP table. */ -void uip_arp_timer(void); - -/** @} */ - -/** - * \addtogroup uipconffunc - * @{ - */ - - -/** - * Specifiy the Ethernet MAC address. - * - * The ARP code needs to know the MAC address of the Ethernet card in - * order to be able to respond to ARP queries and to generate working - * Ethernet headers. - * - * \note This macro only specifies the Ethernet MAC address to the ARP - * code. It cannot be used to change the MAC address of the Ethernet - * card. - * - * \param eaddr A pointer to a struct uip_eth_addr containing the - * Ethernet MAC address of the Ethernet card. - * - * \hideinitializer - */ -#define uip_setethaddr(eaddr) do {uip_ethaddr.addr[0] = eaddr.addr[0]; \ - uip_ethaddr.addr[1] = eaddr.addr[1];\ - uip_ethaddr.addr[2] = eaddr.addr[2];\ - uip_ethaddr.addr[3] = eaddr.addr[3];\ - uip_ethaddr.addr[4] = eaddr.addr[4];\ - uip_ethaddr.addr[5] = eaddr.addr[5];} while(0) - -/** @} */ -/** @} */ - -#endif /* __UIP_ARP_H__ */ diff --git a/libraries/UIPEthernet/utility/uip_clock.h b/libraries/UIPEthernet/utility/uip_clock.h deleted file mode 100644 index 6dd72be..0000000 --- a/libraries/UIPEthernet/utility/uip_clock.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * \defgroup clock Clock interface - * - * The clock interface is the interface between the \ref timer "timer library" - * and the platform specific clock functionality. The clock - * interface must be implemented for each platform that uses the \ref - * timer "timer library". - * - * The clock interface does only one this: it measures time. The clock - * interface provides a macro, CLOCK_SECOND, which corresponds to one - * second of system time. - * - * \sa \ref timer "Timer library" - * - * @{ - */ - -/* - * Copyright (c) 2004, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack - * - * Author: Adam Dunkels - * - * $Id: clock.h,v 1.3 2006/06/11 21:46:39 adam Exp $ - */ -#ifndef __UIP_CLOCK_H__ -#define __UIP_CLOCK_H__ - -#include "clock-arch.h" - -/** - * Initialize the clock library. - * - * This function initializes the clock library and should be called - * from the main() function of the system. - * - */ -void uip_clock_init(void); - -/** - * Get the current clock time. - * - * This function returns the current system clock time. - * - * \return The current clock time, measured in system ticks. - */ -clock_time_t clock_time(void); - -/** - * A second, measured in system clock time. - * - * \hideinitializer - */ -#ifdef CLOCK_CONF_SECOND -#define CLOCK_SECOND CLOCK_CONF_SECOND -#else -#define CLOCK_SECOND (clock_time_t)32 -#endif - -#endif /* __CLOCK_H__ */ - -/** @} */ diff --git a/libraries/UIPEthernet/utility/uip_debug.cpp b/libraries/UIPEthernet/utility/uip_debug.cpp deleted file mode 100644 index 444bd0e..0000000 --- a/libraries/UIPEthernet/utility/uip_debug.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include -#include -#include -extern "C" { - #import "utility/uip.h" -} - -struct uip_conn con[UIP_CONNS]; - -void -UIPDebug::uip_debug_printconns() -{ - for(uint8_t i=0;iripaddr,rhs->ripaddr)) - { - Serial.print(" ripaddr: "); - uip_debug_printbytes((const uint8_t *)lhs->ripaddr,4); - Serial.print(" -> "); - uip_debug_printbytes((const uint8_t *)rhs->ripaddr,4); - Serial.println(); - uip_ipaddr_copy(lhs->ripaddr,rhs->ripaddr); - changed = true; - } - if (lhs->lport != rhs->lport) - { - Serial.print(" lport: "); - Serial.print(htons(lhs->lport)); - Serial.print(" -> "); - Serial.println(htons(rhs->lport)); - lhs->lport = rhs->lport; - changed = true; - } - if (lhs->rport != rhs->rport) - { - Serial.print(" rport: "); - Serial.print(htons(lhs->rport)); - Serial.print(" -> "); - Serial.println(htons(rhs->rport)); - lhs->rport = rhs->rport; - changed = true; - } - if ((uint32_t)lhs->rcv_nxt[0] != (uint32_t)rhs->rcv_nxt[0]) - { - Serial.print(" rcv_nxt: "); - uip_debug_printbytes(lhs->rcv_nxt,4); - Serial.print(" -> "); - uip_debug_printbytes(rhs->rcv_nxt,4); - *((uint32_t *)&lhs->rcv_nxt[0]) = (uint32_t)rhs->rcv_nxt[0]; - Serial.println(); - changed = true; - } - if ((uint32_t)lhs->snd_nxt[0] != (uint32_t)rhs->snd_nxt[0]) - { - Serial.print(" snd_nxt: "); - uip_debug_printbytes(lhs->snd_nxt,4); - Serial.print(" -> "); - uip_debug_printbytes(rhs->snd_nxt,4); - *((uint32_t *)&lhs->snd_nxt[0]) = (uint32_t)rhs->snd_nxt[0]; - Serial.println(); - changed = true; - } - if (lhs->len != rhs->len) - { - Serial.print(" len: "); - Serial.print(lhs->len); - Serial.print(" -> "); - Serial.println(rhs->len); - lhs->len = rhs->len; - changed = true; - } - if (lhs->mss != rhs->mss) - { - Serial.print(" mss: "); - Serial.print(lhs->mss); - Serial.print(" -> "); - Serial.println(rhs->mss); - lhs->mss = rhs->mss; - changed = true; - } - if (lhs->initialmss != rhs->initialmss) - { - Serial.print(" initialmss: "); - Serial.print(lhs->initialmss); - Serial.print(" -> "); - Serial.println(rhs->initialmss); - lhs->initialmss = rhs->initialmss; - changed = true; - } - if (lhs->sa != rhs->sa) - { - Serial.print(" sa: "); - Serial.print(lhs->sa); - Serial.print(" -> "); - Serial.println(rhs->sa); - lhs->sa = rhs->sa; - changed = true; - } - if (lhs->sv != rhs->sv) - { - Serial.print(" sv: "); - Serial.print(lhs->sv); - Serial.print(" -> "); - Serial.println(rhs->sv); - lhs->sv = rhs->sv; - changed = true; - } - if (lhs->rto != rhs->rto) - { - Serial.print(" rto: "); - Serial.print(lhs->rto); - Serial.print(" -> "); - Serial.println(rhs->rto); - lhs->rto = rhs->rto; - changed = true; - } - if (lhs->tcpstateflags != rhs->tcpstateflags) - { - Serial.print(" tcpstateflags: "); - Serial.print(lhs->tcpstateflags); - Serial.print(" -> "); - Serial.println(rhs->tcpstateflags); - lhs->tcpstateflags = rhs->tcpstateflags; - changed = true; - } - if (lhs->timer != rhs->timer) - { - Serial.print(" timer: "); - Serial.print(lhs->timer); - Serial.print(" -> "); - Serial.println(rhs->timer); - lhs->timer = rhs->timer; - changed = true; - } - if (lhs->nrtx != rhs->nrtx) - { - Serial.print(" nrtx: "); - Serial.print(lhs->nrtx); - Serial.print(" -> "); - Serial.println(rhs->nrtx); - lhs->nrtx = rhs->nrtx; - changed = true; - } - return changed; -} - -void -UIPDebug::uip_debug_printbytes(const uint8_t *data, uint8_t len) -{ - for(uint8_t i=0;i - */ - -/* - * Copyright (c) 2004, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack - * - * Author: Adam Dunkels - * - * $Id: timer.c,v 1.2 2006/06/12 08:00:30 adam Exp $ - */ - -#include "uip_clock.h" -#include "uip_timer.h" - -/*---------------------------------------------------------------------------*/ -/** - * Set a timer. - * - * This function is used to set a timer for a time sometime in the - * future. The function timer_expired() will evaluate to true after - * the timer has expired. - * - * \param t A pointer to the timer - * \param interval The interval before the timer expires. - * - */ -void -uip_timer_set(struct uip_timer *t, clock_time_t interval) -{ - t->interval = interval; - t->start = clock_time(); -} -/*---------------------------------------------------------------------------*/ -/** - * Reset the timer with the same interval. - * - * This function resets the timer with the same interval that was - * given to the timer_set() function. The start point of the interval - * is the exact time that the timer last expired. Therefore, this - * function will cause the timer to be stable over time, unlike the - * timer_rester() function. - * - * \param t A pointer to the timer. - * - * \sa timer_restart() - */ -void -uip_timer_reset(struct uip_timer *t) -{ - t->start += t->interval; -} -/*---------------------------------------------------------------------------*/ -/** - * Restart the timer from the current point in time - * - * This function restarts a timer with the same interval that was - * given to the timer_set() function. The timer will start at the - * current time. - * - * \note A periodic timer will drift if this function is used to reset - * it. For preioric timers, use the timer_reset() function instead. - * - * \param t A pointer to the timer. - * - * \sa timer_reset() - */ -void -uip_timer_restart(struct uip_timer *t) -{ - t->start = clock_time(); -} -/*---------------------------------------------------------------------------*/ -/** - * Check if a timer has expired. - * - * This function tests if a timer has expired and returns true or - * false depending on its status. - * - * \param t A pointer to the timer - * - * \return Non-zero if the timer has expired, zero otherwise. - * - */ -int -uip_timer_expired(struct uip_timer *t) -{ - return (clock_time_t)(clock_time() - t->start) >= (clock_time_t)t->interval; -} -/*---------------------------------------------------------------------------*/ - -/** @} */ diff --git a/libraries/UIPEthernet/utility/uip_timer.h b/libraries/UIPEthernet/utility/uip_timer.h deleted file mode 100644 index f260349..0000000 --- a/libraries/UIPEthernet/utility/uip_timer.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * \defgroup timer Timer library - * - * The timer library provides functions for setting, resetting and - * restarting timers, and for checking if a timer has expired. An - * application must "manually" check if its timers have expired; this - * is not done automatically. - * - * A timer is declared as a \c struct \c timer and all access to the - * timer is made by a pointer to the declared timer. - * - * \note The timer library uses the \ref clock "Clock library" to - * measure time. Intervals should be specified in the format used by - * the clock library. - * - * @{ - */ - - -/** - * \file - * Timer library header file. - * \author - * Adam Dunkels - */ - -/* - * Copyright (c) 2004, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack - * - * Author: Adam Dunkels - * - * $Id: timer.h,v 1.3 2006/06/11 21:46:39 adam Exp $ - */ -#ifndef __UIP_TIMER_H__ -#define __UIP_TIMER_H__ - -#include "uip_clock.h" - -/** - * A timer. - * - * This structure is used for declaring a timer. The timer must be set - * with timer_set() before it can be used. - * - * \hideinitializer - */ -struct uip_timer { - clock_time_t start; - clock_time_t interval; -}; - -void uip_timer_set(struct uip_timer *t, clock_time_t interval); -void uip_timer_reset(struct uip_timer *t); -void uip_timer_restart(struct uip_timer *t); -int uip_timer_expired(struct uip_timer *t); - -#endif /* __UIP_TIMER_H__ */ - -/** @} */ diff --git a/libraries/UIPEthernet/utility/uipethernet-conf.h b/libraries/UIPEthernet/utility/uipethernet-conf.h deleted file mode 100644 index 733e26e..0000000 --- a/libraries/UIPEthernet/utility/uipethernet-conf.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef UIPETHERNET_CONF_H -#define UIPETHERNET_CONF_H - -/* for TCP */ -#define UIP_SOCKET_NUMPACKETS 5 -#define UIP_CONF_MAX_CONNECTIONS 4 - -/* for UDP */ -#define UIP_CONF_UDP 0//1 -#define UIP_CONF_BROADCAST 0//1 -#define UIP_CONF_UDP_CONNS 0//4 -#define UIP_UDP_NUMPACKETS 0//5 - -/* number of attempts on write before returning number of bytes sent so far - * set to -1 to block until connection is closed by timeout */ -#define UIP_ATTEMPTS_ON_WRITE -1 - -#endif diff --git a/libraries/UIPEthernet/utility/uipopt.h b/libraries/UIPEthernet/utility/uipopt.h deleted file mode 100644 index 42badf8..0000000 --- a/libraries/UIPEthernet/utility/uipopt.h +++ /dev/null @@ -1,543 +0,0 @@ -/** - * \defgroup uipopt Configuration options for uIP - * @{ - * - * uIP is configured using the per-project configuration file - * uipopt.h. This file contains all compile-time options for uIP and - * should be tweaked to match each specific project. The uIP - * distribution contains a documented example "uipopt.h" that can be - * copied and modified for each project. - * - * \note Most of the configuration options in the uipopt.h should not - * be changed, but rather the per-project uip-conf.h file. - */ - -/** - * \file - * Configuration options for uIP. - * \author Adam Dunkels - * - * This file is used for tweaking various configuration options for - * uIP. You should make a copy of this file into one of your project's - * directories instead of editing this example "uipopt.h" file that - * comes with the uIP distribution. - */ - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uipopt.h,v 1.4 2006/06/12 08:00:31 adam Exp $ - * - */ - -#ifndef __UIPOPT_H__ -#define __UIPOPT_H__ - -#ifndef UIP_LITTLE_ENDIAN -#define UIP_LITTLE_ENDIAN 3412 -#endif /* UIP_LITTLE_ENDIAN */ -#ifndef UIP_BIG_ENDIAN -#define UIP_BIG_ENDIAN 1234 -#endif /* UIP_BIG_ENDIAN */ - -#include "uip-conf.h" - -/*------------------------------------------------------------------------------*/ - -/** - * \name Static configuration options - * @{ - * - * These configuration options can be used for setting the IP address - * settings statically, but only if UIP_FIXEDADDR is set to 1. The - * configuration options for a specific node includes IP address, - * netmask and default router as well as the Ethernet address. The - * netmask, default router and Ethernet address are appliciable only - * if uIP should be run over Ethernet. - * - * All of these should be changed to suit your project. -*/ - -/** - * Determines if uIP should use a fixed IP address or not. - * - * If uIP should use a fixed IP address, the settings are set in the - * uipopt.h file. If not, the macros uip_sethostaddr(), - * uip_setdraddr() and uip_setnetmask() should be used instead. - * - * \hideinitializer - */ -#define UIP_FIXEDADDR 0 - -/** - * Ping IP address asignment. - * - * uIP uses a "ping" packets for setting its own IP address if this - * option is set. If so, uIP will start with an empty IP address and - * the destination IP address of the first incoming "ping" (ICMP echo) - * packet will be used for setting the hosts IP address. - * - * \note This works only if UIP_FIXEDADDR is 0. - * - * \hideinitializer - */ -#ifdef UIP_CONF_PINGADDRCONF -#define UIP_PINGADDRCONF UIP_CONF_PINGADDRCONF -#else /* UIP_CONF_PINGADDRCONF */ -#define UIP_PINGADDRCONF 0 -#endif /* UIP_CONF_PINGADDRCONF */ - - -/** - * Specifies if the uIP ARP module should be compiled with a fixed - * Ethernet MAC address or not. - * - * If this configuration option is 0, the macro uip_setethaddr() can - * be used to specify the Ethernet address at run-time. - * - * \hideinitializer - */ -#define UIP_FIXEDETHADDR 0 - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name IP configuration options - * @{ - * - */ -/** - * The IP TTL (time to live) of IP packets sent by uIP. - * - * This should normally not be changed. - */ -#define UIP_TTL 64 - -/** - * Turn on support for IP packet reassembly. - * - * uIP supports reassembly of fragmented IP packets. This features - * requires an additonal amount of RAM to hold the reassembly buffer - * and the reassembly code size is approximately 700 bytes. The - * reassembly buffer is of the same size as the uip_buf buffer - * (configured by UIP_BUFSIZE). - * - * \note IP packet reassembly is not heavily tested. - * - * \hideinitializer - */ -#define UIP_REASSEMBLY 0 - -/** - * The maximum time an IP fragment should wait in the reassembly - * buffer before it is dropped. - * - */ -#define UIP_REASS_MAXAGE 40 - -/** @} */ - -/*------------------------------------------------------------------------------*/ -/** - * \name UDP configuration options - * @{ - */ - -/** - * Toggles wether UDP support should be compiled in or not. - * - * \hideinitializer - */ -#ifdef UIP_CONF_UDP -#define UIP_UDP UIP_CONF_UDP -#else /* UIP_CONF_UDP */ -#define UIP_UDP 0 -#endif /* UIP_CONF_UDP */ - -/** - * Toggles if UDP checksums should be used or not. - * - * \note Support for UDP checksums is currently not included in uIP, - * so this option has no function. - * - * \hideinitializer - */ -#ifdef UIP_CONF_UDP_CHECKSUMS -#define UIP_UDP_CHECKSUMS UIP_CONF_UDP_CHECKSUMS -#else -#define UIP_UDP_CHECKSUMS 0 -#endif - -/** - * The maximum amount of concurrent UDP connections. - * - * \hideinitializer - */ -#ifdef UIP_CONF_UDP_CONNS -#define UIP_UDP_CONNS UIP_CONF_UDP_CONNS -#else /* UIP_CONF_UDP_CONNS */ -#define UIP_UDP_CONNS 10 -#endif /* UIP_CONF_UDP_CONNS */ - -/** - * The name of the function that should be called when UDP datagrams arrive. - * - * \hideinitializer - */ - - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name TCP configuration options - * @{ - */ - -/** - * Determines if support for opening connections from uIP should be - * compiled in. - * - * If the applications that are running on top of uIP for this project - * do not need to open outgoing TCP connections, this configration - * option can be turned off to reduce the code size of uIP. - * - * \hideinitializer - */ -#define UIP_ACTIVE_OPEN 1 - -/** - * The maximum number of simultaneously open TCP connections. - * - * Since the TCP connections are statically allocated, turning this - * configuration knob down results in less RAM used. Each TCP - * connection requires approximatly 30 bytes of memory. - * - * \hideinitializer - */ -#ifndef UIP_CONF_MAX_CONNECTIONS -#define UIP_CONNS 10 -#else /* UIP_CONF_MAX_CONNECTIONS */ -#define UIP_CONNS UIP_CONF_MAX_CONNECTIONS -#endif /* UIP_CONF_MAX_CONNECTIONS */ - - -/** - * The maximum number of simultaneously listening TCP ports. - * - * Each listening TCP port requires 2 bytes of memory. - * - * \hideinitializer - */ -#ifndef UIP_CONF_MAX_LISTENPORTS -#define UIP_LISTENPORTS 20 -#else /* UIP_CONF_MAX_LISTENPORTS */ -#define UIP_LISTENPORTS UIP_CONF_MAX_LISTENPORTS -#endif /* UIP_CONF_MAX_LISTENPORTS */ - -/** - * Determines if support for TCP urgent data notification should be - * compiled in. - * - * Urgent data (out-of-band data) is a rarely used TCP feature that - * very seldom would be required. - * - * \hideinitializer - */ -#define UIP_URGDATA 0 - -/** - * The initial retransmission timeout counted in timer pulses. - * - * This should not be changed. - */ -#define UIP_RTO 3 - -/** - * The maximum number of times a segment should be retransmitted - * before the connection should be aborted. - * - * This should not be changed. - */ -#define UIP_MAXRTX 8 - -/** - * The maximum number of times a SYN segment should be retransmitted - * before a connection request should be deemed to have been - * unsuccessful. - * - * This should not need to be changed. - */ -#define UIP_MAXSYNRTX 5 - -/** - * The TCP maximum segment size. - * - * This is should not be to set to more than - * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. - */ -#ifndef UIP_CONF_TCP_MSS -#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) -#else -#define UIP_TCP_MSS UIP_CONF_TCP_MSS -#endif - -/** - * The size of the advertised receiver's window. - * - * Should be set low (i.e., to the size of the uip_buf buffer) is the - * application is slow to process incoming data, or high (32768 bytes) - * if the application processes data quickly. - * - * \hideinitializer - */ -#ifndef UIP_CONF_RECEIVE_WINDOW -#define UIP_RECEIVE_WINDOW UIP_TCP_MSS -#else -#define UIP_RECEIVE_WINDOW UIP_CONF_RECEIVE_WINDOW -#endif - -/** - * How long a connection should stay in the TIME_WAIT state. - * - * This configiration option has no real implication, and it should be - * left untouched. - */ -#define UIP_TIME_WAIT_TIMEOUT 120 - - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name ARP configuration options - * @{ - */ - -/** - * The size of the ARP table. - * - * This option should be set to a larger value if this uIP node will - * have many connections from the local network. - * - * \hideinitializer - */ -#ifdef UIP_CONF_ARPTAB_SIZE -#define UIP_ARPTAB_SIZE UIP_CONF_ARPTAB_SIZE -#else -#define UIP_ARPTAB_SIZE 8 -#endif - -/** - * The maxium age of ARP table entries measured in 10ths of seconds. - * - * An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD - * default). - */ -#define UIP_ARP_MAXAGE 120 - -/** @} */ - -/*------------------------------------------------------------------------------*/ - -/** - * \name General configuration options - * @{ - */ - -/** - * The size of the uIP packet buffer. - * - * The uIP packet buffer should not be smaller than 60 bytes, and does - * not need to be larger than 1500 bytes. Lower size results in lower - * TCP throughput, larger size results in higher TCP throughput. - * - * \hideinitializer - */ -#ifndef UIP_CONF_BUFFER_SIZE -#define UIP_BUFSIZE 400 -#else /* UIP_CONF_BUFFER_SIZE */ -#define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE -#endif /* UIP_CONF_BUFFER_SIZE */ - - -/** - * Determines if statistics support should be compiled in. - * - * The statistics is useful for debugging and to show the user. - * - * \hideinitializer - */ -#ifndef UIP_CONF_STATISTICS -#define UIP_STATISTICS 0 -#else /* UIP_CONF_STATISTICS */ -#define UIP_STATISTICS UIP_CONF_STATISTICS -#endif /* UIP_CONF_STATISTICS */ - -/** - * Determines if logging of certain events should be compiled in. - * - * This is useful mostly for debugging. The function uip_log() - * must be implemented to suit the architecture of the project, if - * logging is turned on. - * - * \hideinitializer - */ -#ifndef UIP_CONF_LOGGING -#define UIP_LOGGING 0 -#else /* UIP_CONF_LOGGING */ -#define UIP_LOGGING UIP_CONF_LOGGING -#endif /* UIP_CONF_LOGGING */ - -/** - * Broadcast support. - * - * This flag configures IP broadcast support. This is useful only - * together with UDP. - * - * \hideinitializer - * - */ -#if UIP_UDP && UIP_CONF_BROADCAST -#define UIP_BROADCAST UIP_CONF_BROADCAST -#else /* UIP_CONF_BROADCAST */ -#define UIP_BROADCAST 0 -#endif /* UIP_CONF_BROADCAST */ - -/** - * Print out a uIP log message. - * - * This function must be implemented by the module that uses uIP, and - * is called by uIP whenever a log message is generated. - */ -void uip_log(char *msg); - -/** - * The link level header length. - * - * This is the offset into the uip_buf where the IP header can be - * found. For Ethernet, this should be set to 14. For SLIP, this - * should be set to 0. - * - * \hideinitializer - */ -#ifdef UIP_CONF_LLH_LEN -#define UIP_LLH_LEN UIP_CONF_LLH_LEN -#else /* UIP_CONF_LLH_LEN */ -#define UIP_LLH_LEN 14 -#endif /* UIP_CONF_LLH_LEN */ - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name CPU architecture configuration - * @{ - * - * The CPU architecture configuration is where the endianess of the - * CPU on which uIP is to be run is specified. Most CPUs today are - * little endian, and the most notable exception are the Motorolas - * which are big endian. The BYTE_ORDER macro should be changed to - * reflect the CPU architecture on which uIP is to be run. - */ - -/** - * The byte order of the CPU architecture on which uIP is to be run. - * - * This option can be either BIG_ENDIAN (Motorola byte order) or - * LITTLE_ENDIAN (Intel byte order). - * - * \hideinitializer - */ -#ifdef UIP_CONF_BYTE_ORDER -#define UIP_BYTE_ORDER UIP_CONF_BYTE_ORDER -#else /* UIP_CONF_BYTE_ORDER */ -#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN -#endif /* UIP_CONF_BYTE_ORDER */ - -/** @} */ -/*------------------------------------------------------------------------------*/ - -/** - * \name Appication specific configurations - * @{ - * - * An uIP application is implemented using a single application - * function that is called by uIP whenever a TCP/IP event occurs. The - * name of this function must be registered with uIP at compile time - * using the UIP_APPCALL definition. - * - * uIP applications can store the application state within the - * uip_conn structure by specifying the type of the application - * structure by typedef:ing the type uip_tcp_appstate_t and uip_udp_appstate_t. - * - * The file containing the definitions must be included in the - * uipopt.h file. - * - * The following example illustrates how this can look. - \code - -void httpd_appcall(void); -#define UIP_APPCALL httpd_appcall - -struct httpd_state { - u8_t state; - u16_t count; - char *dataptr; - char *script; -}; -typedef struct httpd_state uip_tcp_appstate_t - \endcode - */ - -/** - * \var #define UIP_APPCALL - * - * The name of the application function that uIP should call in - * response to TCP/IP events. - * - */ - -/** - * \var typedef uip_tcp_appstate_t - * - * The type of the application state that is to be stored in the - * uip_conn structure. This usually is typedef:ed to a struct holding - * application state information. - */ - -/** - * \var typedef uip_udp_appstate_t - * - * The type of the application state that is to be stored in the - * uip_conn structure. This usually is typedef:ed to a struct holding - * application state information. - */ -/** @} */ -/** @} */ - -#endif /* __UIPOPT_H__ */ diff --git a/libraries/UIPEthernet/utility/util.h b/libraries/UIPEthernet/utility/util.h deleted file mode 100644 index 5042e82..0000000 --- a/libraries/UIPEthernet/utility/util.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H - -#define htons(x) ( ((x)<<8) | (((x)>>8)&0xFF) ) -#define ntohs(x) htons(x) - -#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ - ((x)<< 8 & 0x00FF0000UL) | \ - ((x)>> 8 & 0x0000FF00UL) | \ - ((x)>>24 & 0x000000FFUL) ) -#define ntohl(x) htonl(x) - -#endif From 6aa321cf2e94778409222a896d73fc8b27ce17a3 Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Thu, 24 Jul 2014 13:31:00 +0200 Subject: [PATCH 272/326] Remote reboot capability --- libraries/MySensors/MyMessage.h | 3 ++- libraries/MySensors/MySensor.cpp | 4 ++++ libraries/MySensors/MySensor.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 34deb03..a1999b6 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -41,7 +41,8 @@ typedef enum { typedef enum { I_BATTERY_LEVEL, I_TIME, I_VERSION, I_ID_REQUEST, I_ID_RESPONSE, I_INCLUSION_MODE, I_CONFIG, I_PING, I_PING_ACK, - I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION + I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION, + I_REBOOT } internal; // Type of sensor (for presentation message) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index a0ddcf2..87fae97 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -326,6 +326,10 @@ boolean MySensor::process() { bool isMetric; switch (type) { + case I_REBOOT: + wdt_enable(WDTO_15MS); + for (;;); + break; case I_ID_RESPONSE: if (nc.nodeId == AUTO) { nc.nodeId = msg.getByte(); diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 6e16071..5f81727 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "utility/LowPower.h" #include "utility/RF24.h" From cdf4d6ab52554f9fe190887559df01f37a70351d Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Thu, 24 Jul 2014 13:34:44 +0200 Subject: [PATCH 273/326] Binary message payload handling --- libraries/MySensors/MyGateway.cpp | 32 ++++++++++++++++++++++++++++--- libraries/MySensors/MyGateway.h | 2 ++ libraries/MySensors/MyMessage.cpp | 24 +++++++++++++++++++++++ libraries/MySensors/MyMessage.h | 6 ++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index 9040bbc..0a929be 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -105,10 +105,22 @@ void MyGateway::checkInclusionFinished() { } } +uint8_t MyGateway::h2i(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; +} void MyGateway::parseAndSend(char *commandBuffer) { boolean ok = false; char *str, *p, *value=NULL; + uint8_t bvalue[MAX_PAYLOAD]; + uint8_t blen = 0; int i = 0; uint16_t destination = 0; uint8_t sensor = 0; @@ -138,7 +150,18 @@ void MyGateway::parseAndSend(char *commandBuffer) { type = atoi(str); break; case 5: // Variable value - value = str; + if (command == C_STREAM) { + blen = 0; + uint8_t val; + while (*str) { + val = h2i(*str++) << 4; + val += h2i(*str++); + bvalue[blen] = val; + blen++; + } + } else { + value = str; + } break; } i++; @@ -161,7 +184,10 @@ void MyGateway::parseAndSend(char *commandBuffer) { msg.type = type; mSetCommand(msg,command); mSetAck(msg,ack==0?false:true); - msg.set(value); + if (command == C_STREAM) + msg.set(bvalue, blen); + else + msg.set(value); ok = sendRoute(msg); if (!ok) { errBlink(1); @@ -211,7 +237,7 @@ void MyGateway::serial(const char *fmt, ... ) { } void MyGateway::serial(MyMessage &msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); + serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, (mGetCommand(msg) == C_STREAM) ? msg.getStream(convBuf) : msg.getString(convBuf)); } diff --git a/libraries/MySensors/MyGateway.h b/libraries/MySensors/MyGateway.h index 185d933..e527bc3 100644 --- a/libraries/MySensors/MyGateway.h +++ b/libraries/MySensors/MyGateway.h @@ -68,6 +68,8 @@ class MyGateway : public MySensor uint8_t pinTx; uint8_t pinEr; + uint8_t h2i(char c); + void serial(const char *fmt, ... ); void serial(MyMessage &msg); void interruptStartInclusion(); diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 96ae96e..39c3cc4 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -28,6 +28,30 @@ const char* MyMessage::getString() const { } } +// handles single character hex (0 - 15) +char MyMessage::i2h(uint8_t i) const { + uint8_t k = i & 0x0F; + if (k <= 9) + return '0' + k; + else + return 'A' + k - 10; +} + +char* MyMessage::getStream(char *buffer) const { + uint8_t cmd = miGetCommand(); + if ((cmd == C_STREAM) && (buffer != NULL)) { + for (uint8_t i = 0; i < miGetLength(); i++) + { + buffer[i * 2] = i2h(data[i] >> 4); + buffer[(i * 2) + 1] = i2h(data[i]); + } + buffer[miGetLength() * 2] = '\0'; + return buffer; + } else { + return NULL; + } +} + char* MyMessage::getString(char *buffer) const { uint8_t payloadType = miGetPayloadType(); diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index a1999b6..d8df627 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -13,6 +13,8 @@ #define MyMessage_h #include +#include +#include #define PROTOCOL_VERSION 2 #define MAX_MESSAGE_LENGTH 32 @@ -92,6 +94,7 @@ typedef enum { // internal access for special fields +#define miGetCommand() BF_GET(command_ack_payload, 0, 3) #define miGetPayloadType() BF_GET(command_ack_payload, 4, 4) #define miGetLength() BF_GET(version_length, 3, 5) @@ -107,11 +110,14 @@ public: MyMessage(uint8_t sensor, uint8_t type); + char i2h(uint8_t i) const; + /** * 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. */ + char* getStream(char *buffer) const; char* getString(char *buffer) const; const char* getString() const; void* getCustom() const; From 1c475da8d0bb173a9049d7442744d48dd22caa2b Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Thu, 24 Jul 2014 13:37:44 +0200 Subject: [PATCH 274/326] OTA Bootloader - including adjustments to library header files to allow usage across C (bootloader) and C++ (firmware sketches) - including definition of new message types for firmware config and firmware code --- Bootloader/.gitignore | 3 + Bootloader/Makefile | 66 ++++++++ Bootloader/MyOtaBootloader.c | 207 +++++++++++++++++++++++++ Bootloader/MyOtaBootloader.h | 67 ++++++++ Bootloader/MyOtaBootloaderHW.h | 154 +++++++++++++++++++ Bootloader/MyOtaBootloaderRF24.h | 253 +++++++++++++++++++++++++++++++ libraries/MySensors/MyMessage.h | 22 ++- libraries/MySensors/MySensor.h | 17 ++- 8 files changed, 781 insertions(+), 8 deletions(-) create mode 100644 Bootloader/.gitignore create mode 100644 Bootloader/Makefile create mode 100644 Bootloader/MyOtaBootloader.c create mode 100644 Bootloader/MyOtaBootloader.h create mode 100644 Bootloader/MyOtaBootloaderHW.h create mode 100644 Bootloader/MyOtaBootloaderRF24.h diff --git a/Bootloader/.gitignore b/Bootloader/.gitignore new file mode 100644 index 0000000..bdb41b7 --- /dev/null +++ b/Bootloader/.gitignore @@ -0,0 +1,3 @@ +*.o +*.hex +*.elf \ No newline at end of file diff --git a/Bootloader/Makefile b/Bootloader/Makefile new file mode 100644 index 0000000..72cf373 --- /dev/null +++ b/Bootloader/Makefile @@ -0,0 +1,66 @@ +PROJECT = MyOtaBootloader +INCLUDES = ../libraries/MySensors + +MCU = atmega328p +CLK = 16000000L + +ISP_PORT = com5 +ISP_SPEED = 115200 +ISP_PROTOCOL = stk500v2 +ISP_MCU = m328p +# HFUSE = D8 - 4096 byte boot +ISP_HFUSE = D8 +# LFUSE = 4E - Low power resonator (16MHz), fast start, clkdiv/8 +ISP_LFUSE = FF +# EFUSE = 06 - 1.8V brownout +ISP_EFUSE = 06 +ISP_ARGS = -c$(ISP_PROTOCOL) -P$(ISP_PORT) -b$(ISP_SPEED) -p$(ISP_MCU) + +ifeq ($(OS),Windows_NT) + BINPATH = C:/Program Files (x86)/Arduino/hardware/tools/avr/bin/ + JAVAPATH = C:/Program Files/Java/jdk1.7.0_40/bin/ + JAVAC = $(JAVAPATH)javac.exe + JAVA = $(JAVAPATH)java.exe +else + UNAME_S := $(shell uname -s) + UNAME_P := $(shell uname -p) + UNAME_M := $(shell uname -m) + ifeq ($(UNAME_S),Linux) + ifeq ($(UNAME_P),x86_64) + BINPATH = ../arduino/bin-linux64/bin/ + else ifneq ($(filter %86,$(UNAME_P)),) + BINPATH = ../arduino/bin-linux32/bin/ + else ifneq ($(filter arm%,$(UNAME_M)),) + BINPATH = /usr/bin/ + endif + endif +endif + +CFLAGS = -c -g -Os -fno-inline-small-functions -fno-split-wide-types -mshort-calls -Wall -mmcu=$(MCU) -DF_CPU=$(CLK) -std=gnu99 +LDFLAGS = -Os -Wl,--section-start=.text=0x7000 -nostartfiles -mmcu=$(MCU) + +all: clean out + +clean: + - rm *.o + - rm *.elf + - rm *.hex + +$(PROJECT).o: $(PROJECT).c + "$(BINPATH)avr-gcc" $(CFLAGS) -I$(INCLUDES) $< -o $@ + +$(PROJECT).elf: $(PROJECT).o + "$(BINPATH)avr-gcc" $(LDFLAGS) -o $@ $< -lm + +$(PROJECT).hex: $(PROJECT).elf + "$(BINPATH)avr-objcopy" -O ihex -R .eeprom $< $@ + +out: $(PROJECT).hex + "$(BINPATH)avr-size" $(PROJECT).elf + +load: clean out isp + +isp: $(PROJECT).hex + "$(BINPATH)avrdude" $(ISP_ARGS) -e -u -Ulock:w:0x3f:m -qq -Uefuse:w:0x$(ISP_EFUSE):m -Uhfuse:w:0x$(ISP_HFUSE):m -Ulfuse:w:0x$(ISP_LFUSE):m -Ulock:w:0x0f:m + "$(BINPATH)avrdude" $(ISP_ARGS) -V -q -s -Uflash:w:$(PROJECT).hex + "$(BINPATH)avrdude" $(ISP_ARGS) diff --git a/Bootloader/MyOtaBootloader.c b/Bootloader/MyOtaBootloader.c new file mode 100644 index 0000000..a601075 --- /dev/null +++ b/Bootloader/MyOtaBootloader.c @@ -0,0 +1,207 @@ +#include "MyOtaBootloader.h" + +int main(void) __attribute__ ((naked)) __attribute__ ((section (".init9"))); + +volatile char dummy; + +EMPTY_INTERRUPT(WDT_vect); + +static uint16_t calcCRCrom (const void* ptr, uint16_t len) { + uint16_t crc = ~0; + for (uint16_t i = 0; i < len; ++i) + crc = _crc16_update(crc, pgm_read_byte((uint16_t) ptr + i)); + return crc; +} + +static uint8_t validFirmware () { + return calcCRCrom(0, fc.blocks * FIRMWARE_BLOCK_SIZE) == fc.crc; +} + +static void reboot() { + wdt_enable(WDTO_15MS); + for (;;); +} + +static void startup() { + if (validFirmware()) { + wdt_disable(); + clock_prescale_set(orgClockDiv); + ((void(*)()) 0)(); + } else + reboot(); +} + +static void boot_program_page(uint32_t page, uint8_t *buf) { + uint8_t sreg = SREG; + cli(); + eeprom_busy_wait(); + boot_page_erase(page); + boot_spm_busy_wait(); + for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) { + uint16_t w = *buf++; + w += (*buf++) << 8; + boot_page_fill(page + i, w); + } + boot_page_write(page); + boot_spm_busy_wait(); + boot_rww_enable(); + SREG = sreg; +} + +static boolean sendWrite(MyMessage message) { + return write(nc.parentNodeId, message.array, HEADER_SIZE + mGetLength(message), (message.destination == BROADCAST_ADDRESS)); +} + +static uint8_t sendAndWait(uint8_t reqType, uint8_t resType) { + msg.type = reqType; + for (uint8_t i = 0; i < 10; i++) { + sendWrite(msg); + for (uint16_t j = 0; j < 20; j++) { + wdt_reset(); + for (uint16_t j = 0; j < 100; j++) { + wdt_reset(); + uint8_t pipe; + boolean avail = available(&pipe); + if (avail && pipe<=6) { + read(rmsg.array,pipe); + if(!(mGetVersion(rmsg) == PROTOCOL_VERSION)) + continue; + if (rmsg.destination == nc.nodeId) { + if (mGetCommand(rmsg) == C_INTERNAL) { + if (rmsg.type == I_PING_ACK) { + if (rmsg.data[0] < nc.distance - 1) { + nc.distance = rmsg.data[0] + 1; + nc.parentNodeId = rmsg.sender; + eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId); + eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance); + } + } + } + if ((mGetCommand(rmsg) == mGetCommand(msg)) && (rmsg.type == resType)) + return 1; + } + } + delaym(1); + } + } + } + return 0; +} + +int main () { + asm volatile ("clr __zero_reg__"); + // switch to 4 MHz + orgClockDiv = clock_prescale_get(); + clock_prescale_set(F_CPU_DIV); + MCUSR = 0; + // enable watchdog to avoid deadlock + wdt_enable(WDTO_8S); + +#ifdef LED_DEBUG + // enable LEDs for debugging and blink once as visual "bootloader started" sign + led_init(); + blink(LED_MBOTH, 250); +#endif +#ifdef UART_DEBUG + // enable UART for debugging and send start message + uart_init(); + uart_puts("Go\n"); +#endif + + wdt_reset(); + // initialize RF module + begin(); + wdt_reset(); + // Read settings from EEPROM + eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(struct NodeConfig)); + wdt_reset(); + + msg.sender = nc.nodeId; + msg.last = nc.nodeId; + address(nc.nodeId); + + msg.sensor = NODE_SENSOR_ID; + mSetLength(msg, 0); + mSetPayloadType(msg, P_STRING); + mSetAck(msg,false); + mSetVersion(msg, PROTOCOL_VERSION); + + nc.distance = 255; + mSetCommand(msg, C_INTERNAL); + msg.destination = BROADCAST_ADDRESS; + if (!sendAndWait(I_PING, I_PING_ACK)) +// starting existing firmware if no ping response (if firmware is valid) +// reboot(); + startup(); + + msg.destination = GATEWAY_ADDRESS; + + if (nc.nodeId == AUTO) { + openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); + if (!sendAndWait(I_ID_REQUEST, I_ID_RESPONSE)) + reboot(); + if (mGetPayloadType(rmsg) == P_BYTE) + nc.nodeId = rmsg.data[0]; + else if (mGetPayloadType(rmsg) == P_STRING) + nc.nodeId = atoi(rmsg.data); + if (nc.nodeId == AUTO) + reboot(); + eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId); + msg.sender = nc.nodeId; + msg.last = nc.nodeId; + address(nc.nodeId); + } + + // Read settings from EEPROM + eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(struct FirmwareConfig)); + + mSetPayloadType(msg,P_CUSTOM); + + FirmwareConfigRequest *firmwareConfigRequest = (FirmwareConfigRequest *)msg.data; + mSetLength(msg, sizeof(FirmwareConfigRequest)); + mSetCommand(msg, C_STREAM); + firmwareConfigRequest->type = fc.type; + firmwareConfigRequest->version = fc.version; + if (!sendAndWait(ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE)) +// starting existing firmware if no firmware config response (if firmware is valid) +// reboot(); + startup(); + FirmwareConfigResponse *firmwareConfigResponse = (FirmwareConfigResponse *)rmsg.data; + if (firmwareConfigResponse->version == fc.version) + if (firmwareConfigResponse->blocks == fc.blocks) + if (firmwareConfigResponse->crc == fc.crc) + if (validFirmware()) + startup(); + fc.type = firmwareConfigResponse->type; + fc.version = firmwareConfigResponse->version; + fc.blocks = firmwareConfigResponse->blocks; + fc.crc = firmwareConfigResponse->crc; + + FirmwareRequest *firmwareRequest = (FirmwareRequest *)msg.data; + mSetLength(msg, sizeof(FirmwareRequest)); + firmwareRequest->type = fc.type; + firmwareRequest->version = fc.version; + for (uint16_t block = 0; block < fc.blocks; block++) { + firmwareRequest->block = block; + if (!sendAndWait(ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE)) + reboot(); + FirmwareResponse *firmwareResponse = (FirmwareResponse *)rmsg.data; + uint8_t offset = (block * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE; + memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); + if (offset == SPM_PAGESIZE - FIRMWARE_BLOCK_SIZE) + boot_program_page((block * FIRMWARE_BLOCK_SIZE) - offset, progBuf); + } + + wdt_reset(); + + if (validFirmware()) { + eeprom_write_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(struct FirmwareConfig)); +#ifdef UART_DEBUG + uart_puts("fw ok\n"); + } else { + uart_puts("fw nok\n"); +#endif + } + + reboot(); +} diff --git a/Bootloader/MyOtaBootloader.h b/Bootloader/MyOtaBootloader.h new file mode 100644 index 0000000..d906370 --- /dev/null +++ b/Bootloader/MyOtaBootloader.h @@ -0,0 +1,67 @@ +#ifndef MyOtaBootloader_H +#define MyOtaBootloader_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MyMessage.h" +#include "MySensor.h" +#include "MyOtaBootloaderRF24.h" + +#define FIRMWARE_BLOCK_SIZE 16 + +struct FirmwareConfig +{ + uint16_t type; + uint16_t version; + uint16_t blocks; + uint16_t crc; +}; + +typedef struct +{ + uint16_t type; + uint16_t version; +} FirmwareConfigRequest; + +typedef struct +{ + uint16_t type; + uint16_t version; + uint16_t blocks; + uint16_t crc; +} FirmwareConfigResponse; + +typedef struct +{ + uint16_t type; + uint16_t version; + uint16_t block; +} FirmwareRequest; + +typedef struct +{ + uint16_t type; + uint16_t version; + uint16_t block; + uint8_t data[FIRMWARE_BLOCK_SIZE]; +} FirmwareResponse; + +static struct NodeConfig nc; +static struct FirmwareConfig fc; +static MyMessage msg; +static MyMessage rmsg; + +static clock_div_t orgClockDiv = 0; + +static uint8_t progBuf[SPM_PAGESIZE]; + +#endif // MyOtaBootloader_H diff --git a/Bootloader/MyOtaBootloaderHW.h b/Bootloader/MyOtaBootloaderHW.h new file mode 100644 index 0000000..2eec57b --- /dev/null +++ b/Bootloader/MyOtaBootloaderHW.h @@ -0,0 +1,154 @@ +#ifndef MyOtaBootloaderHW_H +#define MyOtaBootloaderHW_H + +#include +#include + +//#define LED_DEBUG +//#define UART_DEBUG + +// hardware +// ******** + +#define F_CPU_DIV clock_div_4 +#define F_CPU_REAL F_CPU / (1 << F_CPU_DIV) +#define UART_BAUD 9600UL +#define DELAY_M 125 * (1 << F_CPU_DIV) + +// boolean definition +// ****************** + +typedef uint8_t bool; +typedef uint8_t boolean; +#define false 0x0 +#define true 0x1 +#define LOW 0x0 +#define HIGH 0x1 + +// delay +// ***** + +static void delayu() { + asm("nop"); +} + +static void delaym(uint16_t t) { + uint16_t d, a; + for (d = 0; d < t; d++) + for (a = 0; a < DELAY_M; a++) + asm("nop"); +} + +// LED debug +// ********* + +#ifdef LED_DEBUG +#define LED_DDR DDRD +#define LED_PORT PORTD +#define LED_RED 6 // Arduino Pin 6 <-> Bit 6 of port D +#define LED_GREEN 7 // Arduino Pin 7 <-> Bit 7 of port D +#define LED_MRED (1 << LED_RED) +#define LED_MGREEN (1 << LED_GREEN) +#define LED_MBOTH (LED_MRED | LED_MGREEN) + +static void led_init() { + LED_DDR |= LED_MBOTH; + LED_PORT &= ~LED_MBOTH; +} + +static void blink(uint8_t mask, uint16_t time) { + LED_PORT |= mask; + if (time > 0) + { + delaym(time); + LED_PORT &= ~mask; + delaym(time); + } +} +#endif + +// UART debug +// ********** + +#ifdef UART_DEBUG +static void uart_init() +{ + UCSR0A = _BV(U2X0); //Double speed mode USART0 + UCSR0B = _BV(RXEN0) | _BV(TXEN0); + UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); + UBRR0L = (uint8_t)( (F_CPU_REAL + UART_BAUD * 4L) / (UART_BAUD * 8L) - 1 ); +} + +static void uart_putc(char ch) { + while (!(UCSR0A & _BV(UDRE0))); + UDR0 = ch; +} + +static void uart_puts(char *s) +{ + while (*s) + uart_putc(*s++); +} + +static void uart_puti(uint8_t i) +{ + char s[5]; + itoa( i, s, 10 ); + uart_puts( s ); +} +#endif + +// SPI communication +// ***************** + +#define SPI_DDR DDRB +#define SPI_PORT PORTB +#define SPI_PIN PINB +#define SPI_SCLK 5 // Arduino Pin 13 <-> Bit 5 of port B +#define SPI_MISO 4 // Arduino Pin 12 <-> Bit 4 of port B +#define SPI_MOSI 3 // Arduino Pin 11 <-> Bit 3 of port B +#define SPI_CSN 2 // Arduino Pin 10 <-> Bit 2 of port B +#define SPI_CE 1 // Arduino Pin 9 <-> Bit 1 of port B + +#define csnlow() SPI_PORT &= ~(1<> SPI_MISO) & 0x01); // capture current MISO bit + delayu(); + SPI_PORT &= ~(1 << SPI_SCLK); + delayu(); + } + return value; +} + +static uint8_t spiTrans(uint8_t cmd){ + uint8_t status; + csnlow(); + status = SPItransfer( cmd ); + csnhigh(); + return status; +} + +#endif // MyOtaBootloaderHW_H diff --git a/Bootloader/MyOtaBootloaderRF24.h b/Bootloader/MyOtaBootloaderRF24.h new file mode 100644 index 0000000..fa2495c --- /dev/null +++ b/Bootloader/MyOtaBootloaderRF24.h @@ -0,0 +1,253 @@ +#ifndef MyOtaBootloaderRF24_H +#define MyOtaBootloaderRF24_H + +#include "MyOtaBootloaderHW.h" +#include "../libraries/MySensors/utility/nRF24L01.h" + +#include + +// hardware-agnostic interface to transceiver +// ****************************************** +// static void begin() +// static void address(uint8_t addr) +// static boolean write(uint8_t next, uint8_t* packet, uint8_t length, boolean multicast) +// static bool available(uint8_t* pipe_num) +// static void read(uint8_t* buf, uint8_t pipe) + +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + +static uint8_t read_register(uint8_t reg) +{ + csnlow(); + SPItransfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + uint8_t result = SPItransfer(0xff); + csnhigh(); + return result; +} + +static uint8_t write_registers(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + uint8_t status; + csnlow(); + status = SPItransfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + SPItransfer(*buf++); + csnhigh(); + return status; +} + +static uint8_t write_register(uint8_t reg, uint8_t value) +{ + uint8_t status; + csnlow(); + status = SPItransfer( W_REGISTER | ( REGISTER_MASK & reg ) ); + SPItransfer(value); + csnhigh(); + return status; +} + +static uint8_t read_payload(uint8_t* buf, uint8_t data_len) +{ +#ifdef UART_DEBUG + uart_puts("-> "); +#endif + uint8_t status; + uint8_t* current = buf; + csnlow(); + status = SPItransfer( R_RX_PAYLOAD ); + while ( data_len-- ) { + *current = SPItransfer(0xFF); +#ifdef UART_DEBUG + uart_puti(*current); + uart_putc(','); +#endif + current++; + } + csnhigh(); +#ifdef UART_DEBUG + uart_putc('\n'); +#endif + return status; +} + +#define get_status() spiTrans(NOP) + +static void powerUp(void) +{ + if (!(read_register(CONFIG) & _BV(PWR_UP))){ + write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP)); + delaym(5); + } +} + +#define convert_64_bit_to_byte_array(value, data) ((*(uint64_t *)(data)) = (uint64_t)(value)) + +static void startListening(void) +{ + powerUp(); + write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + if (pipe0_reading_address){ + uint8_t addressa[8]; + convert_64_bit_to_byte_array(pipe0_reading_address, addressa); + write_registers(RX_ADDR_P0, addressa, 5); + } + spiTrans(FLUSH_RX); + spiTrans(FLUSH_TX); + cehigh(); +} + +static void stopListening(void) +{ + celow(); + spiTrans(FLUSH_TX); + spiTrans(FLUSH_RX); + write_register(CONFIG, read_register(CONFIG) & ~_BV(PRIM_RX)); +} + +static void write_payload(const uint8_t* buf, uint8_t data_len, const uint8_t writeType) +{ +#ifdef UART_DEBUG + uart_puts("<- "); +#endif + const uint8_t* current = buf; + csnlow(); + SPItransfer(writeType); + while ( data_len-- ) { +#ifdef UART_DEBUG + uart_puti(*current); + uart_putc(','); +#endif + SPItransfer(*current++); + } + csnhigh(); +#ifdef UART_DEBUG + uart_putc('\n'); +#endif +} + +static bool writem( const uint8_t* buf, uint8_t len, const bool multicast ) +{ + write_payload( buf, len, multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD) ; + cehigh(); + celow(); + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + if( status & _BV(MAX_RT)){ + spiTrans(FLUSH_TX); + return 0; + } + return 1; +} + +static uint8_t getDynamicPayloadSize(void) +{ + uint8_t result = 0; + csnlow(); + SPItransfer( R_RX_PL_WID ); + result = SPItransfer(0xff); + csnhigh(); + if(result > 32) { + spiTrans(FLUSH_RX); + return 0; + } + return result; +} + +static bool available(uint8_t* pipe_num) +{ + if (!(read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ + if (pipe_num) { + uint8_t status = get_status(); + *pipe_num = ( status >> RX_P_NO ) & 0b111; + } + return 1; + } + return 0; +} + +static void writeAckPayload(uint8_t pipe, const uint8_t* buf, uint8_t len) +{ + const uint8_t* current = buf; + uint8_t data_len = len; + csnlow(); + SPItransfer(W_ACK_PAYLOAD | ( pipe & 0b111 ) ); + while ( data_len-- ) + SPItransfer(*current++); + csnhigh(); +} + +static void read( uint8_t* buf, uint8_t pipe ){ + read_payload(buf, getDynamicPayloadSize()); + write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS)); + writeAckPayload(pipe,&pipe, 1 ); +} + +static void openWritingPipe(uint64_t value) +{ + uint8_t valuea[8]; + convert_64_bit_to_byte_array(value, valuea); + write_registers(RX_ADDR_P0, valuea, 5); + write_registers(TX_ADDR, valuea, 5); +} + +static void openReadingPipe(uint8_t child, uint64_t address) +{ + if (child == 0) + pipe0_reading_address = address; + uint8_t addressa[8]; + convert_64_bit_to_byte_array(address, addressa); + if ( child < 2 ) + write_registers(RX_ADDR_P0 + child, addressa, 5); + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(ERX_P0 + child)); +} + +static boolean write(uint8_t next, uint8_t* packet, uint8_t length, boolean multicast) { + powerUp(); + stopListening(); + openWritingPipe(TO_ADDR(next)); + bool ok = writem(packet, length, multicast); + startListening(); + return ok; +} + +static void address(uint8_t addr) +{ + if (addr != BROADCAST_ADDRESS) { + openReadingPipe(WRITE_PIPE, TO_ADDR(addr)); + openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(addr)); + } +} + +static void begin(void) +{ + pipe0_reading_address = 0; + + SPIinit(); + delaym( 10 ) ; + + write_register(SETUP_RETR, 5 << ARD | 15 << ARC); + write_register(EN_AA, 0b00111111 & ~_BV(BROADCAST_PIPE)) ; + write_register(RF_CH, RF24_CHANNEL); + write_register(RF_SETUP, (read_register(RF_SETUP) & 0b11010000) | ((RF24_PA_LEVEL << 1) + 1) | ((RF24_DATARATE & 0b00000010 ) << 4) | ((RF24_DATARATE & 0b00000001 ) << 3)); + write_register(CONFIG, read_register(CONFIG) | _BV(CRCO) | _BV(EN_CRC)) ; + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT)); + + csnlow(); + SPItransfer(ACTIVATE); + SPItransfer(0x73); + csnhigh(); + write_register(FEATURE, read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + write_register(DYNPD, read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + + spiTrans(FLUSH_RX); + spiTrans(FLUSH_TX); + + powerUp(); //Power up by default when begin() is called + write_register(CONFIG, read_register(CONFIG) & ~_BV(PRIM_RX)); + openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); +} +#endif // MyOtaBootloaderRF24_H diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index d8df627..ae52f46 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -12,9 +12,11 @@ #ifndef MyMessage_h #define MyMessage_h -#include +#ifdef __cplusplus +#include #include #include +#endif #define PROTOCOL_VERSION 2 #define MAX_MESSAGE_LENGTH 32 @@ -56,7 +58,8 @@ typedef enum { // Type of data stream (for streamed message) typedef enum { - ST_FIRMWARE, ST_SOUND, ST_IMAGE + ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE, ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE, + ST_SOUND, ST_IMAGE } stream; typedef enum { @@ -101,7 +104,7 @@ typedef enum { #define miSetLength(_length) BF_SET(version_length, _length, 3, 5) #define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 4, 4) - +#ifdef __cplusplus class MyMessage { public: @@ -144,6 +147,13 @@ public: MyMessage& set(unsigned int value); MyMessage& set(int value); +#else + +typedef union { +struct +{ + +#endif uint8_t version_length; // 3 bit - Protocol version // 5 bit - Length of payload @@ -167,6 +177,12 @@ public: int iValue; char data[MAX_PAYLOAD + 1]; } __attribute__((packed)); +#ifdef __cplusplus } __attribute__((packed)); +#else +}; +uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1]; +} __attribute__((packed)) MyMessage; +#endif #endif diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 5f81727..b11d787 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -15,16 +15,19 @@ #include "Version.h" // Auto generated by bot #include "MyConfig.h" #include "MyMessage.h" -#include #include -#include #include #include #include #include + +#ifdef __cplusplus +#include +#include #include "utility/LowPower.h" #include "utility/RF24.h" #include "utility/RF24_config.h" +#endif #ifdef DEBUG #define debug(x,...) debugPrint(x, ##__VA_ARGS__) @@ -47,7 +50,11 @@ #define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) #define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. #define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller) -#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) // First free address for sketch static configuration +#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) +#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2) +#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2) +#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2) +#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) // First free address for sketch static configuration // This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). // This is also act as base value for sensor nodeId @@ -74,8 +81,7 @@ struct ControllerConfig { uint8_t isMetric; }; - - +#ifdef __cplusplus class MySensor : public RF24 { public: @@ -267,5 +273,6 @@ class MySensor : public RF24 void removeChildRoute(uint8_t childId); void internalSleep(int ms); }; +#endif #endif From cc5a9fe9a8829e5731a4e47234a6eb5dbf44da3d Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Thu, 24 Jul 2014 13:38:55 +0200 Subject: [PATCH 275/326] Node JS controller initial commit --- NodeJsController/BlinkGreen.hex | 71 + NodeJsController/BlinkRed.hex | 71 + .../DallasTemperatureSensor.cpp.hex | 1220 +++++++++++++++++ NodeJsController/NodeJsController.js | 617 +++++++++ NodeJsController/Reboot.js | 45 + NodeJsController/package.json | 9 + 6 files changed, 2033 insertions(+) create mode 100644 NodeJsController/BlinkGreen.hex create mode 100644 NodeJsController/BlinkRed.hex create mode 100644 NodeJsController/DallasTemperatureSensor.cpp.hex create mode 100644 NodeJsController/NodeJsController.js create mode 100644 NodeJsController/Reboot.js create mode 100644 NodeJsController/package.json diff --git a/NodeJsController/BlinkGreen.hex b/NodeJsController/BlinkGreen.hex new file mode 100644 index 0000000..00c3de6 --- /dev/null +++ b/NodeJsController/BlinkGreen.hex @@ -0,0 +1,71 @@ +:100000000C9461000C947E000C947E000C947E0095 +:100010000C947E000C947E000C947E000C947E0068 +:100020000C947E000C947E000C947E000C947E0058 +:100030000C947E000C947E000C947E000C947E0048 +:100040000C949A000C947E000C947E000C947E001C +:100050000C947E000C947E000C947E000C947E0028 +:100060000C947E000C947E00000000002400270009 +:100070002A0000000000250028002B0000000000DE +:1000800023002600290004040404040404040202DA +:100090000202020203030303030301020408102007 +:1000A0004080010204081020010204081020000012 +:1000B0000007000201000003040600000000000029 +:1000C000000011241FBECFEFD8E0DEBFCDBF11E08E +:1000D000A0E0B1E0E4E5F4E002C005900D92A030AC +:1000E000B107D9F711E0A0E0B1E001C01D92A9303D +:1000F000B107E1F70E9418020C9428020C9400004A +:1001000087E061E00E94C4016AEF70E080E090E067 +:100110000E941D0187E060E00E94C4016AEF70E068 +:1001200080E090E00E941D01089587E061E00E9458 +:10013000850108951F920F920FB60F9211242F93ED +:100140003F938F939F93AF93BF93809104019091BE +:100150000501A0910601B0910701309108010196B7 +:10016000A11DB11D232F2D5F2D3720F02D57019696 +:10017000A11DB11D209308018093040190930501F6 +:10018000A0930601B09307018091000190910101B5 +:10019000A0910201B09103010196A11DB11D8093B0 +:1001A000000190930101A0930201B0930301BF915C +:1001B000AF919F918F913F912F910F900FBE0F9014 +:1001C0001F901895789484B5826084BD84B5816051 +:1001D00084BD85B5826085BD85B5816085BDEEE64F +:1001E000F0E0808181608083E1E8F0E0108280812E +:1001F00082608083808181608083E0E8F0E080819C +:1002000081608083E1EBF0E0808184608083E0EBBB +:10021000F0E0808181608083EAE7F0E080818460A3 +:1002200080838081826080838081816080838081FF +:10023000806880831092C1000895EF92FF920F931F +:100240001F93CF93DF937B018C013FB7F89480918C +:10025000000190910101A0910201B091030126B526 +:10026000A89B05C02F3F19F00196A11DB11D3FBFEE +:10027000BA2FA92F982F8827820F911DA11DB11D7C +:1002800052E0880F991FAA1FBB1F5A95D1F7EC01A6 +:1002900030C00E9427023FB7F894809100019091EE +:1002A0000101A0910201B091030126B5A89B05C0F0 +:1002B0002F3F19F00196A11DB11D3FBFBA2FA92FE5 +:1002C000982F8827820F911DA11DB11D32E0880F44 +:1002D000991FAA1FBB1F3A95D1F78C1B9D0B885EF7 +:1002E0009340B8F20894E108F10801091109C851D6 +:1002F000DC4FE114F1040105110559F6DF91CF91AE +:100300001F910F91FF90EF900895CF93DF93482FA7 +:1003100050E0CA0186569F4FFC0134914A575F4F07 +:10032000FA018491882369F190E0880F991FFC01FC +:10033000E859FF4FA591B491FC01EE58FF4FC591CC +:10034000D491662351F42FB7F8948C91932F909504 +:1003500089238C93888189230BC0623061F42FB785 +:10036000F8948C91932F909589238C938881832B7B +:1003700088832FBF06C09FB7F8948C91832B8C93F2 +:100380009FBFDF91CF910895482F50E0CA01825559 +:100390009F4FFC012491CA0186569F4FFC01949106 +:1003A0004A575F4FFA013491332309F440C02223A6 +:1003B00051F1233071F0243028F42130A1F02230A3 +:1003C00011F514C02630B1F02730C1F02430D9F433 +:1003D00004C0809180008F7703C0809180008F7D62 +:1003E0008093800010C084B58F7702C084B58F7D64 +:1003F00084BD09C08091B0008F7703C08091B000A8 +:100400008F7D8093B000E32FF0E0EE0FFF1FEE58DA +:10041000FF4FA591B4912FB7F894662321F48C91E6 +:100420009095892302C08C91892B8C932FBF0895BE +:10043000CF93DF930E94E2000E949500C0E0D0E0DD +:100440000E9480002097E1F30E940000F9CF0895F8 +:04045000F894FFCF4E +:00000001FF diff --git a/NodeJsController/BlinkRed.hex b/NodeJsController/BlinkRed.hex new file mode 100644 index 0000000..3f2c259 --- /dev/null +++ b/NodeJsController/BlinkRed.hex @@ -0,0 +1,71 @@ +:100000000C9461000C947E000C947E000C947E0095 +:100010000C947E000C947E000C947E000C947E0068 +:100020000C947E000C947E000C947E000C947E0058 +:100030000C947E000C947E000C947E000C947E0048 +:100040000C949A000C947E000C947E000C947E001C +:100050000C947E000C947E000C947E000C947E0028 +:100060000C947E000C947E00000000002400270009 +:100070002A0000000000250028002B0000000000DE +:1000800023002600290004040404040404040202DA +:100090000202020203030303030301020408102007 +:1000A0004080010204081020010204081020000012 +:1000B0000007000201000003040600000000000029 +:1000C000000011241FBECFEFD8E0DEBFCDBF11E08E +:1000D000A0E0B1E0E4E5F4E002C005900D92A030AC +:1000E000B107D9F711E0A0E0B1E001C01D92A9303D +:1000F000B107E1F70E9418020C9428020C9400004A +:1001000086E061E00E94C4016AEF70E080E090E068 +:100110000E941D0186E060E00E94C4016AEF70E069 +:1001200080E090E00E941D01089586E061E00E9459 +:10013000850108951F920F920FB60F9211242F93ED +:100140003F938F939F93AF93BF93809104019091BE +:100150000501A0910601B0910701309108010196B7 +:10016000A11DB11D232F2D5F2D3720F02D57019696 +:10017000A11DB11D209308018093040190930501F6 +:10018000A0930601B09307018091000190910101B5 +:10019000A0910201B09103010196A11DB11D8093B0 +:1001A000000190930101A0930201B0930301BF915C +:1001B000AF919F918F913F912F910F900FBE0F9014 +:1001C0001F901895789484B5826084BD84B5816051 +:1001D00084BD85B5826085BD85B5816085BDEEE64F +:1001E000F0E0808181608083E1E8F0E0108280812E +:1001F00082608083808181608083E0E8F0E080819C +:1002000081608083E1EBF0E0808184608083E0EBBB +:10021000F0E0808181608083EAE7F0E080818460A3 +:1002200080838081826080838081816080838081FF +:10023000806880831092C1000895EF92FF920F931F +:100240001F93CF93DF937B018C013FB7F89480918C +:10025000000190910101A0910201B091030126B526 +:10026000A89B05C02F3F19F00196A11DB11D3FBFEE +:10027000BA2FA92F982F8827820F911DA11DB11D7C +:1002800052E0880F991FAA1FBB1F5A95D1F7EC01A6 +:1002900030C00E9427023FB7F894809100019091EE +:1002A0000101A0910201B091030126B5A89B05C0F0 +:1002B0002F3F19F00196A11DB11D3FBFBA2FA92FE5 +:1002C000982F8827820F911DA11DB11D32E0880F44 +:1002D000991FAA1FBB1F3A95D1F78C1B9D0B885EF7 +:1002E0009340B8F20894E108F10801091109C851D6 +:1002F000DC4FE114F1040105110559F6DF91CF91AE +:100300001F910F91FF90EF900895CF93DF93482FA7 +:1003100050E0CA0186569F4FFC0134914A575F4F07 +:10032000FA018491882369F190E0880F991FFC01FC +:10033000E859FF4FA591B491FC01EE58FF4FC591CC +:10034000D491662351F42FB7F8948C91932F909504 +:1003500089238C93888189230BC0623061F42FB785 +:10036000F8948C91932F909589238C938881832B7B +:1003700088832FBF06C09FB7F8948C91832B8C93F2 +:100380009FBFDF91CF910895482F50E0CA01825559 +:100390009F4FFC012491CA0186569F4FFC01949106 +:1003A0004A575F4FFA013491332309F440C02223A6 +:1003B00051F1233071F0243028F42130A1F02230A3 +:1003C00011F514C02630B1F02730C1F02430D9F433 +:1003D00004C0809180008F7703C0809180008F7D62 +:1003E0008093800010C084B58F7702C084B58F7D64 +:1003F00084BD09C08091B0008F7703C08091B000A8 +:100400008F7D8093B000E32FF0E0EE0FFF1FEE58DA +:10041000FF4FA591B4912FB7F894662321F48C91E6 +:100420009095892302C08C91892B8C932FBF0895BE +:10043000CF93DF930E94E2000E949500C0E0D0E0DD +:100440000E9480002097E1F30E940000F9CF0895F8 +:04045000F894FFCF4E +:00000001FF diff --git a/NodeJsController/DallasTemperatureSensor.cpp.hex b/NodeJsController/DallasTemperatureSensor.cpp.hex new file mode 100644 index 0000000..dbd4fa5 --- /dev/null +++ b/NodeJsController/DallasTemperatureSensor.cpp.hex @@ -0,0 +1,1220 @@ +:100000000C94AE020C94231F0C94501F0C94D60237 +:100010000C94D6020C94D6020C9447140C94D6027D +:100020000C94D6020C94D6020C94D6020C94D602F0 +:100030000C94D6020C94D6020C94D6020C94D602E0 +:100040000C947D1F0C94D6020C9453220C94D6224F +:100050000C94D6020C94D6020C94D6020C94D602C0 +:100060000C94D6020C94D602303B303B25643B25E1 +:10007000643B0072643D636C6561720A0069643DB3 +:1000800025640A0066756C6C0A006E6577207061E5 +:1000900072656E743D25642C20643D25640A0076EB +:1000A000657273696F6E206D69736D61746368004A +:1000B000726561643A2025642D25642D25642073C2 +:1000C0003D25642C633D25642C743D25642C70749F +:1000D0003D25642C6C3D25643A25730A0073656EDA +:1000E000643A2025642D25642D25642D2564207314 +:1000F0003D25642C633D25642C743D25642C70746F +:100100003D25642C6C3D25642C73743D25733A2584 +:10011000730A00726571206E6F64652069640A005D +:10012000636865636B2077697265730A00257320C5 +:10013000737461727465642C2069642025640A00FC +:100140000001020304050A0B0C0D0E0F111213140B +:100150001516A702AD02B302BB02C402CE02D7023B +:10016000DE02E602ED02F402FC02504120506F77FD +:10017000657209203D2025530D0A00435243204C4F +:10018000656E67746809203D2025530D0A004D6F88 +:1001900064656C0909203D2025530D0A00446174F3 +:1001A00061205261746509203D2025530D0A0044E9 +:1001B000594E50442F4645415455524500434F4EE9 +:1001C0004649470052465F53455455500052465FDA +:1001D000434800454E5F52584144445200454E5FEB +:1001E00041410052585F50575F50302D36005458EF +:1001F0005F414444520052585F414444525F503280 +:100200002D350052585F414444525F50302D31002B +:100210000D0A00253032780020307800255309255A +:1002200063203D000D0A002030782530327800250B +:1002300053092563203D004F4253455256455F54B4 +:10024000583D253032783A20504F4C535F434E543E +:100250003D2578204152435F434E543D25780D0A99 +:10026000005354415455530909203D20307825301E +:1002700032782052585F44523D25782054585F44CC +:10028000533D2578204D41585F52543D25782052EA +:10029000585F505F4E4F3D25782054585F46554C6F +:1002A0004C3D25780D0A00314D42505300324D42ED +:1002B0005053003235304B425053006E5246323468 +:1002C0004C3031006E524632344C30312B00446990 +:1002D0007361626C65640038206269747300313642 +:1002E00020626974730050415F4D494E0050415F78 +:1002F0004C4F570050415F484947480050415F4DBF +:10030000415800005EBCE2613FDD83C29C7E20A3B9 +:10031000FD1F419DC3217FFCA2401E5F01E3BD3E46 +:100320006082DC237D9FC1421CFEA0E1BF5D038093 +:10033000DE3C62BEE0025CDF81633D7C22C09E1D2C +:1003400043A1FF4618FAA427799BC584DA3866E5ED +:10035000BB5907DB856739BAE406581947A5FB780E +:1003600026C49A653BD987045AB8E6A7F91B45C647 +:10037000987A24F8A6441A99C7257B3A6486D85BF4 +:1003800005E7B98CD2306EEDB3510F4E10F2AC2FA1 +:100390007193CD114FADF3702ECC92D38D6F31B2DE +:1003A000EC0E50AFF1134DCE90722C6D33D18F0CFB +:1003B00052B0EE326C8ED0530DEFB1F0AE4C1291C4 +:1003C000CF2D73CA947628ABF517490856B4EA695D +:1003D00037D58B5709EBB536688AD495CB2977F496 +:1003E000AA4816E9B7550B88D6346A2B7597C94ABF +:1003F00014F6A8742AC896154BA9F7B6E80A54D77C +:10040000896B3500000000240027002A000000004E +:1004100000250028002B00000000002300260029F2 +:10042000000404040404040404020202020202039D +:1004300003030303030102040810204080010204A7 +:10044000081020010204081020000000070002012B +:100450000000030406000000000000000000407AD5 +:1004600010F35A00A0724E18090010A5D4E800003D +:10047000E87648170000E40B54020000CA9A3B00DB +:100480000000E1F505000080969800000040420F52 +:10049000000000A08601000000102700000000E816 +:1004A00003000000006400000000000A00000000DB +:1004B000000100000000002C76D888DC674F08237C +:1004C000DFC1DFAE59E1B1B796E5E3E453C63AE6E2 +:1004D00051997696E8E6C28426EB898C9B62ED40C2 +:1004E0007C6FFCEFBC9C9F40F2BAA56FA5F4900511 +:1004F0005A2AF75C936B6CF9676DC11BFCE0E40D45 +:1005000047FEF520E6B500D0ED902E030094357738 +:10051000050080841E080000204E0A000000C80C60 +:10052000333333330F986E12831141EF8D211489C9 +:100530003BE65516CFFEE6DB18D1844B381BF77C23 +:100540001D901DA4BBE424203284725E228100C968 +:10055000F124ECA1E53D27003F09892211241FBEAB +:10056000CFEFD8E0DEBFCDBF11E0A0E0B1E0E8EC16 +:10057000FBE402C005900D92AC35B107D9F713E04A +:10058000ACE5B1E001C01D92A530B107E1F715E07F +:10059000CCE5D5E004C02297FE010E949325C83522 +:1005A000D107C9F70E94FC220C94E2250C940000AC +:1005B000EF92FF920F931F93F22FF02E47FD02C090 +:1005C000E4E001C0E4E1242F332727FD309537FF15 +:1005D00003C0309521953F4F4F2D512F0F2FEE2EF9 +:1005E0000E9455068F2D912F1F910F91FF90EF9034 +:1005F0000895FC0188279927E89421912032E9F396 +:10060000293010F02E30C8F32B3241F02D3239F45E +:10061000689404C00E94DA03820F911D2191205337 +:100620002A30C0F31EF4909581959F4F08951F9333 +:10063000FC0199278827BC01E89411911032E9F355 +:10064000193010F01E30C8F31B3251F01D3249F43E +:10065000689406C00E94C503610F711D811D911D24 +:10066000119110531A30B0F33EF490958095709527 +:1006700061957F4F8F4F9F4F1F910895FB01DC01C4 +:1006800001900D920020E1F70895FB01DC0141503B +:10069000504048F001900D920020C9F701C01D9212 +:1006A00041505040E0F70895A0E0B0E0EAE5F3E003 +:1006B0000C94B5257C01EB018A01690109C0CE01CA +:1006C0002196F6010995F70181937F0101501040B1 +:1006D00001151105A1F7CDB7DEB7E8E00C94D125DF +:1006E000FB019F01E8944230C4F04532B4F44A3033 +:1006F00029F497FB1EF4909581959F4F642F7727DF +:100700000E949725805D8A330CF0895D8193CB012F +:100710000097A1F716F45DE251931082C9010C9481 +:10072000A508FA01CF93FF93EF93223044F12532CD +:1007300034F5C22FE894CA3049F497FB3EF4909503 +:100740008095709561957F4F8F4F9F4F2C2F33274A +:1007500044275527FF93EF930E946F25EF91FF9158 +:10076000605D6A330CF0695D6193B901CA01605044 +:1007700070408040904051F716F4CDE2C193108252 +:100780008F919F91CF910C94A508592F482F372F07 +:10079000262F660F771F881F991F660F771F881FE8 +:1007A000991F620F731F841F951F660F771F881F85 +:1007B000991F08957AE0979F902D879F802D910D26 +:1007C00011240895FA01CF93FF93EF932230CCF0D8 +:1007D0002532BCF4C22F2C2F332744275527FF93F3 +:1007E000EF930E946F25EF91FF91605D6A330CF0EB +:1007F000695D6193B901CA016050704080409040CA +:1008000051F710828F919F91CF910C94A508FB0115 +:100810009F01423074F0453264F4642F77270E94C0 +:100820009725805D8A330CF0895D8193CB01009719 +:10083000A1F71082C9010C94A508AEE0B0E0E3E294 +:10084000F4E00C94B9250D891E898F89988D97FF46 +:1008500002C080E090E801979E838D831A8309830C +:100860008EE08C839E01255E3F4FCE010196698DFF +:100870007A8DA9010E9478044D815E8157FD0AC0DE +:100880002F813885421753070CF49A01020F131F6A +:10089000F80110822E96E4E00C94D525AEE0B0E08D +:1008A000E4E5F4E00C94B9258C0177FF02C060E028 +:1008B00070E8615070407E836D831A8309838EE0F7 +:1008C0008C83CE010196BA01A9010E9478042D8182 +:1008D0003E8137FD09C0EF81F8852E173F070CF4E4 +:1008E000F901E00FF11F10822E96E4E00C94D5255B +:1008F000ABE0B0E0EEE7F4E00C94AB253C012B015B +:100900005A01FC0117821682838181FD03C06FEFBB +:100910007FEFC6C19AE0892E1E010894211C311C6C +:10092000F3012381F20123FD859123FF81912F01A2 +:10093000882309F4B2C1853239F423FD859123FF60 +:1009400081912F01853229F490E0B3010E94B5080E +:10095000E7CF982FFF24EE249924FFE1FF15D0F074 +:100960009B3269F09C3228F4903259F0933291F422 +:100970000EC09D3249F0903369F441E024C052E04A +:10098000F52A84E0F82A28C098E0F92A25C0E0E199 +:10099000FE2A22C0F7FC29C0892F80538A3070F4C8 +:1009A000F6FE05C0989C902C1124980E15C0E89C6A +:1009B000E02C1124E80EF0E2FF2A0EC09E3229F44A +:1009C000F6FC6BC140E4F42A07C09C3619F450E8E9 +:1009D000F52A02C0983649F4F20123FD959123FFD0 +:1009E00091912F01992309F0B8CF892F8554833035 +:1009F00018F08052833038F444E050E0A40EB51E65 +:100A00005FE359830FC0933631F0933779F0933514 +:100A100009F056C020C0F5018081898342E050E092 +:100A2000A40EB51E610101E010E012C0F501C08006 +:100A3000D180F6FC03C06FEF7FEF02C0692D70E03C +:100A400042E050E0A40EB51EC6010E949A088C0137 +:100A50005FE7F52214C0F501C080D180F6FC03C029 +:100A60006FEF7FEF02C0692D70E042E050E0A40E0E +:100A7000B51EC6010E948F088C0150E8F52AF3FECE +:100A800007C01AC080E290E0B3010E94B508EA9462 +:100A90008E2D90E008171907A8F30EC0F601F7FC99 +:100AA0008591F7FE81916F0190E0B3010E94B50836 +:100AB000E110EA94015010400115110579F7EAC0E0 +:100AC000943611F0993669F5F7FE08C0F5012081DA +:100AD00031814281538184E090E00AC0F501808138 +:100AE00091819C01442737FD4095542F82E090E08E +:100AF000A80EB91E9FE6F92257FF09C050954095F0 +:100B0000309521953F4F4F4F5F4FE0E8FE2ACA01D5 +:100B1000B901A1012AE030E00E94E108D82ED218E4 +:100B200040C0953729F41F2D1F7E2AE030E01DC0FC +:100B30001F2D197F9F3661F0903720F4983509F00A +:100B4000ACC00FC0903739F0983709F0A6C004C088 +:100B500028E030E00AC0106114FD146020E130E0AC +:100B600004C014FD166020E132E017FF08C0F50153 +:100B7000608171818281938144E050E008C0F50179 +:100B800080819181BC0180E090E042E050E0A40EC1 +:100B9000B51EA1010E94E108D82ED2188FE7F82EC9 +:100BA000F122F6FE0BC05EEFF522D91438F4F4FE04 +:100BB00007C0F2FC05C08FEEF82202C01D2D01C057 +:100BC000192DF4FE0DC0FE01ED0DF11D8081803365 +:100BD00019F499EEF92208C01F5FF2FE05C003C0A8 +:100BE0008F2D867809F01F5F0F2DF3FC14C0F0FEE7 +:100BF0000FC01E1510F09D2C0BC09D2C9E0C911A41 +:100C00001E2D06C080E290E0B3010E94B5081F5F70 +:100C10001E15C0F304C01E1510F4E11A01C0EE2425 +:100C200004FF0FC080E390E0B3010E94B50802FF0B +:100C30001DC001FD03C088E790E00EC088E590E08C +:100C40000BC0802F867891F001FF02C08BE201C0BB +:100C500080E2F7FC8DE290E0B3010E94B50806C087 +:100C600080E390E0B3010E94B5089A94D914C0F3D0 +:100C7000DA94F101ED0DF11D808190E0B3010E9445 +:100C8000B508DD20A9F706C080E290E0B3010E941C +:100C9000B508EA94EE20C1F743CEF301668177816F +:100CA000CB012B96E2E10C94C725A9E0B0E0EBE57F +:100CB000F6E00C94AF256A01122FA02EBE2C2BE378 +:100CC000201718F42CE3922E02C0902E9394AE01BC +:100CD0004F5F5F4F27E0092D0E94B707BC01898154 +:100CE000E82EFF24C70189709070019711F4ADE2DE +:100CF00009C0B1FE02C0ABE205C0B0FC02C0A0E07A +:100D000001C0A0E2E3FE3BC0AA2311F423E001C02E +:100D100024E0211710F020E002C0121B212FEB2C41 +:100D2000B4FE02C0322F0AC0F601322F80E202C0A8 +:100D3000819331503323E1F7C20ED11CAA2319F05D +:100D4000F601A1936F01F6013196E2FE05C09EE423 +:100D5000D6019C9381E404C09EE6D6019C9381E673 +:100D600011968C9391833296DF01832F90E202C01B +:100D70009D9381508823E1F7E30FF11D40C0E2FE0F +:100D800042C0AA2311F423E001C024E0211710F08F +:100D900020E002C0121B212FEB2CB4FE02C0922FC8 +:100DA0000AC0F601922F80E202C0819391509923EC +:100DB000E1F7C20ED11CAA2319F0F601A1936F012D +:100DC000F6013196E2FE08C089E4D6018C938EE4E8 +:100DD00011968C9386E407C089E6D6018C938EE643 +:100DE00011968C9386E681833296DF01892F20E26B +:100DF00002C02D9381508823E1F7E90FF11D108285 +:100E00002EEF3FEFAFC0161617061CF041E050E082 +:100E100003C0AB014F5F5F4FAA2019F480E090E060 +:100E200003C08A2D90E0019620E030E0AA2311F063 +:100E300021E030E0240F351F280F391F812F90E06B +:100E40002817390714F020E002C0121B212F4B2D68 +:100E500050E0CA0180719070B4FC0DC0B3FC0BC0AF +:100E6000F601322F40E202C0419331503323E1F7C3 +:100E7000C20ED11C01C0322FAA2319F0F601A19392 +:100E80006F01892B11F0132F0AC0F601132F80E395 +:100E900002C0819311501123E1F7C30ED11C092D1B +:100EA000060FBA8090E1892E912C8E209F20E4FEBF +:100EB00004C0F1E3BF1609F40150101614F001E06C +:100EC00003C0093008F008E09B0177FF02C020E072 +:100ED00030E0CB010196821B930BFE013196E80FA7 +:100EE000F91F8EE2F82E3B01601A71088A2D90E0FE +:100EF00044275527481B590B8FEF2F3F380719F40C +:100F0000D601FD926D01621773072CF0621673060D +:100F100014F4808101C080E32150304031962417C1 +:100F2000350724F0D6018D936D01E6CF26173707DC +:100F300049F4B5E3BB1528F0BB1621F481149104E4 +:100F400009F481E3F6018193D601812F90E202C07A +:100F50009C93815011968823D9F7E10FF11D1082DF +:100F600020E030E0C9012996EEE00C94CB25283032 +:100F700008F027E03327DA01990F311D87FD9160D2 +:100F800000966105710539F432602E5F3D9330E3C0 +:100F90002A95E1F708959F3F30F08038710561058B +:100FA00009F03C5F3C5F3D93913008F08068911DF3 +:100FB000DF93CF931F930F93FF92EF92192F987F98 +:100FC0009695E92F96959695E90FFF27E954FB4FE3 +:100FD00099273327EE24FF24A701E7010590089401 +:100FE000079428F4360FE71EF81E491F511D660F9F +:100FF000771F881F991F0694A1F70590079428F47E +:10100000E70EF81E491F561FC11D770F881F991F35 +:10101000661F0694A1F70590079428F4F80E491F5F +:10102000561FC71FD11D880F991F661F771F069473 +:10103000A1F70590079420F4490F561FC71FD81F2A +:10104000990F661F771F881F0694A9F78491109542 +:10105000177041F0D695C79557954795F794E79443 +:101060001A95C1F7EDE5F4E0689415901591359166 +:101070006591959105907FE27395E118F10A430B14 +:10108000560BC90BD009C0F7E10CF11E431F561FC8 +:10109000C91FD01D7EF4703311F48A95E6CFE89411 +:1010A000015030F0080F0AF40027021708F4202F2F +:1010B0002395022F7A3328F079E37D932A95E9F777 +:1010C00010C07D932A9589F6069497956795379574 +:1010D00017951794E118F10A430B560BC90BD00969 +:1010E00098F023957E9173957A3308F070E37C93A2 +:1010F0002013B8F77E9170617D9330F0839571E392 +:101100007D9370E32A95E1F71124EF90FF900F9102 +:101110001F91CF91DF91992787FD90950895FC014C +:101120000590615070400110D8F7809590958E0F12 +:101130009F1F0895FC016150704001900110D8F785 +:10114000809590958E0F9F1F0895DC01FC01672FFD +:1011500071917723E1F7329704C07C916D9370838E +:101160006291AE17BF07C8F308950F931F93CF93F3 +:10117000DF938C01EB018B8181FF1BC082FF0DC0CF +:101180002E813F818C819D812817390764F4E88185 +:10119000F9810193F983E88306C0E885F985802FFA +:1011A0000995892B31F48E819F8101969F838E83CF +:1011B00002C00FEF1FEFC801DF91CF911F910F9178 +:1011C0000895FA01AA27283051F1203181F1E894DD +:1011D0006F936E7F6E5F7F4F8F4F9F4FAF4FB1E02A +:1011E0003ED0B4E03CD0670F781F891F9A1FA11D25 +:1011F000680F791F8A1F911DA11D6A0F711D811D26 +:10120000911DA11D20D009F468943F912AE0269FEA +:1012100011243019305D3193DEF6CF010895462F49 +:101220004770405D4193B3E00FD0C9F7F6CF462F2A +:101230004F70405D4A3318F0495D31FD4052419393 +:1012400002D0A9F7EACFB4E0A69597958795779550 +:101250006795BA95C9F700976105710508959B01D7 +:10126000AC010A2E06945795479537952795BA9560 +:10127000C9F7620F731F841F951FA01D08950F9358 +:101280001F930CE511E0C80163E00E94B81D8AE6D7 +:1012900091E0B8010E940F198CE791E069E04AE003 +:1012A0000E94E91385E392E060E040E00E94330A87 +:1012B0001F910F910895CF92DF92EF92FF920F93BB +:1012C0001F93CF93DF938CE791E00E94790D8AE61C +:1012D00091E00E94041A83EFC82E81E0D82EC0E06E +:1012E000D0E056C08CE791E00E941B0B882331F0C0 +:1012F0008AE691E06C2F0E94271B05C08AE691E0E8 +:101300006C2F0E944E1B20E030E040E251E40E942E +:101310000C250E942924882777FD8095982F0E940C +:101320005C2420E030E040E251E40E94C1237B01D4 +:101330008C01F6016081718182819381A8019701FE +:101340000E94BD238823F9F0C801B70120E030E0F6 +:101350004EEF52EC0E94BD238823A9F085E392E072 +:101360006C2F0E94380AB801A70121E00E94470AA9 +:10137000BC018CE791E040E00E942112F601E0827E +:10138000F18202831383219684E090E0C80ED91E77 +:101390008091330290913402C817D90724F4C031E8 +:1013A000D10509F09FCF60914001709141018CE718 +:1013B00091E00E94800CDF91CF911F910F91FF90DF +:1013C000EF90DF90CF900895AF92BF92CF92EF92BF +:1013D000FF920F93CF93DF938AE691E00E945D1B0B +:1013E0008CE791E060E070E04FEF20E00FEFE3E08A +:1013F000EE2EF12C5CE4C52E32E0A32EB12C0E941F +:10140000B3128CE791E060E071E043E151E020E04D +:101410000E94B1118AE691E00E941D198093330267 +:1014200010923402C0E0D0E008C08CE791E06C2F4D +:1014300046E020E00E94FF112196809133029091B6 +:101440003402C817D9071CF4C031D10571F7DF91F8 +:10145000CF910F91FF90EF90CF90BF90AF900895F4 +:10146000FC0114820895FC0114826683458308956B +:10147000FC0166830895FC01808187708860808309 +:1014800081818F70806181836783CF010895CF92BE +:10149000DF92EF92FF920F931F937C01F7E0CF2E24 +:1014A000D12CC80ED91ECB01BA0142E086010E94A0 +:1014B000D802F60101900020E9F73197EC19FD09F7 +:1014C000EE0FEE0FEE0FD7018C918770E82BEC93A7 +:1014D00011968C9111978F7011968C93C7011F9163 +:1014E0000F91FF90EF90DF90CF900895CF93DF930F +:1014F000EC01DB010D900020E9F71197A61B4A2FA4 +:1015000050E09A01B3E0220F331FBA95E1F78881CA +:101510008770822B888389818F708983A93110F02D +:1015200049E150E0CE0107960E944503CE01DF91CC +:10153000CF910895FC01818182958F7090E0009792 +:1015400031F0019711F080E0089587810895CF016F +:1015500007960E94F9020895FC01818182958F709F +:1015600090E0009761F0059729F020E030E040E03E +:1015700050E00EC0278130854185528509C0CF01DA +:1015800007960E941703AC01CB01DA019C01AD0163 +:10159000B901CA010895CF93DF93FC01EB0181816A +:1015A00082958F7031F43796CB01BF010E943E03C4 +:1015B00036C06115710599F18330B9F0843028F493 +:1015C000813041F0823059F508C08430A9F085306F +:1015D00031F51CC0878190E002C087819085BE01F3 +:1015E0004AE050E00E9470031AC0878190854AE06B +:1015F00050E00E94070413C0678170858185928541 +:10160000AE012AE030E00E94910309C06781708535 +:1016100081859285AE012AE030E00E94E203CE018E +:10162000DF91CF9108959C01F999FECF32BD21BD84 +:10163000F89A80B50895FC0181890895EF92FF9290 +:101640000F931F93DF93CF93CDB7DEB7CC52D1402A +:101650000FB6F894DEBF0FBECDBFC95CDE4FE88089 +:10166000F980C753D140CB5CDE4FA881B981C55307 +:10167000D14054968C91882389F1EDB7FEB73A9703 +:101680000FB6F894FEBF0FBEEDBF31968E010F5F0F +:101690001F4FADB7BEB712961C930E9311978BE2F6 +:1016A00091E09383828388E690E09583848383E04E +:1016B00090E09783868389E090E0918780870E94FD +:1016C0001D04EDB7FEB73A960FB6F894FEBF0FBEF5 +:1016D000EDBF84E692E0B8010E945423FE01E75C6E +:1016E000FE4FCB5CDE4FA881B981C553D1405496E3 +:1016F0008C919E012F5F3F4F882359F0C9016CE305 +:1017000070E0A7019F010E944E048AE08CAF1DAEDD +:1017100007C0C9016BE271E0A7019F010E944E045E +:1017200084E692E0BE016F5F7F4F0E94542384E6FF +:1017300092E00E94E121C45DDE4F0FB6F894DEBF57 +:101740000FBECDBFCF91DF911F910F91FF90EF9012 +:101750000895CF93DF93EB010CC086E592E069E03A +:1017600070E040E050E020E030E00E94F913C05407 +:10177000DF418091440190914501892B89F08FE1EF +:10178000C034D80754F7B3C086E592E068E070E053 +:1017900040E050E020E030E00E94F913C05ADF4002 +:1017A0008091440190914501892B81F087E0C03DF3 +:1017B000D80764F086E592E067E070E040E050E032 +:1017C00020E030E00E94F913C05DD74080914401D1 +:1017D00090914501892B81F083E0C83ED80764F0E1 +:1017E00086E592E066E070E040E050E020E030E026 +:1017F0000E94F913C85ED340809144019091450145 +:10180000892B81F081E0C43FD80764F086E592E03F +:1018100065E070E040E050E020E030E00E94F91325 +:10182000C45FD1408091440190914501892B79F0AA +:10183000CA3FD10564F086E592E064E070E040E0E4 +:1018400050E020E030E00E94F913CA5FD040809160 +:10185000440190914501892B79F0CD37D10564F091 +:1018600086E592E063E070E040E050E020E030E0A8 +:101870000E94F913C857D0408091440190914501CE +:10188000892B71F0C034D1055CF086E592E062E00E +:1018900070E040E050E020E030E00E94F913EC9767 +:1018A0008091440190914501892B71F0C032D1059E +:1018B0005CF086E592E061E070E040E050E020E01E +:1018C00030E00E94F9136E97809144019091450198 +:1018D000892B99F060978CF086E592E060E070E0EB +:1018E00040E050E020E030E00E94F91306C08FE0B5 +:1018F000C03AD8070CF454CF47CFDF91CF91089569 +:10190000EF92FF920F931F938C017B0184E692E08C +:101910000E94E121C8010E941E1881E090E090938E +:10192000450180934401C801B7010E94A90B1F9192 +:101930000F91FF90EF9008955F926F927F928F9238 +:101940009F92AF92BF92CF92DF92EF92FF920F934E +:101950001F93CF93DF937C01562EEA01822E88815C +:10196000982E969496949694D7011E969C919B835C +:10197000887F82608883DE2CCF2CC7010E940318E9 +:10198000C7010E94331830E040E050E060E070E0B2 +:1019900080E090E0052DF1E03D3F08F4F0E0E1E06B +:1019A000423E08F4E0E031EEA32EAF0EE060F1E03D +:1019B000593A08F4F0E028EAB22EBE0EE1E0B8EAA7 +:1019C000BB1608F0E0E0FE2BA1E0693A08F4A0E0C5 +:1019D000BF2FB855E1E0B83A08F0E0E0AE2BF1E0F7 +:1019E000772309F4F0E0E1E0882309F4E0E08D2DAD +:1019F0009C2D1CEF95014B2F5A2F6F2F7E2F0E948D +:101A00004416892D90E04A9714F040E202C0492D17 +:101A1000495F8D2D9C2DBE01282D0E94F715682E43 +:101A20008D2D9C2D0E944E18BA80AB809C808E809C +:101A300019817D800881662029F497E1C92E91E003 +:101A4000D92E04C08CE1C82E81E0D82EB701695A86 +:101A50007F4FCE010E94CB0AEDB7FEB77A970FB643 +:101A6000F894FEBF0FBEEDBF3196ADB7BEB712966C +:101A7000FC92EE9211972DED30E033832283B482F5 +:101A80001582A682178250861186928613868486D6 +:101A90001586212F30E02770307037872687708AAF +:101AA000118A12951F70128B138A0695069506955A +:101AB000048B158AD78AC68A918F808F0E941E0B4D +:101AC000EDB7FEB77A960FB6F894FEBF0FBEEDBF26 +:101AD000862DDF91CF911F910F91FF90EF90DF90B6 +:101AE000CF90BF90AF909F908F907F906F905F90BE +:101AF00008954F925F926F927F928F929F92AF92D2 +:101B0000BF92CF92DF92EF92FF920F931F93DF93DA +:101B1000CF930F92CDB7DEB74C01582E492C8E01D2 +:101B20000F5F1F4FB8010E94E417882309F4D5C244 +:101B30008981873008F0D1C2C4010E94F414482F73 +:101B400065E1662E712C680C791CC401B3010E94FA +:101B5000EA15C4016981A80121E00E948614D4011C +:101B600055960C9106950695069510E0F401E00F48 +:101B7000F11F148EF401E788D08CC18CB38CF688E9 +:101B8000A28CB401695A7F4FC3010E94CB0A2DB7C2 +:101B90003EB7265130400FB6F8943EBF0FBE2DBF62 +:101BA000EDB7FEB73196ADB7BEB712969C928E9246 +:101BB000119720EB30E033832283E4821582D682B2 +:101BC0001782C0861186B28613862F2D30E02770CB +:101BD000307035872487A6861786F2945FE0F52259 +:101BE000F08A118A138B028B958B848B0E941E0BBB +:101BF000F401858987702DB73EB72A5E3F4F0FB637 +:101C0000F8943EBF0FBE2DBF8230B1F000D000D09F +:101C1000ADB7BEB712969C928E9211978FE990E065 +:101C200014969C938E9313970E941E0B80E00F9046 +:101C30000F900F900F9052C2F4012689022F077067 +:101C4000128DE788F08C318D9289992379F10330D8 +:101C500069F5173059F50E94C51F73708070907038 +:101C60000E941820D4011E968C911E9757968C9333 +:101C700057975996EC9259978FEF5B968C935B9799 +:101C800088E05A968C935A9756968C915697807FF7 +:101C9000836056968C935697C30150966C910E9420 +:101CA0003B0AAC01C4016E2D21E0F5C1F40186852B +:101CB000381709F0DBC123FF21C0D401D6967596F1 +:101CC00081E201900D928150E1F7D401D7968C9179 +:101CD000D797877FD7968C93D7971E968C911E9710 +:101CE000D8968C93D89757968C915797DA968C9371 +:101CF000B4016A5C7F4FC4010E944211F4018289E1 +:101D0000882319F18785F81601F12E2D30E081E73F +:101D100090E0880E991ED401ED91FC918FE89FEF21 +:101D2000880E991EE20FF31F80818F1571F0F082EB +:101D3000F999FECF1FBA2D5F3F4F32BD21BDF0BCD8 +:101D40000FB6F894FA9AF99A0FBE033009F081C1E0 +:101D5000183009F053C0D40154968C91882309F0AF +:101D60004DC0C3010E949A0A482F282F30E0F40189 +:101D7000808990E00197281739070CF0AEC14F5FBA +:101D8000408B27892787F999FECF1FBA81E090E021 +:101D900092BD81BD20BD0FB6F894FA9AF99A0FBE94 +:101DA000D40150963C91F999FECF1FBA82E090E0A1 +:101DB00092BD81BD30BD0FB6F894FA9AF99A0FBE64 +:101DC000EDB7FEB738970FB6F894FEBF0FBEEDBF64 +:101DD0003196ADB7BEB712969C928E9211978AE853 +:101DE00090E09383828324831582368317820E9436 +:101DF0001E0B80E0EDB7FEB73896A3C0EE2009F0C9 +:101E000028C1163009F4A3C0173038F4113009F492 +:101E10000FC1143009F061C112C01A3009F4B0C00A +:101E20001D3009F05AC128E088E190E00FB6F8941F +:101E3000A895809360000FBE20936000FFCFD4016F +:101E40001E968C918F3F09F048C1C3010E949A0AE7 +:101E5000F40186878F3FA9F400D000D0ADB7BEB79C +:101E600012969C928E92119784E890E014969C931F +:101E70008E9313970E941E0B0F900F900F900F9050 +:101E8000FFCF282F30E040E050E060E070E080E0DD +:101E900090E0E22E01E03D3F08F400E011E0423E18 +:101EA00008F410E00F511060E1E0593A08F4E0E066 +:101EB0001855F1E0183A08F0F0E0EF2BF1E0693A3C +:101EC00008F4F0E0BE2FB855E1E0B83A08F0E0E0E1 +:101ED000FE2BA1E0772309F4A0E0E1E0882309F4D8 +:101EE000E0E0852D942D61E04CEFF42E2B2F3F2F59 +:101EF0004A2F5E2F0E946517F4012685F999FECFBF +:101F00001FBA12BC11BC20BD0FB6F894FA9AF99A08 +:101F10000FBE00D000D000D0EDB7FEB73196ADB700 +:101F2000BEB712969C928E9211978DE790E09383A4 +:101F30008283248315820E941E0B80E0EDB7FEB7DA +:101F400036960FB6F894FEBF0FBEEDBFC7C0C301F3 +:101F50000E949A0A20E08D3409F421E0D4015196C0 +:101F60008C915197821709F4B8C051962C93F99926 +:101F7000FECF1FBA83E091E092BD81BD20BDA8C015 +:101F8000F4018289882309F4A8C0C3010E949A0A37 +:101F9000833409F0A2C000D000D0ADB7BEB712960E +:101FA0009C928E92119783E790E014969C938E9367 +:101FB00013970E941E0B20E030E00F900F900F90BF +:101FC0000F90D401AF58BF4F9FEFED91FC91119747 +:101FD000E20FF31F80818F3F81F09083F999FECF4C +:101FE0001FBA2D5F3F4F32BD21BD2350304090BD01 +:101FF0000FB6F894FA9AF99A0FBE2F5F3F4F22302E +:10200000310519F7F4018685878B118E8FEF838F49 +:102010008AE0828F8689807F8360868BC3016FE12F +:1020200071E00E94760ABC01C4010E94421155C0B1 +:1020300023E730E0820E931ED4010D911C9101150F +:10204000110509F44AC0C3010E94AC0AF8010995C0 +:1020500044C0E5E7F0E08E0E9F1ED401ED91FC91A7 +:10206000309711F0C301099581E038C09923A9F197 +:102070008981813091F584010F581F4FD801ED916E +:10208000FC91E30FF11D6081862F81508E3F30F46B +:10209000C401A30120E00E949C0C1FC0C401F401F4 +:1020A0006785A30120E00E949C0C6E2D70E0D80192 +:1020B000ED91FC91E60FF71F80818F1571F0F08292 +:1020C000F999FECF1FBA6D5F7F4F72BD61BDF0BC45 +:1020D0000FB6F894FA9AF99A0FBE80E00F90CF915C +:1020E000DF911F910F91FF90EF90DF90CF90BF9005 +:1020F000AF909F908F907F906F905F904F900895DA +:10210000EF92FF920F931F93CF93DF93EC010E9406 +:10211000C51F7B018C0103C0CE010E94790D0E9476 +:10212000C51F6E197F09800B910B605D7740804061 +:10213000904090F3DF91CF911F910F91FF90EF901E +:102140000895EF92FF920F931F938C0100591F4F38 +:10215000F8011082005710409FEFF801908B8685A0 +:10216000878B918F938F87E0828F8689807F8360B2 +:10217000868B75E1E72EF12CE00EF11EC7016FE1B1 +:1021800071E00E94760AC8016FEFA70121E00E946A +:102190009C0CC8010E9480101F910F91FF90EF903E +:1021A0000895EF92FF920F931F93CF93DF93EC016B +:1021B00000D000D0EDB7FEB79283818383E191E038 +:1021C000948383830E941E0B0F900F900F900F90AB +:1021D0008E85282F30E040E050E060E070E080E045 +:1021E00090E0E22E01E03D3F08F400E011E0423EC5 +:1021F00008F410E00F511060E1E0593A08F4E0E013 +:102200001855F1E0183A08F0F0E0EF2BF1E0693AE8 +:1022100008F4F0E0BE2FB855E1E0B83A08F0E0E08D +:10222000FE2BA1E0772309F4A0E0E1E0882309F484 +:10223000E0E0CE0161E02CEFF22E2B2F3F2F4A2F52 +:102240005E2F0E9465178E858F8B198E8FEF8B8F77 +:1022500083E08A8F8E89807F83608E8BCE01459646 +:102260006FE171E00E94760ABC01CE010E9442112A +:10227000CE010E948010DF91CF911F910F91FF90AE +:10228000EF9008951F93CF93DF93EC01DB0120E0E3 +:1022900011968C9111978770833009F421E08E8517 +:1022A0008F3F59F4222329F015968C91159783308E +:1022B00021F0CE010E94D1102CC08A89882331F1EF +:1022C00014969C911497CF58DF4FE881F981C1573C +:1022D000D040E90FF11D6081862F81508E3F30F490 +:1022E000992321F0CE01AD0120E00DC0222371F031 +:1022F00015968C911597843049F49F3F39F4CE019F +:102300006FEFAD0121E00E949C0C27C08C898823CF +:1023100011F080E022C0CE016F85AD0120E00E9467 +:102320009C0C182F882329F011E0C059DF4F188228 +:1023300013C08B89882351F0C059DF4F8881C05763 +:10234000D040863018F0CE010E94A110FE01E05965 +:10235000FF4F80818F5F8083812FDF91CF911F910D +:102360000895FF920F931F93CF93DF93EC018A019F +:10237000F22E61157105E1F08E858F8B198E8FEF2E +:102380008B8F8BE08A8F30E02170307053E0220F0A +:10239000331F5A95E1F78E89807F8360822B8E8B65 +:1023A000CE0145960E94760ABC01CE010E944211E0 +:1023B00001151105F1F08E858F8B198E8FEF8B8FA4 +:1023C0008CE08A8F2F2D30E02170307043E0220F97 +:1023D000331F4A95E1F78E89807F8360822B8E8B35 +:1023E000CE014596B8010E94760ABC01CE010E943A +:1023F0004211DF91CF911F910F91FF9008950F939C +:102400001F938C01FC018685878B118E638F428F11 +:1024100030E02170307063E0220F331F6A95E1F7DE +:102420008689807F822B868BC801459660E271E0A9 +:102430000E94760ABC01C8010E9442111F910F91AF +:102440000895DC01FB011E968C911E97828350E05B +:102450004170507083E0440F551F8A95E1F78181E8 +:10246000807F8160842B8183CD010E944211811184 +:1024700081E008950F931F938C0180E091E00E940A +:102480000B230F581F4FF8018083918363E070E0A6 +:1024900040E051E023E13BE00E9454031F910F9183 +:1024A0000895EF92FF920F931F93CF93DF93EC0168 +:1024B000E62EF42E8901C059DF4F1882C057D04054 +:1024C000CE010E948718CE010E946C14882389F4E3 +:1024D00000D000D0EDB7FEB7D283C18380E291E097 +:1024E000948383830E941E0B0F900F900F900F9088 +:1024F000FFCFCE0161E00E94E015CE0162E040E036 +:102500000E94FE16CE010E942A17CE016F2D0E9456 +:102510001116CE016E2D0E94E516CE01B8010E9463 +:10252000B416CE0165E04FE00E94D615CE0162E000 +:1025300070E00E949916CE010E944617CE0162E01B +:10254000EE24EA942CEFF22E01EE18EA28EA30E0AD +:1025500040E050E00E946517DF91CF911F910F91ED +:10256000FF90EF9008958F929F92AF92BF92CF927B +:10257000DF92EF92FF920F931F93CF93DF93EC01C3 +:102580004B01D42E122F84E692E040E052EC61E041 +:1025900070E026E00E9443211C8A1A8A112311F060 +:1025A00081E08A8BCB58DF4F99828882C557D04013 +:1025B0008A89882319F0CE010E943A12CE01B70110 +:1025C0004C2D95010E945112CE010E9660E070E0F4 +:1025D00043E050E023E13BE00E945403CE014196EA +:1025E00063E271E041E050E023E13BE00E945403EC +:1025F00089898F3F11F481E0898B0F3F19F00F8794 +:102600001B8A02C081E08B8B8FEFD812DE868B890C +:10261000882331F08F858F3F19F4CE010E94A110DD +:102620008E858F3F19F4CE010E94D1108A898823AC +:1026300019F420E331E002C027E331E0ADB7BEB7C3 +:1026400018970FB6F894BEBF0FBEADBFEDB7FEB77B +:1026500031961296DC93CE9311978DE291E093839D +:102660008283358324838E85868317820E941E0B86 +:10267000DC2ECD2E8DB79EB708960FB6F8949EBF70 +:102680000FBE8DBF8E85282F30E040E050E060E027 +:1026900070E080E090E0E22E01E03D3F08F400E0D1 +:1026A00011E0423E08F410E00F511060E1E0593AA9 +:1026B00008F4E0E01855F1E0183A08F0F0E0EF2BEC +:1026C000F1E0693A08F4F0E0BE2FB855E1E0B83A1D +:1026D00008F0E0E0FE2BA1E0772309F4A0E0E1E0C0 +:1026E000882309F4E0E08D2D9C2D60E04CEFF42E62 +:1026F0002B2F3F2F4A2F5E2F0E9465178E85282F84 +:1027000030E040E050E060E070E080E090E0E22EF9 +:1027100001E03D3F08F400E011E0423E08F410E023 +:102720000F511060E1E0593A08F4E0E01855F1E08B +:10273000183A08F0F0E0EF2BF1E0693A08F4F0E025 +:10274000BE2FB855E1E0B83A08F0E0E0FE2BA1E07A +:10275000772309F4A0E0E1E0882309F4E0E08D2D7F +:102760009C2D61E03CEFF32E2B2F3F2F4A2F5E2F45 +:102770000E9465178A89882311F441E101C042E172 +:10278000CE016FEF20E00E94FF118E858F8B198E96 +:102790008FEF8B8F86E08A8F8E89807F83608E8B10 +:1027A000CE0145966F850E943B0ABC01CE010E9476 +:1027B0004211CE010E948010DF91CF911F910F91A5 +:1027C000FF90EF90DF90CF90BF90AF909F908F9051 +:1027D00008950F931F938C010E945B14C8014596C6 +:1027E0000E94300AC801C6960E94300A1F910F91BC +:1027F00008954115510529F480917A008F778093CF +:102800007A006A307105B9F063FD02C0E8E001C0EA +:10281000E8E2862F8770E82B88E190E00FB6F89405 +:10282000A895809360000FBEE093600080916000E7 +:10283000806480936000232BA1F483B7817F846040 +:1028400083BFF89483B7816083BF85B7806685BFF7 +:102850008F7D85BF7894889583B78E7F83BF78946A +:102860000EC083B7817F846083BFF89483B7816093 +:1028700083BF7894889583B78E7F83BF7894452BE8 +:1028800029F480917A00806880937A0008951F92DD +:102890000F920FB60F9211248F9388E10FB6F89420 +:1028A00080936000109260000FBE8F910F900FBE5A +:1028B0000F901F901895FC01608341831282138250 +:1028C00080E2848315821682178210861186128612 +:1028D0001386148615860895FC0183810895FF925E +:1028E0000F931F938C01F62E81E00E94D91880E08F +:1028F0000E94E21884E00E94E718F80181816F2DA0 +:102900000E94BF201F910F91FF900895EF92FF92B8 +:102910000F931F93CF93DF93EC01162F042FF52E07 +:10292000E22E60E00E946F141770186A1EBD0DB48D +:1029300007FEFDCF802F9F2D9C01F9012E2D213206 +:1029400008F020E28EB508C080818EBD0DB407FE70 +:10295000FDCF31968EB521502223B1F7CE0161E033 +:102960000E946F14DF91CF911F910F91FF90EF9014 +:102970000895CF93DF93EC0160E00E946F1480E52F +:102980008EBD0DB407FEFDCF8EB583E78EBD0DB4B1 +:1029900007FEFDCF8EB5CE0161E00E946F14DF917E +:1029A000CF9108951F93CF93DF93EC01162F60E032 +:1029B0000E946F141EBD0DB407FEFDCF1EB5CE01E3 +:1029C00061E00E946F14812FDF91CF911F910895D4 +:1029D0006FEF0E94D214089561EE0E94D214089500 +:1029E00062EE0E94D21408951F93CF93DF93EC01FF +:1029F00060E00E946F1480E68EBD0DB407FEFDCF2F +:102A00008EB58FEF8EBD0DB407FEFDCF1EB5CE0186 +:102A100061E00E946F14113220F0CE010E94F01488 +:102A200010E0812FDF91CF911F910895EF92FF92D7 +:102A30000F931F93CF93DF93EC01F62EE72E9C812B +:102A4000142F941708F4192F8D81882311F000E0BA +:102A500002C0092F011BCE0160E00E946F1481E6C5 +:102A60008EBD0DB407FEFDCF8F2D9E2D9C01F9016B +:102A7000FEB49FEF07C09EBD0DB407FEFDCF8EB51F +:102A8000819311501123B9F707C08EBD0DB407FE15 +:102A9000FDCF9EB5015001C08FEF0023B1F7CE01ED +:102AA00061E00E946F148F2DDF91CF911F910F91E4 +:102AB000FF90EF900895DF92EF92FF920F931F9394 +:102AC000CF93DF93EC01E62ED72EF22E9C81142FAC +:102AD000941708F4192F8D81882311F000E002C0AB +:102AE000092F011BCE0160E00E946F14FEBC0DB4E3 +:102AF00007FEFDCF8E2D9D2D9C01F901FEB408C06F +:102B000080818EBD0DB407FEFDCF31968EB511507C +:102B10001123B1F706C01EBC0DB407FEFDCF8EB564 +:102B200001500023C1F7CE0161E00E946F148F2D88 +:102B3000DF91CF911F910F91FF90EF90DF9008955B +:102B4000CF93DF93EC01222311F420EA01C020EBA4 +:102B5000CE010E945B15888161E00E94BF208881C0 +:102B600060E00E94BF20DF91CF9108950F931F93E3 +:102B7000CF93DF93EC01162F042F60E00E946F14B7 +:102B80001F7110621EBD0DB407FEFDCF1EB50EBD38 +:102B90000DB407FEFDCF8EB5CE0161E00E946F142B +:102BA000812FDF91CF911F910F910895262F4F70A4 +:102BB0002295207F242B64E0422F0E94B6150895B1 +:102BC000662319F061E04FE302C061E040E00E943B +:102BD000B61508950F931F938C010E941615C80116 +:102BE00067E040E70E94B6151F910F910895CF93BB +:102BF000DF93EC010E94A015CE010E94E8148073BF +:102C0000D9F3CE0167E040E70E94B61584FD02C00B +:102C100081E004C0CE010E94EC1480E0DF91CF91EE +:102C20000895462F603808F04FE765E00E94B6151A +:102C30000895EF92FF920F931F93CF93DF937C0140 +:102C4000162FEA01022F60E00E946F141F711062BC +:102C50001EBD0DB407FEFDCF1EB508C088818EBD18 +:102C60000DB407FEFDCF21968EB501500023B1F7BC +:102C7000C70161E00E946F14812FDF91CF911F91F6 +:102C80000F91FF90EF900895EF92FF920F931F9393 +:102C9000DF93CF93CDB7DEB728970FB6F894DEBF9A +:102CA0000FBECDBF7C0109831A832B833C834D83E8 +:102CB0005E836F8378878E010F5F1F4F6AE0A801E4 +:102CC00025E00E941916C70160E1A80125E00E94D5 +:102CD0001916C70161E1F70144810E94B6152896D3 +:102CE0000FB6F894DEBF0FBECDBFCF91DF911F911D +:102CF0000F91FF90EF9008951F93CF93DF93EC0116 +:102D0000162F60E00E946F141F711EBD0DB407FEE8 +:102D1000FDCF8EB58FEF8EBD0DB407FEFDCF1EB576 +:102D2000CE0161E00E946F14812FDF91CF911F913E +:102D300008950F931F93CF93DF938C01EB0160E015 +:102D40000E947C16482F437F209729F0219711F489 +:102D5000486001C04C60C80160E00E94B615DF9178 +:102D6000CF911F910F910895FF920F931F93CF93CF +:102D7000DF93EC018B0166E00E947C161A82F7ED6E +:102D8000FF2EF8220230110511F480E206C0013056 +:102D9000110529F481E08A8388E0F82A01C01A82AB +:102DA000CE0166E04F2D0E94B615CE0166E00E946E +:102DB0007C168F1511F481E002C01A8280E0DF9149 +:102DC000CF911F910F91FF9008951F93CF93DF93A1 +:102DD000EC01162F66E00E947C16143010F04CE0D7 +:102DE00003C0412F440F4F5F887F482BCE0166E020 +:102DF0000E94B615DF91CF911F9108950F931F93F5 +:102E0000CF93DF93EC01162F042F6730F0F461E0CD +:102E10000E947C16282F002351F081E090E002C030 +:102E2000880F991F1A95E2F7422F482B0AC081E0BC +:102E300090E002C0880F991F1A95E2F7482F40953D +:102E40004223CE0161E00E94B615DF91CF911F9120 +:102E50000F9108950F931F938C010E94B914C8011C +:102E60006DE10E947C16482F4660C8016DE10E940A +:102E7000B615C8016CE10E947C16482F4360C8015A +:102E80006CE10E94B6151F910F9108950F931F9347 +:102E90008C010E94B914C8016DE10E947C16482F74 +:102EA0004460C8016DE10E94B615C8016CE10E9442 +:102EB0007C16482F4F63C8016CE10E94B61581E073 +:102EC000F80185831F910F910895BF92CF92DF92F1 +:102ED000EF92FF920F931F93DF93CF93CDB7DEB79F +:102EE00028970FB6F894DEBF0FBECDBF6C01B62E8B +:102EF000E982FA820B831C832D833E834F8358879C +:102F0000662389F4F601E6828A8187838B81808734 +:102F10008C8181878D8182878E8183878F81848751 +:102F20008885858707C0F6E0F61708F43DC081E084 +:102F3000681761F4EB2DF0E0EA5BFE4FE491C60107 +:102F40006E2FAE014F5F5F4F25E00BC0E62FF0E024 +:102F5000EA5BFE4FE491C6016E2FAE014F5F5F4FFB +:102F600021E00E9419160B2D10E0F801E45BFE4FE2 +:102F7000E491C6016E2FF60144810E94B615C60188 +:102F800062E00E947C16005C1E4FF801649121E013 +:102F900030E002C0220F331F6A95E2F7482F422B20 +:102FA000C60162E00E94B61528960FB6F894DEBFFF +:102FB0000FBECDBFCF91DF911F910F91FF90EF908A +:102FC000DF90CF90BF9008950F931F93CF93DF931F +:102FD000EC018B0167E10E947C1680FF02C080E05B +:102FE0000DC00115110549F0CE010E94E81490E0D2 +:102FF000959587958770F801808381E0DF91CF9167 +:103000001F910F910895CF93DF93EC0160E00E9430 +:103010007C1681FD10C0CE0160E00E947C16482F16 +:103020004260CE0160E00E94B61565E070E080E08D +:1030300090E00E941820DF91CF9108950F931F9385 +:103040008C01FC01808160E00E94BF20C80160E02B +:103050000E947C16482F4D7FC80160E00E94B61583 +:103060001F910F9108950F931F938C01FC01808194 +:1030700060E00E94BF20C8010E94EC14C8010E94B9 +:10308000F014C80160E00E947C16482F4E7FC801F2 +:1030900060E00E94B6151F910F910895CF93DF93C2 +:1030A000EC010E940318CE0160E00E947C16482FBC +:1030B0004160CE0160E00E94B615CE0167E040E7B6 +:1030C0000E94B6158E819F81892B9885892B9985C1 +:1030D000892B9A85892B9B85892B9C85892B9D859E +:1030E000892B41F0AE014A5F5F4FCE016AE025E0D7 +:1030F0000E941916CE010E94F014CE010E94EC1419 +:10310000888161E00E94BF20DF91CF910895CF9325 +:10311000DF93EC01888161E00E948020898161E079 +:103120000E9480200E94F818888160E00E94BF20E1 +:10313000CE0161E00E946F1465E070E080E090E0F5 +:103140000E941820CE0165E04FE00E94D615CE0106 +:1031500062E070E00E94B416882311F081E08B8356 +:10316000CE0160E070E00E94B416CE0162E070E033 +:103170000E949916CE0167E040E70E94B615CE0185 +:103180006CE40E941116CE010E94F014CE010E9440 +:10319000EC14CE010E940318CE0160E00E947C1660 +:1031A000482F4E7FCE0160E00E94B615DF91CF918F +:1031B0000895882319F48CB5806202C08CB58F7D88 +:1031C0008CBD08959CB5937F982B9CBD08952CB51C +:1031D000382F33702C7F322B3CBD2DB590E0959568 +:1031E00087959595879581702E7F822B8DBD08954B +:1031F0008AE061E00E94BF208AE061E00E948020B6 +:103200008CB580618CBD8CB580648CBD8DE061E037 +:103210000E9480208BE061E00E9480200895FC01E4 +:1032200087ED99E1918B808B758364831382108283 +:103230008FE190E0928381830895FC01838108955A +:103240006F927F928F929F92AF92BF92CF92DF92B6 +:10325000EF92FF920F931F93CF93DF93FB01EA014D +:10326000998180E0288130E0282B392BE081E232FF +:1032700031F0E83221F0E03109F084C04BC04C81DC +:103280004F3329F1403420F44F3109F07BC02FC077 +:103290004F3579F04F3709F075C0B901882777FDB0 +:1032A0008095982F0E945C2420E030E040E85DE3A8 +:1032B0002EC035952795B901882777FD8095982FE1 +:1032C0000E945C2420E030E040E05EE320C03595C1 +:1032D000279535952795B901882777FD8095982FF3 +:1032E0000E945C2420E030E040E85EE310C093E000 +:1032F000359527959A95E1F7B901882777FD80954F +:10330000982F0E945C2420E030E040E05FE30E94C0 +:103310000C2532C0EF8035952795B901882777FDB8 +:103320008095982F0E945C2420E030E040E85EE326 +:103330000E9458233B014C016E2D70E02E81621BD0 +:103340007109882777FD8095982F0E945C245B0186 +:103350006C01FF2400E010E0C801B7010E945A246C +:103360009B01AC01C601B5010E94C1239B01AC01C8 +:10337000C401B3010E9459239B01AC01832F942FF8 +:10338000752F00C0262F382F492F572FB901CA019A +:10339000DF91CF911F910F91FF90EF90DF90CF9031 +:1033A000BF90AF909F908F907F906F9008950895F9 +:1033B000EF92FF920F931F938C017B01FC0184819C +:1033C00095810E94D71CF80184819581B7010E94E4 +:1033D000B61CF8018481958164EB40E00E94711C69 +:1033E000F801848195810E94EC1BF82EF8018481FC +:1033F00095810E94D71C80E0FF2009F481E01F9195 +:103400000F91FF90EF9008950F931F938C01FC0193 +:10341000848195810E94D71CF801848195810E9446 +:10342000B11CF8018481958164E440810E94711C83 +:10343000F801818192818F33910551F08F3591058B +:1034400061F04F9779F46EE570E080E090E00EC097 +:103450006CEB70E080E090E009C067E771E080E02D +:1034600090E004C06EEE72E080E090E00E941820D0 +:103470001F910F910895CF92DF92EF92FF920F93D9 +:103480001F938C016B017A01FC01848195810E945C +:10349000D71CF80184819581B6010E94B61CF80101 +:1034A000848195816EEB40E00E94711CF80184815B +:1034B00095810E94201CF7018083F8018481958109 +:1034C0000E94201CF7018183F801848195810E946C +:1034D000201CF7018283F801848195810E94201CC1 +:1034E000F7018383F801848195810E94201CF701F4 +:1034F0008483F801848195810E94201CF7018583D3 +:10350000F801848195810E94201CF7018683F801CF +:10351000848195810E94201CF7018783F8018481B2 +:1035200095810E94201CF7018087F8018481958194 +:103530000E94D71C1F910F91FF90EF90DF90CF90CA +:1035400008950F931F938A010E943B1AC80168E0F7 +:103550000E94DB1B20E0F8019085891709F421E027 +:10356000822F1F910F910895CF92DF92EF92FF92D9 +:103570000F931F93DF93CF93CDB7DEB729970FB685 +:10358000F894DEBF0FBECDBF8C017B016E010894A5 +:10359000C11CD11CA6010E94A11A882329F460E055 +:1035A00070E08EEF92EC05C0C801B701A6010E9441 +:1035B000201929960FB6F894DEBF0FBECDBFCF916C +:1035C000DF911F910F91FF90EF90DF90CF900895C2 +:1035D0000F931F938B01CB0167E00E94DB1B20E060 +:1035E000F8019781891709F421E0822F1F910F912B +:1035F0000895EF92FF920F931F93CF93DF93EC0107 +:103600007B01042F8C819D810E94CE1B10E007C09E +:10361000CE01B7010E94E81A8823A9F00AC08C8164 +:103620009D81B7010E94221D882329F0101759F4AB +:10363000EFCF81E001C080E0DF91CF911F910F912A +:10364000FF90EF9008951F5F0117A8F3E8CFEF9266 +:10365000FF920F931F93DF93CF93CDB7DEB72897D9 +:103660000FB6F894DEBF0FBECDBF7C01462F8E0192 +:103670000F5F1F4FB8010E94F91AC701B8010E94DD +:10368000B41A28960FB6F894DEBF0FBECDBFCF9107 +:10369000DF911F910F91FF90EF9008950E94271BDB +:1036A00026E636E646EE5FE30E940C2520E030E099 +:1036B00040E052E40E94592308959F92AF92BF9236 +:1036C000CF92DF92EF92FF920F931F93DF93CF93EE +:1036D000CDB7DEB761970FB6F894DEBF0FBECDBF92 +:1036E0008C01FC01848195810E94CE1BF80113821C +:1036F0007E010894E11CF11CE9E0AE2EB12CAC0E69 +:10370000BD1E7FE7C72ED12C992493942DC0C801EC +:10371000B7010E94E81A882339F1F80180818823D3 +:1037200041F4C801B7010E94D819882311F0F801AB +:103730009082C801B701A5010E943B1A898180319E +:1037400021F4F801D282C1820BC08D85282F30E090 +:10375000F801818192818217930714F432832183C7 +:10376000F80183818F5F8383F80184819581B7019C +:103770000E94221D882359F661960FB6F894DEBF89 +:103780000FBECDBFCF91DF911F910F91FF90EF90B2 +:10379000DF90CF90BF90AF909F900895FC0113866B +:1037A0001586148687E090E012863197009711F015 +:1037B0000197FACF0895DC0180E00BC0E82FF0E01C +:1037C0008D9190E0E827F927ED5FFC4FE49161507F +:1037D0008E2F662399F70895DF92EF92FF920F9351 +:1037E0001F93FC01E08001811281F894F80181812E +:1037F0008E2981838281DE2CD0948D21828383E087 +:1038000090E00E94D31FF80181818D2181838AE09D +:1038100090E00E94D31FF8010081789485E390E046 +:103820000E94D31F80E010E0FF240E211F21012BF6 +:1038300009F081E01F910F91FF90EF90DF900895C4 +:10384000EF92FF920F931F93CF93DF937C0101E0E0 +:1038500010E0C0E0D0E0C7010E94EC1B8111102BEA +:10386000000F2196C830D105B1F7812FDF91CF919C +:103870001F910F91FF90EF9008951F93CF93DF93C7 +:10388000FC011081C181D281912F909560FF12C0FF +:10389000F8948A8189238A838981812B89838AE0AC +:1038A00090E00E94D31F8A81812B8A83789487E3DA +:1038B00090E011C0F8948A8189238A838981812BC1 +:1038C000898381E490E00E94D31F8A81812B8A83BF +:1038D000789485E090E00E94D31FDF91CF911F91F3 +:1038E0000895CF92DF92EF92FF920F931F93CF93A1 +:1038F000DF938C01E42EFF24F394C0E0D0E0C62EC9 +:10390000DD2460E08F2D90E08C219D21892B09F032 +:1039100061E0C8010E943D1CFF0C2196C830D10512 +:1039200081F7EE20A9F4F894D8011196ED91FC915D +:10393000129791818C918095982391831196ED91A6 +:10394000FC91129792818C91809598239283789420 +:10395000DF91CF911F910F91FF90EF90DF90CF906B +:1039600008956CEC40E00E94711C0895EF92FF9264 +:103970000F931F93CF93DF938C017B0165E540E0AC +:103980000E94711CC0E0D0E0F701EC0FFD1FC801E0 +:10399000608140E00E94711C2196C830D105A1F7DA +:1039A000DF91CF911F910F91FF90EF900895DF92DB +:1039B000EF92FF920F931F93CF93DF93FC0100814F +:1039C000C181D281F8948981D02ED0948D218983B0 +:1039D00078941DE7E02EFF24115059F182E090E029 +:1039E0000E94D31F888190E08E219F21892BA1F313 +:1039F000F8948A818D218A838981802B89837894A8 +:103A000084EF91E00E94D31FF89489818D218983EE +:103A100080E590E00E94D31F888110E090E0E822CA +:103A2000F922EF2809F411E0789484EA91E00E94E9 +:103A3000D31F812FDF91CF911F910F91FF90EF90B6 +:103A4000DF900895AF92BF92DF92EF92FF920F93B3 +:103A50001F93CF93DF93EC015B018D85882309F0E1 +:103A600061C0CE010E94D71C882329F41B861D86C5 +:103A70001C8690E06BC0CE0160EF40E00E94711C9C +:103A800001E0DD24EE24FF24F394CE010E94EC1B20 +:103A9000182FCE010E94EC1B113019F48130E1F493 +:103AA00039C0181709F58B85081770F460E0FE011E +:103AB000EE0DF11D838190E02F2D30E082239323C2 +:103AC000181619064CF003C060E0081729F00930F9 +:103AD00008F04CC00C874AC0FE01EE0DF11D838139 +:103AE0008F29838361E009C0612FFE01EE0DF11D76 +:103AF0009F2D9095838189238383CE010E943D1C55 +:103B00000F5FFF0C19F4E394FF24F39487E08E1504 +:103B100008F0BBCF013430F0DB86DD2011F581E009 +:103B20008D871FC01B861D861C8690E0DE0120E06D +:103B300030E0F501E20FF31F13968C911397808309 +:103B40002F5F3F4F11962830310599F7892FDF916C +:103B5000CF911F910F91FF90EF90DF90BF90AF90AA +:103B600008958B818823F1F291E0E0CFD02EBDCF74 +:103B7000FF920F931F938C01F62E862F60E00E9418 +:103B800080208F2D90E0FC01EB5CFB4FE491D8018D +:103B9000EC938F5D9B4FFC016491E62FF0E0EE0FFC +:103BA000FF1FE95EFB4F8591949112969C938E9333 +:103BB00011971B961C921B971D961C921D971C9685 +:103BC0001C92F80187E090E012863197009711F07F +:103BD0000197FACF1F910F91FF9008950F931F93B4 +:103BE000CF93DF93BC018230910510F462E070E066 +:103BF000E0910303F091040340E050E0A0E0B0E066 +:103C000024C08081918186179707D0F08617970787 +:103C100071F482819381109729F013969C938E936F +:103C200012972CC0909304038093030327C041157F +:103C3000510519F08417950718F48D01AC01EF01B7 +:103C4000DF01828193819C01F9013097D1F6411502 +:103C50005105F9F0CA01861B970B8430910580F459 +:103C60008A819B810115110521F0F80193838283DC +:103C700004C09093040380930303FE01329645C071 +:103C8000FE01E80FF91F619371930297998388836E +:103C90003CC08091010390910203892B41F48091F3 +:103CA0004801909149019093020380930103409150 +:103CB0004A0150914B014115510541F44DB75EB792 +:103CC0008091460190914701481B590B20910103B7 +:103CD0003091020324173507B0F4CA01821B930BFD +:103CE0008617970780F0AB014E5F5F4F84179507EB +:103CF00050F0420F531F5093020340930103F90108 +:103D00006193719302C0E0E0F0E0CF01DF91CF91C9 +:103D10001F910F910895CF93DF939C01009709F4B1 +:103D20008FC0EC0122971B821A826091030370916D +:103D300004036115710581F488819981820F931FB5 +:103D400020910103309102032817390739F5D093E8 +:103D50000203C093010374C0DB0140E050E0AC17E4 +:103D6000BD0708F1BB83AA83FE0121913191E20FC7 +:103D7000F31FAE17BF0779F48D919C911197280F0F +:103D8000391F2E5F3F4F3983288312968D919C9166 +:103D900013979B838A834115510571F4D0930403D3 +:103DA000C09303034DC012968D919C911397AD0162 +:103DB000009711F0DC01D3CFDA011396DC93CE9398 +:103DC0001297FA0121913191E20FF31FCE17DF070D +:103DD00069F488819981280F391F2E5F3F4FFA01BE +:103DE000318320838A819B8193838283E0E0F0E0AA +:103DF000DB0112968D919C911397009719F0BC01ED +:103E0000FD01F6CFAB014E5F5F4FDB018D919C91C1 +:103E1000840F951F20910103309102032817390761 +:103E200079F4309729F4109204031092030302C02E +:103E30001382128242505040509302034093010378 +:103E4000DF91CF9108951F920F920FB60F92112418 +:103E50002F933F934F935F936F937F938F939F9392 +:103E6000AF93BF93EF93FF938091570290915802C5 +:103E7000892B29F0E0915702F09158020995FF91A2 +:103E8000EF91BF91AF919F918F917F916F915F91D2 +:103E90004F913F912F910F900FBE0F901F9018954B +:103EA0001F920F920FB60F9211242F933F934F93AF +:103EB0005F936F937F938F939F93AF93BF93EF9392 +:103EC000FF938091590290915A02892B29F0E09139 +:103ED0005902F0915A020995FF91EF91BF91AF916C +:103EE0009F918F917F916F915F914F913F912F9112 +:103EF0000F900FBE0F901F9018951F920F920FB644 +:103F00000F9211242F933F938F939F93AF93BF935F +:103F100080915F0290916002A0916102B091620273 +:103F2000309163020196A11DB11D232F2D5F2D3706 +:103F300020F02D570196A11DB11D2093630280939F +:103F40005F0290936002A0936102B093620280913D +:103F50005B0290915C02A0915D02B0915E020196BD +:103F6000A11DB11D80935B0290935C02A0935D0242 +:103F7000B0935E02BF91AF919F918F913F912F912E +:103F80000F900FBE0F901F9018958FB7F894209147 +:103F90005F023091600240916102509162028FBFD6 +:103FA000B901CA010895019739F0880F991F880F48 +:103FB000991F02970197F1F70895789484B582606C +:103FC00084BD84B5816084BD85B5826085BD85B5BD +:103FD000816085BDEEE6F0E0808181608083E1E86C +:103FE000F0E01082808182608083808181608083A4 +:103FF000E0E8F0E0808181608083E1EBF0E08081A7 +:1040000084608083E0EBF0E0808181608083EAE778 +:10401000F0E0808184608083808182608083808101 +:10402000816080838081806880831092C1000895C0 +:10403000EF92FF920F931F93CF93DF937B018C013D +:104040003FB7F89480915B0290915C02A0915D0271 +:10405000B0915E0226B5A89B05C02F3F19F00196CE +:10406000A11DB11D3FBFBA2FA92F982F8827820FFE +:10407000911DA11DB11D52E0880F991FAA1FBB1FE2 +:104080005A95D1F7EC0130C00E9457233FB7F894FE +:1040900080915B0290915C02A0915D02B0915E0202 +:1040A00026B5A89B05C02F3F19F00196A11DB11D93 +:1040B0003FBFBA2FA92F982F8827820F911DA11DCE +:1040C000B11D32E0880F991FAA1FBB1F3A95D1F787 +:1040D0008C1B9D0B885E9340B8F20894E108F108B0 +:1040E00001091109C851DC4FE114F1040105110562 +:1040F00059F6DF91CF911F910F91FF90EF900895A6 +:10410000CF93DF93482F50E0CA018B5C9B4FFC019B +:1041100034914F5D5B4FFA018491882369F190E0FF +:10412000880F991FFC01ED5FFB4FA591B491FC0135 +:10413000E35FFB4FC591D491662351F42FB7F894F8 +:104140008C91932F909589238C93888189230BC020 +:10415000623061F42FB7F8948C91932F9095892356 +:104160008C938881832B88832FBF06C09FB7F894D8 +:104170008C91832B8C939FBFDF91CF910895482F13 +:1041800050E0CA01875B9B4FFC012491CA018B5C04 +:104190009B4FFC0194914F5D5B4FFA0134913323A7 +:1041A00009F440C0222351F1233071F0243028F467 +:1041B0002130A1F0223011F514C02630B1F02730A3 +:1041C000C1F02430D9F404C0809180008F7703C0FF +:1041D000809180008F7D8093800010C084B58F77A0 +:1041E00002C084B58F7D84BD09C08091B0008F77F7 +:1041F00003C08091B0008F7D8093B000E32FF0E08A +:10420000EE0FFF1FE35FFB4FA591B4912FB7F8941A +:10421000662321F48C919095892302C08C91892B7F +:104220008C932FBF0895DC015C968C915C97FD0107 +:10423000E80FF11DE35AFF4F20815C968C915C974B +:1042400090E001968F7390705C968C935C97569675 +:10425000ED91FC91579720835096ED91FC915197E9 +:104260008081806480835B969C915B975C968C9147 +:104270005C97981739F45296ED91FC915397808191 +:104280008F7D80830895DF92EF92FF920F931F93AB +:10429000CF93DF93EC017A018B01D22EE889F98963 +:1042A00082E08083403081EE580780E0680780E03C +:1042B0007807A1F060E079E08DE390E0A801970134 +:1042C0000E946F2521503040404050405695479500 +:1042D0003795279580E12030380798F0E889F989EB +:1042E000108260E874E88EE190E0A80197010E94D6 +:1042F0006F252150304040405040569547953795A6 +:104300002795EC85FD853083EE85FF852083188E0B +:10431000EC89FD89D082EA89FB8980818061808374 +:10432000EA89FB89808188608083EA89FB898081B2 +:1043300080688083EA89FB8980818F7D8083DF911B +:10434000CF911F910F91FF90EF90DF900895FC01A6 +:10435000218D828D30E0205C3F4F281B31092F7367 +:104360003070C9010895FC01918D828D981719F460 +:104370002FEF3FEF06C0828DE80FF11D858D282FAE +:1043800030E0C9010895DC0159969C9159975A96DD +:104390008C915A97981719F42FEF3FEF10C05A9647 +:1043A0008C915A97FD01E80FF11D958D5A968C91CD +:1043B0005A978F5F8F735A968C93292F30E0C901DB +:1043C0000895CF93DF93EC01888D882361F419C0A1 +:1043D0008C9185FF0AC0E889F989808185FF05C035 +:1043E000CE010E941321AA89BB898C9185FD05C04D +:1043F000E889F989808186FD04C00FB607FEE8CF01 +:10440000F4CFDF91CF9108950F931F93CF93DF9354 +:10441000EC01062F9B8D8C8D981791F5E889F98911 +:10442000808185FF2DC0EE89FF896083E889F98945 +:104430008081806480831DC00FB607FC08C0E889B6 +:10444000F989808185FF03C0CE010E9413218C8DE4 +:10445000181791F38B8DFE01E80FF11DE35AFF4F02 +:1044600000831B8FEA89FB8980818062808381E0E1 +:10447000888F81E090E0DF91CF911F910F91089597 +:104480001B8D1F5F1F73E3CF8CEC92E2892B51F0E1 +:1044900080E090E0892B31F00E94CC22882311F03B +:1044A0000E94000008951F920F920FB60F921124E0 +:1044B0002F938F939F93EF93FF93E0917402F0916A +:1044C0007502808182FD16C0E0917A02F0917B0234 +:1044D000208190917D029F5F9F7380917E0298174B +:1044E00071F0E0917D02F0E0EC59FD4F258F909343 +:1044F0007D0205C0E0917A02F0917B02E081FF919C +:10450000EF919F918F912F910F900FBE0F901F9061 +:104510001895109267021092660288EE93E0A0E070 +:10452000B0E08093680290936902A0936A02B0930E +:104530006B0280E591E0909365028093640285ECC4 +:1045400090E0909371028093700284EC90E09093DD +:1045500073028093720280EC90E0909375028093D6 +:10456000740281EC90E0909377028093760282EC63 +:1045700090E0909379028093780286EC90E090939B +:104580007B0280937A0210927D0210927E0210923A +:104590007F0210928002089584E692E00E94A72193 +:1045A00020E0892B09F021E0822F08951F920F92BD +:1045B0000FB60F9211242F933F934F935F936F93F6 +:1045C0007F938F939F93AF93BF93EF93FF9384E673 +:1045D00092E00E941321FF91EF91BF91AF919F91C3 +:1045E0008F917F916F915F914F913F912F910F909C +:1045F0000FBE0F901F901895CF93DF930E94DD1F81 +:104600000E94E409C4E4D2E20E945B092097E1F32E +:104610000E944422F9CF0E94EE1D0895CF92DF92AE +:10462000EF92FF920F931F93CF93DF937C016B0167 +:104630008A01C0E0D0E00FC0D6016D916D01D701B5 +:10464000ED91FC910190F081E02DC7010995C80F13 +:10465000D91F015010400115110571F7CE01DF91EE +:10466000CF911F910F91FF90EF90DF90CF90089521 +:10467000CF93DF93EC016115710519F420E030E070 +:104680000FC0DB010D900020E9F71197A61BB70BB7 +:10469000E881F9810280F381E02DAD0109959C014B +:1046A000C901DF91CF9108950E943823089508959C +:1046B0005058BB27AA270ED075C166D130F06BD1F8 +:1046C00020F031F49F3F11F41EF45BC10EF4E0952D +:1046D000E7FB51C1E92F77D180F3BA17620773075F +:1046E0008407950718F071F49EF58FC10EF4E095DC +:1046F0000B2EBA2FA02D0B01B90190010C01CA019C +:10470000A0011124FF27591B99F0593F50F4503E46 +:1047100068F11A16F040A22F232F342F4427585F38 +:10472000F3CF469537952795A795F0405395C9F750 +:104730007EF41F16BA0B620B730B840BBAF0915008 +:10474000A1F0FF0FBB1F661F771F881FC2F70EC0A7 +:10475000BA0F621F731F841F48F48795779567957A +:10476000B795F7959E3F08F0B3CF9395880F08F063 +:104770009927EE0F979587950895D9D008F481E091 +:1047800008950CD00FC107D140F0FED030F021F4D5 +:104790005F3F19F0F0C0511139C1F3C014D198F343 +:1047A0009923C9F35523B1F3951B550BBB27AA27B2 +:1047B00062177307840738F09F5F5F4F220F331F24 +:1047C000441FAA1FA9F333D00E2E3AF0E0E830D0F0 +:1047D00091505040E695001CCAF729D0FE2F27D0F3 +:1047E000660F771F881FBB1F261737074807AB07C1 +:1047F000B0E809F0BB0B802DBF01FF2793585F4F36 +:104800002AF09E3F510568F0B6C000C15F3FECF34F +:10481000983EDCF3869577956795B795F7959F5FFA +:10482000C9F7880F911D9695879597F90895E1E04E +:10483000660F771F881FBB1F621773078407BA07AD +:1048400020F0621B730B840BBA0BEE1F88F7E09508 +:10485000089504D06894B111D9C00895BCD088F0EF +:104860009F5790F0B92F9927B751A0F0D1F0660F5C +:10487000771F881F991F1AF0BA95C9F712C0B13077 +:1048800081F0C3D0B1E00895C0C0672F782F88278A +:10489000B85F39F0B93FCCF3869577956795B395B6 +:1048A000D9F73EF490958095709561957F4F8F4F25 +:1048B0009F4F0895E89409C097FB3EF4909580952A +:1048C000709561957F4F8F4F9F4F9923A9F0F92FD6 +:1048D00096E9BB279395F695879577956795B79554 +:1048E000F111F8CFFAF4BB0F11F460FF1BC06F5F3A +:1048F0007F4F8F4F9F4F16C0882311F096E911C04C +:10490000772321F09EE8872F762F05C0662371F06C +:1049100096E8862F70E060E02AF09A95660F771F80 +:10492000881FDAF7880F9695879597F90895990F5C +:104930000008550FAA0BE0E8FEEF16161706E80769 +:10494000F907C0F012161306E407F50798F0621B8A +:10495000730B840B950B39F40A2661F0232B242B5F +:10496000252B21F408950A2609F4A140A6958FEF7E +:10497000811D811D089597F99F6780E870E060E0D0 +:1049800008959FEF80EC089500240A9416161706E8 +:1049900018060906089500240A9412161306140630 +:1049A00005060895092E0394000C11F4882352F093 +:1049B000BB0F40F4BF2B11F460FF04C06F5F7F4F4B +:1049C0008F4F9F4F089557FD9058440F551F59F032 +:1049D0005F3F71F04795880F97FB991F61F09F3FEC +:1049E00079F087950895121613061406551FF2CF15 +:1049F0004695F1DF08C0161617061806991FF1CF65 +:104A000086957105610508940895E894BB2766278B +:104A10007727CB0197F908950BD0C4CFB5DF28F0E5 +:104A2000BADF18F0952309F0A6CFABCF1124EECF53 +:104A3000CADFA0F3959FD1F3950F50E0551F629FF9 +:104A4000F001729FBB27F00DB11D639FAA27F00DE7 +:104A5000B11DAA1F649F6627B00DA11D661F829F0E +:104A60002227B00DA11D621F739FB00DA11D621FF3 +:104A7000839FA00D611D221F749F3327A00D611D10 +:104A8000231F849F600D211D822F762F6A2F1124F2 +:104A90009F5750408AF0E1F088234AF0EE0FFF1F45 +:104AA000BB1F661F771F881F91505040A9F79E3F7C +:104AB000510570F060CFAACF5F3FECF3983EDCF376 +:104AC000869577956795B795F795E7959F5FC1F7B9 +:104AD000FE2B880F911D9695879597F90895A1E271 +:104AE0001A2EAA1BBB1BFD010DC0AA1FBB1FEE1F68 +:104AF000FF1FA217B307E407F50720F0A21BB30BB3 +:104B0000E40BF50B661F771F881F991F1A9469F72E +:104B100060957095809590959B01AC01BD01CF018A +:104B20000895EE0FFF1F0590F491E02D0994AA1B44 +:104B3000BB1B51E107C0AA1FBB1FA617B70710F088 +:104B4000A61BB70B881F991F5A95A9F780959095BA +:104B5000BC01CD0108952F923F924F925F926F92C8 +:104B60007F928F929F92AF92BF92CF92DF92EF92FD +:104B7000FF920F931F93CF93DF93CDB7DEB7CA1B7E +:104B8000DB0B0FB6F894DEBF0FBECDBF09942A88A9 +:104B9000398848885F846E847D848C849B84AA8451 +:104BA000B984C884DF80EE80FD800C811B81AA81DE +:104BB000B981CE0FD11D0FB6F894DEBF0FBECDBFA9 +:084BC000ED010895F894FFCF08 +:104BC80054656D70657261747572652053656E7396 +:104BD8006F7200312E30006661696C006F6B0000E7 +:104BE800312E346231202831383834386132290086 +:104BF80073656E736F7200726570656174657200BB +:104C0800307500000100800005030000000000006E +:0C4C180004220E23A721C321B321E121B7 +:00000001FF diff --git a/NodeJsController/NodeJsController.js b/NodeJsController/NodeJsController.js new file mode 100644 index 0000000..25a53c4 --- /dev/null +++ b/NodeJsController/NodeJsController.js @@ -0,0 +1,617 @@ +const gwType = 'Ethernet'; +const gwAddress = '10.0.1.99'; +const gwPort = 9999; + +//const gwType = 'Serial'; +//const gwPort = 'COM4'; +//const gwBaud = 115200; + +const dbAddress = '127.0.0.1'; +const dbPort = 27017; +const dbName = 'MySensorsDb'; + +const fwHexFiles = [ 'BlinkRed.hex', 'BlinkGreen.hex', 'DallasTemperatureSensor.cpp.hex' ]; +const fwDefaultType = 2; // index of hex file from array above + +const FIRMWARE_BLOCK_SIZE = 16; +const BROADCAST_ADDRESS = 255; +const NODE_SENSOR_ID = 255; + +const C_PRESENTATION = 0; +const C_SET = 1; +const C_REQ = 2; +const C_INTERNAL = 3; +const C_STREAM = 4; + +const V_TEMP = 0; +const V_HUM = 1; +const V_LIGHT = 2; +const V_DIMMER = 3; +const V_PRESSURE = 4; +const V_FORECAST = 5; +const V_RAIN = 6; +const V_RAINRATE = 7; +const V_WIND = 8; +const V_GUST = 9; +const V_DIRECTION = 10; +const V_UV = 11; +const V_WEIGHT = 12; +const V_DISTANCE = 13; +const V_IMPEDANCE = 14; +const V_ARMED = 15; +const V_TRIPPED = 16; +const V_WATT = 17; +const V_KWH = 18; +const V_SCENE_ON = 19; +const V_SCENE_OFF = 20; +const V_HEATER = 21; +const V_HEATER_SW = 22; +const V_LIGHT_LEVEL = 23; +const V_VAR1 = 24; +const V_VAR2 = 25; +const V_VAR3 = 26; +const V_VAR4 = 27; +const V_VAR5 = 28; +const V_UP = 29; +const V_DOWN = 30; +const V_STOP = 31; +const V_IR_SEND = 32; +const V_IR_RECEIVE = 33; +const V_FLOW = 34; +const V_VOLUME = 35; +const V_LOCK_STATUS = 36; + +const I_BATTERY_LEVEL = 0; +const I_TIME = 1; +const I_VERSION = 2; +const I_ID_REQUEST = 3; +const I_ID_RESPONSE = 4; +const I_INCLUSION_MODE = 5; +const I_CONFIG = 6; +const I_PING = 7; +const I_PING_ACK = 8; +const I_LOG_MESSAGE = 9; +const I_CHILDREN = 10; +const I_SKETCH_NAME = 11; +const I_SKETCH_VERSION = 12; +const I_REBOOT = 13; + +const S_DOOR = 0; +const S_MOTION = 1; +const S_SMOKE = 2; +const S_LIGHT = 3; +const S_DIMMER = 4; +const S_COVER = 5; +const S_TEMP = 6; +const S_HUM = 7; +const S_BARO = 8; +const S_WIND = 9; +const S_RAIN = 10; +const S_UV = 11; +const S_WEIGHT = 12; +const S_POWER = 13; +const S_HEATER = 14; +const S_DISTANCE = 15; +const S_LIGHT_LEVEL = 16; +const S_ARDUINO_NODE = 17; +const S_ARDUINO_REPEATER_NODE = 18; +const S_LOCK = 19; +const S_IR = 20; +const S_WATER = 21; +const S_AIR_QUALITY = 22; + +const ST_FIRMWARE_CONFIG_REQUEST = 0; +const ST_FIRMWARE_CONFIG_RESPONSE = 1; +const ST_FIRMWARE_REQUEST = 2; +const ST_FIRMWARE_RESPONSE = 3; +const ST_SOUND = 4; +const ST_IMAGE = 5; + +const P_STRING = 0; +const P_BYTE = 1; +const P_INT16 = 2; +const P_UINT16 = 3; +const P_LONG32 = 4; +const P_ULONG32 = 5; +const P_CUSTOM = 6; + +var fs = require('fs'); + +function crcUpdate(old, value) { + var c = old ^ value; + for (var i = 0; i < 8; ++i) { + if ((c & 1) > 0) + c = ((c >> 1) ^ 0xA001); + else + c = (c >> 1); + } + return c; +} + +function pullWord(arr, pos) { + return arr[pos] + 256 * arr[pos + 1]; +} + +function pushWord(arr, val) { + arr.push(val & 0x00FF); + arr.push((val >> 8) & 0x00FF); +} + +function pushDWord(arr, val) { + arr.push(val & 0x000000FF); + arr.push((val >> 8) & 0x000000FF); + arr.push((val >> 16) & 0x000000FF); + arr.push((val >> 24) & 0x000000FF); +} + +function loadFirmware(fwtype, fwversion, filename, db) { + console.log("loading firmware: " + filename); + fwdata = []; + var start = 0; + var end = 0; + var pos = 0; + var hex = fs.readFileSync(filename).toString().split("\n"); + for(l in hex) { + line = hex[l].trim(); + if (line.length > 0) { + while (line.substring(0, 1) != ":") + line = line.substring(1); + var reclen = parseInt(line.substring(1, 3), 16); + var offset = parseInt(line.substring(3, 7), 16); + var rectype = parseInt(line.substring(7, 9), 16); + var data = line.substring(9, 9 + 2 * reclen); + var chksum = parseInt(line.substring(9 + (2 * reclen), 9 + (2 * reclen) + 2), 16); + if (rectype == 0) { + if ((start == 0) && (end == 0)) { + if (offset % 128 > 0) + throw new Error("error loading hex file - offset can't be devided by 128"); + start = offset; + end = offset; + } + if (offset < end) + throw new Error("error loading hex file - offset lower than end"); + while (offset > end) { + fwdata.push(255); + pos++; + end++; + } + for (var i = 0; i < reclen; i++) { + fwdata.push(parseInt(data.substring(i * 2, (i * 2) + 2), 16)); + pos++; + } + end += reclen; + } + } + } + var pad = end % 128; // ATMega328 has 64 words per page / 128 bytes per page + for (var i = 0; i < 128 - pad; i++) { + fwdata.push(255); + pos++; + end++; + } + var blocks = (end - start) / FIRMWARE_BLOCK_SIZE; + var crc = 0xFFFF; + for (var i = 0; i < blocks * FIRMWARE_BLOCK_SIZE; ++i) { + var v = crc; + crc = crcUpdate(crc, fwdata[i]); + } + db.collection('firmware', function(err, c) { + c.update({ + 'type': fwtype, + 'version': fwversion + }, { + $set: { + 'filename': filename, + 'blocks': blocks, + 'crc': crc, + 'data': fwdata + } + }, { + upsert: true + }, function(err, result) { + if (err) + console.log('Error writing firmware to database'); + }); + }); + console.log("loading firmware done. blocks: " + blocks + " / crc: " + crc); +} + +/* +function decode(msg) { + var msgs = msg.toString().split(";"); + rsender = +msgs[0]; + rsensor = +msgs[1]; + rcommand = +msgs[2]; + rtype = +msgs[3]; + var pl = msgs[4].trim(); + rpayload = []; + for (var i = 0; i < pl.length; i+=2) { + var b = parseInt(pl.substring(i, i + 2), 16); + rpayload.push(b); + } +} +*/ + +function encode(destination, sensor, command, acknowledge, type, payload) { + var msg = destination.toString(10) + ";" + sensor.toString(10) + ";" + command.toString(10) + ";" + acknowledge.toString(10) + ";" + type.toString(10) + ";"; + if (command == 4) { + for (var i = 0; i < payload.length; i++) { + if (payload[i] < 16) + msg += "0"; + msg += payload[i].toString(16); + } + } else { + msg += payload; + } + msg += '\n'; + return msg.toString(); +} + +function saveProtocol(sender, payload, db) { + db.collection('node', function(err, c) { + c.update({ + 'id': sender + }, { + $set: { + 'protocol': payload + } + }, { + upsert: true + }, function(err, result) { + if (err) + console.log("Error writing protocol to database"); + }); + }); +} + +function saveValue(sender, sensor, type, payload, db) { + var cn = "Value-" + sender.toString() + "-" + sensor.toString(); + db.createCollection(cn, function(err, c) { + c.save({ + 'timestamp': new Date().getTime(), + 'type': type, + 'value': payload + }, function(err, result) { + if (err) + console.log("Error writing value to database"); + }); + }); +} + +function saveBatteryLevel(sender, payload, db) { + var cn = "BatteryLevel-" + sender.toString(); + db.createCollection(cn, function(err, c) { + c.save({ + 'timestamp': new Date().getTime(), + 'value': payload + }, function(err, result) { + if (err) + console.log("Error writing battery level to database"); + }); + }); +} + +function saveSketchName(sender, payload, db) { + db.collection('node', function(err, c) { + c.update({ + 'id': sender + }, { + $set: { + 'sketchName': payload + } + }, function(err, result) { + if (err) + console.log("Error writing sketch name to database"); + }); + }); +} + +function saveSketchVersion(sender, payload, db) { + db.collection('node', function(err, c) { + c.update({ + 'id': sender + }, { + $set: { + 'sketchVersion': payload + } + }, function(err, result) { + if (err) + console.log("Error writing sketch version to database"); + }); + }); +} + +function sendTime(destination, sensor, gw) { + var payload = new Date().getTime(); + var command = C_INTERNAL; + var acknowledge = 0; // no ack + var type = I_TIME; + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); +} + +function sendNextAvailableSensorId(db, gw) { + db.collection('node', function(err, c) { + c.find({ + $query: { }, + $orderby: { + 'id': 1 + } + }).toArray(function(err, results) { + if (err) + console.log('Error finding nodes'); + var id = 1; + for (var i = 0; i < results.length; i++) + if (results[i].id > i + 1) { + id = i + 1; + break; + } + if (id < 255) { + c.save({ + 'id': id + }, function(err, result) { + if (err) + console.log('Error writing node to database'); + var destination = BROADCAST_ADDRESS; + var sensor = NODE_SENSOR_ID; + var command = C_INTERNAL; + var acknowledge = 0; // no ack + var type = I_ID_RESPONSE; + var payload = id; + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); + }); + } + }); + }); +} + +function sendConfig(destination, gw) { + var payload = "M"; + var sensor = NODE_SENSOR_ID; + var command = C_INTERNAL; + var acknowledge = 0; // no ack + var type = I_CONFIG; + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); +} + +function sendFirmwareConfigResponse(destination, fwtype, fwversion, db, gw) { + // keep track of type/versin info for each node + // at the same time update the last modified date + // could be used to remove nodes not seen for a long time etc. + db.collection('node', function(err, c) { + c.update({ + 'id': destination + }, { + $set: { + 'type': fwtype, + 'version': fwversion + } + }, { + upsert: true + }, function(err, result) { + if (err) + console.log("Error writing node type and version to database"); + }); + }); + if (fwtype == 0xFFFF) { + // sensor does not know which type / blank EEPROM + // take predefined type (ideally selected in UI prior to connection of new sensor) + if (fwDefaultType == 0xFFFF) + throw new Error('No default sensor type defined'); + fwtype = fwDefaultType; + } + db.collection('firmware', function(err, c) { + c.findOne({ + $query: { + 'type': fwtype + }, + $orderby: { + 'version': -1 + } + }, function(err, result) { + if (err) + console.log('Error finding firmware for type ' + fwtype); + if (!result) + console.log('No firmware found for type ' + fwtype); + var payload = []; + pushWord(payload, result.type); + pushWord(payload, result.version); + pushWord(payload, result.blocks); + pushWord(payload, result.crc); + var sensor = NODE_SENSOR_ID; + var command = C_STREAM; + var acknowledge = 0; // no ack + var type = 1; // I_FIRMWARE_CONFIG_RESPONSE + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); + }); + }); +} + +function sendFirmwareResponse(destination, fwtype, fwversion, fwblock, db, gw) { + db.collection('firmware', function(err, c) { + c.findOne({ + 'type': fwtype, + 'version': fwversion + }, function(err, result) { + if (err) + console.log('Error finding firmware version ' + fwversion + ' for type ' + fwtype); + var payload = []; + pushWord(payload, result.type); + pushWord(payload, result.version); + pushWord(payload, fwblock); + for (var i = 0; i < FIRMWARE_BLOCK_SIZE; i++) + payload.push(result.data[fwblock * FIRMWARE_BLOCK_SIZE + i]); + var sensor = NODE_SENSOR_ID; + var command = C_STREAM; + var acknowledge = 0; // no ack + var type = ST_FIRMWARE_RESPONSE; + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); + }); + }); +} + +function rebootNode(destination, gw) { + var sensor = NODE_SENSOR_ID; + var command = C_INTERNAL; + var acknowledge = 0; // no ack + var type = I_REBOOT; + var payload = ""; + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); +} + +function rfReceived(data, db, gw) { + if ((data != null) && (data != "")) { + console.log('<- ' + data); + // decoding message + var datas = data.toString().split(";"); + var sender = +datas[0]; + var sensor = +datas[1]; + var command = +datas[2]; + var type = +datas[3]; + var rawpayload = datas[4].trim(); + var payload; + if (command == 4) { + payload = []; + for (var i = 0; i < rawpayload.length; i+=2) + payload.push(parseInt(rawpayload.substring(i, i + 2), 16)); + } else { + payload = rawpayload; + } + // decision on appropriate response + switch (command) { + case C_PRESENTATION: + saveProtocol(sender, payload, db); + break; + case C_SET: + saveValue(sender, sensor, type, payload, db); + break; + case C_REQ: + break; + case C_INTERNAL: + switch (type) { + case I_BATTERY_LEVEL: + saveBatteryLevel(sender, payload, db); + break; + case I_TIME: + sendTime(sender, sensor, gw); + break; + case I_VERSION: + break; + case I_ID_REQUEST: + sendNextAvailableSensorId(db, gw); + break; + case I_ID_RESPONSE: + break; + case I_INCLUSION_MODE: + break; + case I_CONFIG: + sendConfig(sender, gw); + break; + case I_PING: + break; + case I_PING_ACK: + break; + case I_LOG_MESSAGE: + break; + case I_CHILDREN: + break; + case I_SKETCH_NAME: + saveSketchName(sender, payload, db); + break; + case I_SKETCH_VERSION: + saveSketchVersion(sender, payload, db); + break; + case I_REBOOT: + break; + } + break; + case C_STREAM: + switch (type) { + case ST_FIRMWARE_CONFIG_REQUEST: + var fwtype = pullWord(payload, 0); + var fwversion = pullWord(payload, 2); + sendFirmwareConfigResponse(sender, fwtype, fwversion, db, gw); + break; + case ST_FIRMWARE_CONFIG_RESPONSE: + break; + case ST_FIRMWARE_REQUEST: + var fwtype = pullWord(payload, 0); + var fwversion = pullWord(payload, 2); + var fwblock = pullWord(payload, 4); + sendFirmwareResponse(sender, fwtype, fwversion, fwblock, db, gw); + break; + case ST_FIRMWARE_RESPONSE: + break; + case ST_SOUND: + break; + case ST_IMAGE: + break; + } + break; + } + } +} + +var dbc = require('mongodb').MongoClient; +dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err, db) { + if(err) { + console.log('Error connecting to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName); + return; + } + console.log('Connected to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName); + db.createCollection('node', function(err, collection) { }); + db.createCollection('firmware', function(err, collection) { }); + + // ToDo : check for new hex files / only load if new / get type and version from filename + for (var i = 0; i < hexFiles.length; i++) + loadFirmware(i, 1, hexFiles[i], db); + + var gw; + if (gwType == 'Ethernet') { + gw = require('net').Socket(); + gw.connect(gwPort, gwAddress); + gw.setEncoding('ascii'); + gw.on('connect', function() { + console.log('connected to ethernet gateway at ' + gwAddress + ":" + gwPort); + }).on('data', function(rd) { + var rds = rd.toString().split('\n'); + for (var i = 0; i < rds.length; i++) + rfReceived(rds[i].trim(), db, gw); + }).on('end', function() { + console.log('disconnected from gateway'); + }).on('error', function() { + console.log('connection error - trying to reconnect'); + gw.connect(gwPort, gwAddress); + gw.setEncoding('ascii'); + }); + } else if (gwType == 'Serial') { + gw = require('serialport').SerialPort(gwPort, { baudrate: gwBaud }, false); + gw.open(); + gw.on('open', function() { + console.log('connected to serial gateway at ' + gwPort); + }).on('data', function(rd) { + var rds = rd.toString().split('\n'); + for (var i = 0; i < rds.length; i++) + rfReceived(rds[i].trim(), db, gw); + }).on('end', function() { + console.log('disconnected from gateway'); + }).on('error', function() { + console.log('connection error - trying to reconnect'); + gw.open(); + }); + } else { + throw new Error('unknown Gateway type'); + } +}); + diff --git a/NodeJsController/Reboot.js b/NodeJsController/Reboot.js new file mode 100644 index 0000000..4a521e1 --- /dev/null +++ b/NodeJsController/Reboot.js @@ -0,0 +1,45 @@ +var NodeIdToReboot = 1; + +var gwType = 'Ethernet'; +var gwAddress = '10.0.1.99'; +var gwPort = 9999; + +var BROADCAST_ADDRESS = 255; +var NODE_SENSOR_ID = 255; + +function encode(destination, sensor, command, acknowledge, type, payload, binary) { + var msg = destination.toString(10) + ";" + sensor.toString(10) + ";" + command.toString(10) + ";" + acknowledge.toString(10) + ";" + type.toString(10) + ";"; + if (binary) { + for (var i = 0; i < payload.length; i++) { + if (payload[i] < 16) + msg += "0"; + msg += payload[i].toString(16); + } + } else { + msg += payload; + } + msg += '\n'; + return msg.toString(); +} + +function rebootNode(destination, gw) { + var sensor = NODE_SENSOR_ID; + var command = 3; // C_INTERNAL + var acknowledge = 0; // no ack + var type = 13; // I_REBOOT + var payload = ""; + var td = encode(destination, sensor, command, acknowledge, type, payload, false); + console.log('-> ' + td.toString()); + gw.write(td); +} + +var gw = require('net').Socket(); +gw.connect(gwPort, gwAddress); +gw.setEncoding('ascii'); +gw.on('connect', function() { + console.log('connected to ethernet gateway at ' + gwAddress + ":" + gwPort); + rebootNode(NodeIdToReboot, gw); + console.log('reboot request submitted for ' + NodeIdToReboot); +}).on('end', function() { + console.log('disconnected from gateway'); +}); diff --git a/NodeJsController/package.json b/NodeJsController/package.json new file mode 100644 index 0000000..f9ec6ff --- /dev/null +++ b/NodeJsController/package.json @@ -0,0 +1,9 @@ +{ + "name": "NodeJsController", + "version": "0.0.1", + "description": "MySensors Controller based on Node JS", + "dependencies": { + "net": "~1.0.2", + "mongodb": "~1.4.7" + } +} From 5968ddf21964dff4043e70df8bd65286c90b5127 Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Sat, 26 Jul 2014 11:28:57 +0200 Subject: [PATCH 276/326] Send reboot msg after message retrieval to deal with sensors going to sleep minimal adjustment to the dallas temperature sensor as an example added scripts to drop the database and to set a reboot manually for debugging purposes --- .../DallasTemperatureSensor.cpp.hex | 1888 +++++++++-------- .../DallasTemperatureSensor.ino | 69 + NodeJsController/DropDb.js | 17 + NodeJsController/NodeJsController.js | 101 +- NodeJsController/Reboot.js | 63 +- 5 files changed, 1129 insertions(+), 1009 deletions(-) create mode 100644 NodeJsController/DallasTemperatureSensor/DallasTemperatureSensor.ino create mode 100644 NodeJsController/DropDb.js diff --git a/NodeJsController/DallasTemperatureSensor.cpp.hex b/NodeJsController/DallasTemperatureSensor.cpp.hex index dbd4fa5..a714a35 100644 --- a/NodeJsController/DallasTemperatureSensor.cpp.hex +++ b/NodeJsController/DallasTemperatureSensor.cpp.hex @@ -1,8 +1,8 @@ -:100000000C94AE020C94231F0C94501F0C94D60237 -:100010000C94D6020C94D6020C9447140C94D6027D +:100000000C94AE020C94311F0C945E1F0C94D6021B +:100010000C94D6020C94D6020C9455140C94D6026F :100020000C94D6020C94D6020C94D6020C94D602F0 :100030000C94D6020C94D6020C94D6020C94D602E0 -:100040000C947D1F0C94D6020C9453220C94D6224F +:100040000C948B1F0C94D6020C9461220C94E42225 :100050000C94D6020C94D6020C94D6020C94D602C0 :100060000C94D6020C94D602303B303B25643B25E1 :10007000643B0072643D636C6561720A0069643DB3 @@ -83,12 +83,12 @@ :10052000333333330F986E12831141EF8D211489C9 :100530003BE65516CFFEE6DB18D1844B381BF77C23 :100540001D901DA4BBE424203284725E228100C968 -:10055000F124ECA1E53D27003F09892211241FBEAB -:10056000CFEFD8E0DEBFCDBF11E0A0E0B1E0E8EC16 -:10057000FBE402C005900D92AC35B107D9F713E04A -:10058000ACE5B1E001C01D92A530B107E1F715E07F -:10059000CCE5D5E004C02297FE010E949325C83522 -:1005A000D107C9F70E94FC220C94E2250C940000AC +:10055000F124ECA1E53D27003F09972211241FBE9D +:10056000CFEFD8E0DEBFCDBF11E0A0E0B1E0E4EE18 +:10057000FBE402C005900D92A036B107D9F713E055 +:10058000A0E6B1E001C01D92A930B107E1F715E086 +:10059000CCE5D5E004C02297FE010E94A125C83514 +:1005A000D107C9F70E940A230C94F0250C9400008F :1005B000EF92FF920F931F93F22FF02E47FD02C090 :1005C000E4E001C0E4E1242F332727FD309537FF15 :1005D00003C0309521953F4F4F2D512F0F2FEE2EF9 @@ -105,17 +105,17 @@ :1006800001900D920020E1F70895FB01DC0141503B :10069000504048F001900D920020C9F701C01D9212 :1006A00041505040E0F70895A0E0B0E0EAE5F3E003 -:1006B0000C94B5257C01EB018A01690109C0CE01CA +:1006B0000C94C3257C01EB018A01690109C0CE01BC :1006C0002196F6010995F70181937F0101501040B1 -:1006D00001151105A1F7CDB7DEB7E8E00C94D125DF +:1006D00001151105A1F7CDB7DEB7E8E00C94DF25D1 :1006E000FB019F01E8944230C4F04532B4F44A3033 :1006F00029F497FB1EF4909581959F4F642F7727DF -:100700000E949725805D8A330CF0895D8193CB012F +:100700000E94A525805D8A330CF0895D8193CB0121 :100710000097A1F716F45DE251931082C9010C9481 :10072000A508FA01CF93FF93EF93223044F12532CD :1007300034F5C22FE894CA3049F497FB3EF4909503 :100740008095709561957F4F8F4F9F4F2C2F33274A -:1007500044275527FF93EF930E946F25EF91FF9158 +:1007500044275527FF93EF930E947D25EF91FF914A :10076000605D6A330CF0695D6193B901CA01605044 :1007700070408040904051F716F4CDE2C193108252 :100780008F919F91CF910C94A508592F482F372F07 @@ -124,24 +124,24 @@ :1007B000991F08957AE0979F902D879F802D910D26 :1007C00011240895FA01CF93FF93EF932230CCF0D8 :1007D0002532BCF4C22F2C2F332744275527FF93F3 -:1007E000EF930E946F25EF91FF91605D6A330CF0EB +:1007E000EF930E947D25EF91FF91605D6A330CF0DD :1007F000695D6193B901CA016050704080409040CA :1008000051F710828F919F91CF910C94A508FB0115 :100810009F01423074F0453264F4642F77270E94C0 -:100820009725805D8A330CF0895D8193CB01009719 +:10082000A525805D8A330CF0895D8193CB0100970B :10083000A1F71082C9010C94A508AEE0B0E0E3E294 -:10084000F4E00C94B9250D891E898F89988D97FF46 +:10084000F4E00C94C7250D891E898F89988D97FF38 :1008500002C080E090E801979E838D831A8309830C :100860008EE08C839E01255E3F4FCE010196698DFF :100870007A8DA9010E9478044D815E8157FD0AC0DE :100880002F813885421753070CF49A01020F131F6A -:10089000F80110822E96E4E00C94D525AEE0B0E08D -:1008A000E4E5F4E00C94B9258C0177FF02C060E028 +:10089000F80110822E96E4E00C94E325AEE0B0E07F +:1008A000E4E5F4E00C94C7258C0177FF02C060E01A :1008B00070E8615070407E836D831A8309838EE0F7 :1008C0008C83CE010196BA01A9010E9478042D8182 :1008D0003E8137FD09C0EF81F8852E173F070CF4E4 -:1008E000F901E00FF11F10822E96E4E00C94D5255B -:1008F000ABE0B0E0EEE7F4E00C94AB253C012B015B +:1008E000F901E00FF11F10822E96E4E00C94E3254D +:1008F000ABE0B0E0EEE7F4E00C94B9253C012B014D :100900005A01FC0117821682838181FD03C06FEFBB :100910007FEFC6C19AE0892E1E010894211C311C6C :10092000F3012381F20123FD859123FF81912F01A2 @@ -200,8 +200,8 @@ :100C7000DA94F101ED0DF11D808190E0B3010E9445 :100C8000B508DD20A9F706C080E290E0B3010E941C :100C9000B508EA94EE20C1F743CEF301668177816F -:100CA000CB012B96E2E10C94C725A9E0B0E0EBE57F -:100CB000F6E00C94AF256A01122FA02EBE2C2BE378 +:100CA000CB012B96E2E10C94D525A9E0B0E0EBE571 +:100CB000F6E00C94BD256A01122FA02EBE2C2BE36A :100CC000201718F42CE3922E02C0902E9394AE01BC :100CD0004F5F5F4F27E0092D0E94B707BC01898154 :100CE000E82EFF24C70189709070019711F4ADE2DE @@ -244,7 +244,7 @@ :100F300049F4B5E3BB1528F0BB1621F481149104E4 :100F400009F481E3F6018193D601812F90E202C07A :100F50009C93815011968823D9F7E10FF11D1082DF -:100F600020E030E0C9012996EEE00C94CB25283032 +:100F600020E030E0C9012996EEE00C94D925283024 :100F700008F027E03327DA01990F311D87FD9160D2 :100F800000966105710539F432602E5F3D9330E3C0 :100F90002A95E1F708959F3F30F08038710561058B @@ -294,927 +294,929 @@ :101250006795BA95C9F700976105710508959B01D7 :10126000AC010A2E06945795479537952795BA9560 :10127000C9F7620F731F841F951FA01D08950F9358 -:101280001F930CE511E0C80163E00E94B81D8AE6D7 -:1012900091E0B8010E940F198CE791E069E04AE003 -:1012A0000E94E91385E392E060E040E00E94330A87 +:101280001F9300E611E0C80163E00E94C61D8EE6D0 +:1012900091E0B8010E941D1980E891E069E04AE000 +:1012A0000E94F71389E392E060E040E00E94410A67 :1012B0001F910F910895CF92DF92EF92FF920F93BB -:1012C0001F93CF93DF938CE791E00E94790D8AE61C -:1012D00091E00E94041A83EFC82E81E0D82EC0E06E -:1012E000D0E056C08CE791E00E941B0B882331F0C0 -:1012F0008AE691E06C2F0E94271B05C08AE691E0E8 -:101300006C2F0E944E1B20E030E040E251E40E942E -:101310000C250E942924882777FD8095982F0E940C -:101320005C2420E030E040E251E40E94C1237B01D4 +:1012C0001F93CF93DF9380E891E00E94870D8EE615 +:1012D00091E00E94121A87EFC82E81E0D82EC0E05C +:1012E000D0E056C080E891E00E94290B882331F0BD +:1012F0008EE691E06C2F0E94351B05C08EE691E0D2 +:101300006C2F0E945C1B20E030E040E251E40E9420 +:101310001A250E943724882777FD8095982F0E94F0 +:101320006A2420E030E040E251E40E94CF237B01B8 :101330008C01F6016081718182819381A8019701FE -:101340000E94BD238823F9F0C801B70120E030E0F6 -:101350004EEF52EC0E94BD238823A9F085E392E072 -:101360006C2F0E94380AB801A70121E00E94470AA9 -:10137000BC018CE791E040E00E942112F601E0827E +:101340000E94CB238823F9F0C801B70120E030E0E8 +:101350004EEF52EC0E94CB238823A9F089E392E060 +:101360006C2F0E94460AB801A70121E00E94550A8D +:10137000BC0180E891E040E00E942F12F601E0827B :10138000F18202831383219684E090E0C80ED91E77 -:101390008091330290913402C817D90724F4C031E8 -:1013A000D10509F09FCF60914001709141018CE718 -:1013B00091E00E94800CDF91CF911F910F91FF90DF -:1013C000EF90DF90CF900895AF92BF92CF92EF92BF -:1013D000FF920F93CF93DF938AE691E00E945D1B0B -:1013E0008CE791E060E070E04FEF20E00FEFE3E08A -:1013F000EE2EF12C5CE4C52E32E0A32EB12C0E941F -:10140000B3128CE791E060E071E043E151E020E04D -:101410000E94B1118AE691E00E941D198093330267 -:1014200010923402C0E0D0E008C08CE791E06C2F4D -:1014300046E020E00E94FF112196809133029091B6 -:101440003402C817D9071CF4C031D10571F7DF91F8 -:10145000CF910F91FF90EF90CF90BF90AF900895F4 -:10146000FC0114820895FC0114826683458308956B -:10147000FC0166830895FC01808187708860808309 -:1014800081818F70806181836783CF010895CF92BE -:10149000DF92EF92FF920F931F937C01F7E0CF2E24 -:1014A000D12CC80ED91ECB01BA0142E086010E94A0 -:1014B000D802F60101900020E9F73197EC19FD09F7 -:1014C000EE0FEE0FEE0FD7018C918770E82BEC93A7 -:1014D00011968C9111978F7011968C93C7011F9163 -:1014E0000F91FF90EF90DF90CF900895CF93DF930F -:1014F000EC01DB010D900020E9F71197A61B4A2FA4 -:1015000050E09A01B3E0220F331FBA95E1F78881CA -:101510008770822B888389818F708983A93110F02D -:1015200049E150E0CE0107960E944503CE01DF91CC -:10153000CF910895FC01818182958F7090E0009792 -:1015400031F0019711F080E0089587810895CF016F -:1015500007960E94F9020895FC01818182958F709F -:1015600090E0009761F0059729F020E030E040E03E -:1015700050E00EC0278130854185528509C0CF01DA -:1015800007960E941703AC01CB01DA019C01AD0163 -:10159000B901CA010895CF93DF93FC01EB0181816A -:1015A00082958F7031F43796CB01BF010E943E03C4 -:1015B00036C06115710599F18330B9F0843028F493 -:1015C000813041F0823059F508C08430A9F085306F -:1015D00031F51CC0878190E002C087819085BE01F3 -:1015E0004AE050E00E9470031AC0878190854AE06B -:1015F00050E00E94070413C0678170858185928541 -:10160000AE012AE030E00E94910309C06781708535 -:1016100081859285AE012AE030E00E94E203CE018E -:10162000DF91CF9108959C01F999FECF32BD21BD84 -:10163000F89A80B50895FC0181890895EF92FF9290 -:101640000F931F93DF93CF93CDB7DEB7CC52D1402A -:101650000FB6F894DEBF0FBECDBFC95CDE4FE88089 -:10166000F980C753D140CB5CDE4FA881B981C55307 -:10167000D14054968C91882389F1EDB7FEB73A9703 -:101680000FB6F894FEBF0FBEEDBF31968E010F5F0F -:101690001F4FADB7BEB712961C930E9311978BE2F6 -:1016A00091E09383828388E690E09583848383E04E -:1016B00090E09783868389E090E0918780870E94FD -:1016C0001D04EDB7FEB73A960FB6F894FEBF0FBEF5 -:1016D000EDBF84E692E0B8010E945423FE01E75C6E -:1016E000FE4FCB5CDE4FA881B981C553D1405496E3 -:1016F0008C919E012F5F3F4F882359F0C9016CE305 -:1017000070E0A7019F010E944E048AE08CAF1DAEDD -:1017100007C0C9016BE271E0A7019F010E944E045E -:1017200084E692E0BE016F5F7F4F0E94542384E6FF -:1017300092E00E94E121C45DDE4F0FB6F894DEBF57 -:101740000FBECDBFCF91DF911F910F91FF90EF9012 -:101750000895CF93DF93EB010CC086E592E069E03A -:1017600070E040E050E020E030E00E94F913C05407 -:10177000DF418091440190914501892B89F08FE1EF -:10178000C034D80754F7B3C086E592E068E070E053 -:1017900040E050E020E030E00E94F913C05ADF4002 -:1017A0008091440190914501892B81F087E0C03DF3 -:1017B000D80764F086E592E067E070E040E050E032 -:1017C00020E030E00E94F913C05DD74080914401D1 -:1017D00090914501892B81F083E0C83ED80764F0E1 -:1017E00086E592E066E070E040E050E020E030E026 -:1017F0000E94F913C85ED340809144019091450145 -:10180000892B81F081E0C43FD80764F086E592E03F -:1018100065E070E040E050E020E030E00E94F91325 -:10182000C45FD1408091440190914501892B79F0AA -:10183000CA3FD10564F086E592E064E070E040E0E4 -:1018400050E020E030E00E94F913CA5FD040809160 -:10185000440190914501892B79F0CD37D10564F091 -:1018600086E592E063E070E040E050E020E030E0A8 -:101870000E94F913C857D0408091440190914501CE -:10188000892B71F0C034D1055CF086E592E062E00E -:1018900070E040E050E020E030E00E94F913EC9767 -:1018A0008091440190914501892B71F0C032D1059E -:1018B0005CF086E592E061E070E040E050E020E01E -:1018C00030E00E94F9136E97809144019091450198 -:1018D000892B99F060978CF086E592E060E070E0EB -:1018E00040E050E020E030E00E94F91306C08FE0B5 -:1018F000C03AD8070CF454CF47CFDF91CF91089569 -:10190000EF92FF920F931F938C017B0184E692E08C -:101910000E94E121C8010E941E1881E090E090938E -:10192000450180934401C801B7010E94A90B1F9192 -:101930000F91FF90EF9008955F926F927F928F9238 -:101940009F92AF92BF92CF92DF92EF92FF920F934E -:101950001F93CF93DF937C01562EEA01822E88815C -:10196000982E969496949694D7011E969C919B835C -:10197000887F82608883DE2CCF2CC7010E940318E9 -:10198000C7010E94331830E040E050E060E070E0B2 -:1019900080E090E0052DF1E03D3F08F4F0E0E1E06B -:1019A000423E08F4E0E031EEA32EAF0EE060F1E03D -:1019B000593A08F4F0E028EAB22EBE0EE1E0B8EAA7 -:1019C000BB1608F0E0E0FE2BA1E0693A08F4A0E0C5 -:1019D000BF2FB855E1E0B83A08F0E0E0AE2BF1E0F7 -:1019E000772309F4F0E0E1E0882309F4E0E08D2DAD -:1019F0009C2D1CEF95014B2F5A2F6F2F7E2F0E948D -:101A00004416892D90E04A9714F040E202C0492D17 -:101A1000495F8D2D9C2DBE01282D0E94F715682E43 -:101A20008D2D9C2D0E944E18BA80AB809C808E809C -:101A300019817D800881662029F497E1C92E91E003 -:101A4000D92E04C08CE1C82E81E0D82EB701695A86 -:101A50007F4FCE010E94CB0AEDB7FEB77A970FB643 -:101A6000F894FEBF0FBEEDBF3196ADB7BEB712966C -:101A7000FC92EE9211972DED30E033832283B482F5 -:101A80001582A682178250861186928613868486D6 -:101A90001586212F30E02770307037872687708AAF -:101AA000118A12951F70128B138A0695069506955A -:101AB000048B158AD78AC68A918F808F0E941E0B4D -:101AC000EDB7FEB77A960FB6F894FEBF0FBEEDBF26 -:101AD000862DDF91CF911F910F91FF90EF90DF90B6 -:101AE000CF90BF90AF909F908F907F906F905F90BE -:101AF00008954F925F926F927F928F929F92AF92D2 -:101B0000BF92CF92DF92EF92FF920F931F93DF93DA -:101B1000CF930F92CDB7DEB74C01582E492C8E01D2 -:101B20000F5F1F4FB8010E94E417882309F4D5C244 -:101B30008981873008F0D1C2C4010E94F414482F73 -:101B400065E1662E712C680C791CC401B3010E94FA -:101B5000EA15C4016981A80121E00E948614D4011C -:101B600055960C9106950695069510E0F401E00F48 -:101B7000F11F148EF401E788D08CC18CB38CF688E9 -:101B8000A28CB401695A7F4FC3010E94CB0A2DB7C2 -:101B90003EB7265130400FB6F8943EBF0FBE2DBF62 -:101BA000EDB7FEB73196ADB7BEB712969C928E9246 -:101BB000119720EB30E033832283E4821582D682B2 -:101BC0001782C0861186B28613862F2D30E02770CB -:101BD000307035872487A6861786F2945FE0F52259 -:101BE000F08A118A138B028B958B848B0E941E0BBB -:101BF000F401858987702DB73EB72A5E3F4F0FB637 -:101C0000F8943EBF0FBE2DBF8230B1F000D000D09F -:101C1000ADB7BEB712969C928E9211978FE990E065 -:101C200014969C938E9313970E941E0B80E00F9046 -:101C30000F900F900F9052C2F4012689022F077067 -:101C4000128DE788F08C318D9289992379F10330D8 -:101C500069F5173059F50E94C51F73708070907038 -:101C60000E941820D4011E968C911E9757968C9333 -:101C700057975996EC9259978FEF5B968C935B9799 -:101C800088E05A968C935A9756968C915697807FF7 -:101C9000836056968C935697C30150966C910E9420 -:101CA0003B0AAC01C4016E2D21E0F5C1F40186852B -:101CB000381709F0DBC123FF21C0D401D6967596F1 -:101CC00081E201900D928150E1F7D401D7968C9179 -:101CD000D797877FD7968C93D7971E968C911E9710 -:101CE000D8968C93D89757968C915797DA968C9371 -:101CF000B4016A5C7F4FC4010E944211F4018289E1 -:101D0000882319F18785F81601F12E2D30E081E73F -:101D100090E0880E991ED401ED91FC918FE89FEF21 -:101D2000880E991EE20FF31F80818F1571F0F082EB -:101D3000F999FECF1FBA2D5F3F4F32BD21BDF0BCD8 -:101D40000FB6F894FA9AF99A0FBE033009F081C1E0 -:101D5000183009F053C0D40154968C91882309F0AF -:101D60004DC0C3010E949A0A482F282F30E0F40189 -:101D7000808990E00197281739070CF0AEC14F5FBA -:101D8000408B27892787F999FECF1FBA81E090E021 -:101D900092BD81BD20BD0FB6F894FA9AF99A0FBE94 -:101DA000D40150963C91F999FECF1FBA82E090E0A1 -:101DB00092BD81BD30BD0FB6F894FA9AF99A0FBE64 -:101DC000EDB7FEB738970FB6F894FEBF0FBEEDBF64 -:101DD0003196ADB7BEB712969C928E9211978AE853 -:101DE00090E09383828324831582368317820E9436 -:101DF0001E0B80E0EDB7FEB73896A3C0EE2009F0C9 -:101E000028C1163009F4A3C0173038F4113009F492 -:101E10000FC1143009F061C112C01A3009F4B0C00A -:101E20001D3009F05AC128E088E190E00FB6F8941F -:101E3000A895809360000FBE20936000FFCFD4016F -:101E40001E968C918F3F09F048C1C3010E949A0AE7 -:101E5000F40186878F3FA9F400D000D0ADB7BEB79C -:101E600012969C928E92119784E890E014969C931F -:101E70008E9313970E941E0B0F900F900F900F9050 -:101E8000FFCF282F30E040E050E060E070E080E0DD -:101E900090E0E22E01E03D3F08F400E011E0423E18 -:101EA00008F410E00F511060E1E0593A08F4E0E066 -:101EB0001855F1E0183A08F0F0E0EF2BF1E0693A3C -:101EC00008F4F0E0BE2FB855E1E0B83A08F0E0E0E1 -:101ED000FE2BA1E0772309F4A0E0E1E0882309F4D8 -:101EE000E0E0852D942D61E04CEFF42E2B2F3F2F59 -:101EF0004A2F5E2F0E946517F4012685F999FECFBF -:101F00001FBA12BC11BC20BD0FB6F894FA9AF99A08 -:101F10000FBE00D000D000D0EDB7FEB73196ADB700 -:101F2000BEB712969C928E9211978DE790E09383A4 -:101F30008283248315820E941E0B80E0EDB7FEB7DA -:101F400036960FB6F894FEBF0FBEEDBFC7C0C301F3 -:101F50000E949A0A20E08D3409F421E0D4015196C0 -:101F60008C915197821709F4B8C051962C93F99926 -:101F7000FECF1FBA83E091E092BD81BD20BDA8C015 -:101F8000F4018289882309F4A8C0C3010E949A0A37 -:101F9000833409F0A2C000D000D0ADB7BEB712960E -:101FA0009C928E92119783E790E014969C938E9367 -:101FB00013970E941E0B20E030E00F900F900F90BF -:101FC0000F90D401AF58BF4F9FEFED91FC91119747 -:101FD000E20FF31F80818F3F81F09083F999FECF4C -:101FE0001FBA2D5F3F4F32BD21BD2350304090BD01 -:101FF0000FB6F894FA9AF99A0FBE2F5F3F4F22302E -:10200000310519F7F4018685878B118E8FEF838F49 -:102010008AE0828F8689807F8360868BC3016FE12F -:1020200071E00E94760ABC01C4010E94421155C0B1 -:1020300023E730E0820E931ED4010D911C9101150F -:10204000110509F44AC0C3010E94AC0AF8010995C0 -:1020500044C0E5E7F0E08E0E9F1ED401ED91FC91A7 -:10206000309711F0C301099581E038C09923A9F197 -:102070008981813091F584010F581F4FD801ED916E -:10208000FC91E30FF11D6081862F81508E3F30F46B -:10209000C401A30120E00E949C0C1FC0C401F401F4 -:1020A0006785A30120E00E949C0C6E2D70E0D80192 -:1020B000ED91FC91E60FF71F80818F1571F0F08292 -:1020C000F999FECF1FBA6D5F7F4F72BD61BDF0BC45 -:1020D0000FB6F894FA9AF99A0FBE80E00F90CF915C -:1020E000DF911F910F91FF90EF90DF90CF90BF9005 -:1020F000AF909F908F907F906F905F904F900895DA -:10210000EF92FF920F931F93CF93DF93EC010E9406 -:10211000C51F7B018C0103C0CE010E94790D0E9476 -:10212000C51F6E197F09800B910B605D7740804061 -:10213000904090F3DF91CF911F910F91FF90EF901E -:102140000895EF92FF920F931F938C0100591F4F38 -:10215000F8011082005710409FEFF801908B8685A0 -:10216000878B918F938F87E0828F8689807F8360B2 -:10217000868B75E1E72EF12CE00EF11EC7016FE1B1 -:1021800071E00E94760AC8016FEFA70121E00E946A -:102190009C0CC8010E9480101F910F91FF90EF903E -:1021A0000895EF92FF920F931F93CF93DF93EC016B -:1021B00000D000D0EDB7FEB79283818383E191E038 -:1021C000948383830E941E0B0F900F900F900F90AB -:1021D0008E85282F30E040E050E060E070E080E045 -:1021E00090E0E22E01E03D3F08F400E011E0423EC5 -:1021F00008F410E00F511060E1E0593A08F4E0E013 -:102200001855F1E0183A08F0F0E0EF2BF1E0693AE8 -:1022100008F4F0E0BE2FB855E1E0B83A08F0E0E08D -:10222000FE2BA1E0772309F4A0E0E1E0882309F484 -:10223000E0E0CE0161E02CEFF22E2B2F3F2F4A2F52 -:102240005E2F0E9465178E858F8B198E8FEF8B8F77 -:1022500083E08A8F8E89807F83608E8BCE01459646 -:102260006FE171E00E94760ABC01CE010E9442112A -:10227000CE010E948010DF91CF911F910F91FF90AE -:10228000EF9008951F93CF93DF93EC01DB0120E0E3 -:1022900011968C9111978770833009F421E08E8517 -:1022A0008F3F59F4222329F015968C91159783308E -:1022B00021F0CE010E94D1102CC08A89882331F1EF -:1022C00014969C911497CF58DF4FE881F981C1573C -:1022D000D040E90FF11D6081862F81508E3F30F490 -:1022E000992321F0CE01AD0120E00DC0222371F031 -:1022F00015968C911597843049F49F3F39F4CE019F -:102300006FEFAD0121E00E949C0C27C08C898823CF -:1023100011F080E022C0CE016F85AD0120E00E9467 -:102320009C0C182F882329F011E0C059DF4F188228 -:1023300013C08B89882351F0C059DF4F8881C05763 -:10234000D040863018F0CE010E94A110FE01E05965 -:10235000FF4F80818F5F8083812FDF91CF911F910D -:102360000895FF920F931F93CF93DF93EC018A019F -:10237000F22E61157105E1F08E858F8B198E8FEF2E -:102380008B8F8BE08A8F30E02170307053E0220F0A -:10239000331F5A95E1F78E89807F8360822B8E8B65 -:1023A000CE0145960E94760ABC01CE010E944211E0 -:1023B00001151105F1F08E858F8B198E8FEF8B8FA4 -:1023C0008CE08A8F2F2D30E02170307043E0220F97 -:1023D000331F4A95E1F78E89807F8360822B8E8B35 -:1023E000CE014596B8010E94760ABC01CE010E943A -:1023F0004211DF91CF911F910F91FF9008950F939C -:102400001F938C01FC018685878B118E638F428F11 -:1024100030E02170307063E0220F331F6A95E1F7DE -:102420008689807F822B868BC801459660E271E0A9 -:102430000E94760ABC01C8010E9442111F910F91AF -:102440000895DC01FB011E968C911E97828350E05B -:102450004170507083E0440F551F8A95E1F78181E8 -:10246000807F8160842B8183CD010E944211811184 -:1024700081E008950F931F938C0180E091E00E940A -:102480000B230F581F4FF8018083918363E070E0A6 -:1024900040E051E023E13BE00E9454031F910F9183 -:1024A0000895EF92FF920F931F93CF93DF93EC0168 -:1024B000E62EF42E8901C059DF4F1882C057D04054 -:1024C000CE010E948718CE010E946C14882389F4E3 -:1024D00000D000D0EDB7FEB7D283C18380E291E097 -:1024E000948383830E941E0B0F900F900F900F9088 -:1024F000FFCFCE0161E00E94E015CE0162E040E036 -:102500000E94FE16CE010E942A17CE016F2D0E9456 -:102510001116CE016E2D0E94E516CE01B8010E9463 -:10252000B416CE0165E04FE00E94D615CE0162E000 -:1025300070E00E949916CE010E944617CE0162E01B -:10254000EE24EA942CEFF22E01EE18EA28EA30E0AD -:1025500040E050E00E946517DF91CF911F910F91ED -:10256000FF90EF9008958F929F92AF92BF92CF927B -:10257000DF92EF92FF920F931F93CF93DF93EC01C3 -:102580004B01D42E122F84E692E040E052EC61E041 -:1025900070E026E00E9443211C8A1A8A112311F060 -:1025A00081E08A8BCB58DF4F99828882C557D04013 -:1025B0008A89882319F0CE010E943A12CE01B70110 -:1025C0004C2D95010E945112CE010E9660E070E0F4 -:1025D00043E050E023E13BE00E945403CE014196EA -:1025E00063E271E041E050E023E13BE00E945403EC -:1025F00089898F3F11F481E0898B0F3F19F00F8794 -:102600001B8A02C081E08B8B8FEFD812DE868B890C -:10261000882331F08F858F3F19F4CE010E94A110DD -:102620008E858F3F19F4CE010E94D1108A898823AC -:1026300019F420E331E002C027E331E0ADB7BEB7C3 -:1026400018970FB6F894BEBF0FBEADBFEDB7FEB77B -:1026500031961296DC93CE9311978DE291E093839D -:102660008283358324838E85868317820E941E0B86 -:10267000DC2ECD2E8DB79EB708960FB6F8949EBF70 -:102680000FBE8DBF8E85282F30E040E050E060E027 -:1026900070E080E090E0E22E01E03D3F08F400E0D1 -:1026A00011E0423E08F410E00F511060E1E0593AA9 -:1026B00008F4E0E01855F1E0183A08F0F0E0EF2BEC -:1026C000F1E0693A08F4F0E0BE2FB855E1E0B83A1D -:1026D00008F0E0E0FE2BA1E0772309F4A0E0E1E0C0 -:1026E000882309F4E0E08D2D9C2D60E04CEFF42E62 -:1026F0002B2F3F2F4A2F5E2F0E9465178E85282F84 -:1027000030E040E050E060E070E080E090E0E22EF9 -:1027100001E03D3F08F400E011E0423E08F410E023 -:102720000F511060E1E0593A08F4E0E01855F1E08B -:10273000183A08F0F0E0EF2BF1E0693A08F4F0E025 -:10274000BE2FB855E1E0B83A08F0E0E0FE2BA1E07A -:10275000772309F4A0E0E1E0882309F4E0E08D2D7F -:102760009C2D61E03CEFF32E2B2F3F2F4A2F5E2F45 -:102770000E9465178A89882311F441E101C042E172 -:10278000CE016FEF20E00E94FF118E858F8B198E96 -:102790008FEF8B8F86E08A8F8E89807F83608E8B10 -:1027A000CE0145966F850E943B0ABC01CE010E9476 -:1027B0004211CE010E948010DF91CF911F910F91A5 -:1027C000FF90EF90DF90CF90BF90AF909F908F9051 -:1027D00008950F931F938C010E945B14C8014596C6 -:1027E0000E94300AC801C6960E94300A1F910F91BC -:1027F00008954115510529F480917A008F778093CF -:102800007A006A307105B9F063FD02C0E8E001C0EA -:10281000E8E2862F8770E82B88E190E00FB6F89405 -:10282000A895809360000FBEE093600080916000E7 -:10283000806480936000232BA1F483B7817F846040 -:1028400083BFF89483B7816083BF85B7806685BFF7 -:102850008F7D85BF7894889583B78E7F83BF78946A -:102860000EC083B7817F846083BFF89483B7816093 -:1028700083BF7894889583B78E7F83BF7894452BE8 -:1028800029F480917A00806880937A0008951F92DD -:102890000F920FB60F9211248F9388E10FB6F89420 -:1028A00080936000109260000FBE8F910F900FBE5A -:1028B0000F901F901895FC01608341831282138250 -:1028C00080E2848315821682178210861186128612 -:1028D0001386148615860895FC0183810895FF925E -:1028E0000F931F938C01F62E81E00E94D91880E08F -:1028F0000E94E21884E00E94E718F80181816F2DA0 -:102900000E94BF201F910F91FF900895EF92FF92B8 -:102910000F931F93CF93DF93EC01162F042FF52E07 -:10292000E22E60E00E946F141770186A1EBD0DB48D -:1029300007FEFDCF802F9F2D9C01F9012E2D213206 -:1029400008F020E28EB508C080818EBD0DB407FE70 -:10295000FDCF31968EB521502223B1F7CE0161E033 -:102960000E946F14DF91CF911F910F91FF90EF9014 -:102970000895CF93DF93EC0160E00E946F1480E52F -:102980008EBD0DB407FEFDCF8EB583E78EBD0DB4B1 -:1029900007FEFDCF8EB5CE0161E00E946F14DF917E -:1029A000CF9108951F93CF93DF93EC01162F60E032 -:1029B0000E946F141EBD0DB407FEFDCF1EB5CE01E3 -:1029C00061E00E946F14812FDF91CF911F910895D4 -:1029D0006FEF0E94D214089561EE0E94D214089500 -:1029E00062EE0E94D21408951F93CF93DF93EC01FF -:1029F00060E00E946F1480E68EBD0DB407FEFDCF2F -:102A00008EB58FEF8EBD0DB407FEFDCF1EB5CE0186 -:102A100061E00E946F14113220F0CE010E94F01488 -:102A200010E0812FDF91CF911F910895EF92FF92D7 -:102A30000F931F93CF93DF93EC01F62EE72E9C812B -:102A4000142F941708F4192F8D81882311F000E0BA -:102A500002C0092F011BCE0160E00E946F1481E6C5 -:102A60008EBD0DB407FEFDCF8F2D9E2D9C01F9016B -:102A7000FEB49FEF07C09EBD0DB407FEFDCF8EB51F -:102A8000819311501123B9F707C08EBD0DB407FE15 -:102A9000FDCF9EB5015001C08FEF0023B1F7CE01ED -:102AA00061E00E946F148F2DDF91CF911F910F91E4 -:102AB000FF90EF900895DF92EF92FF920F931F9394 -:102AC000CF93DF93EC01E62ED72EF22E9C81142FAC -:102AD000941708F4192F8D81882311F000E002C0AB -:102AE000092F011BCE0160E00E946F14FEBC0DB4E3 -:102AF00007FEFDCF8E2D9D2D9C01F901FEB408C06F -:102B000080818EBD0DB407FEFDCF31968EB511507C -:102B10001123B1F706C01EBC0DB407FEFDCF8EB564 -:102B200001500023C1F7CE0161E00E946F148F2D88 -:102B3000DF91CF911F910F91FF90EF90DF9008955B -:102B4000CF93DF93EC01222311F420EA01C020EBA4 -:102B5000CE010E945B15888161E00E94BF208881C0 -:102B600060E00E94BF20DF91CF9108950F931F93E3 -:102B7000CF93DF93EC01162F042F60E00E946F14B7 -:102B80001F7110621EBD0DB407FEFDCF1EB50EBD38 -:102B90000DB407FEFDCF8EB5CE0161E00E946F142B -:102BA000812FDF91CF911F910F910895262F4F70A4 -:102BB0002295207F242B64E0422F0E94B6150895B1 -:102BC000662319F061E04FE302C061E040E00E943B -:102BD000B61508950F931F938C010E941615C80116 -:102BE00067E040E70E94B6151F910F910895CF93BB -:102BF000DF93EC010E94A015CE010E94E8148073BF -:102C0000D9F3CE0167E040E70E94B61584FD02C00B -:102C100081E004C0CE010E94EC1480E0DF91CF91EE -:102C20000895462F603808F04FE765E00E94B6151A -:102C30000895EF92FF920F931F93CF93DF937C0140 -:102C4000162FEA01022F60E00E946F141F711062BC -:102C50001EBD0DB407FEFDCF1EB508C088818EBD18 -:102C60000DB407FEFDCF21968EB501500023B1F7BC -:102C7000C70161E00E946F14812FDF91CF911F91F6 -:102C80000F91FF90EF900895EF92FF920F931F9393 -:102C9000DF93CF93CDB7DEB728970FB6F894DEBF9A -:102CA0000FBECDBF7C0109831A832B833C834D83E8 -:102CB0005E836F8378878E010F5F1F4F6AE0A801E4 -:102CC00025E00E941916C70160E1A80125E00E94D5 -:102CD0001916C70161E1F70144810E94B6152896D3 -:102CE0000FB6F894DEBF0FBECDBFCF91DF911F911D -:102CF0000F91FF90EF9008951F93CF93DF93EC0116 -:102D0000162F60E00E946F141F711EBD0DB407FEE8 -:102D1000FDCF8EB58FEF8EBD0DB407FEFDCF1EB576 -:102D2000CE0161E00E946F14812FDF91CF911F913E -:102D300008950F931F93CF93DF938C01EB0160E015 -:102D40000E947C16482F437F209729F0219711F489 -:102D5000486001C04C60C80160E00E94B615DF9178 -:102D6000CF911F910F910895FF920F931F93CF93CF -:102D7000DF93EC018B0166E00E947C161A82F7ED6E -:102D8000FF2EF8220230110511F480E206C0013056 -:102D9000110529F481E08A8388E0F82A01C01A82AB -:102DA000CE0166E04F2D0E94B615CE0166E00E946E -:102DB0007C168F1511F481E002C01A8280E0DF9149 -:102DC000CF911F910F91FF9008951F93CF93DF93A1 -:102DD000EC01162F66E00E947C16143010F04CE0D7 -:102DE00003C0412F440F4F5F887F482BCE0166E020 -:102DF0000E94B615DF91CF911F9108950F931F93F5 -:102E0000CF93DF93EC01162F042F6730F0F461E0CD -:102E10000E947C16282F002351F081E090E002C030 -:102E2000880F991F1A95E2F7422F482B0AC081E0BC -:102E300090E002C0880F991F1A95E2F7482F40953D -:102E40004223CE0161E00E94B615DF91CF911F9120 -:102E50000F9108950F931F938C010E94B914C8011C -:102E60006DE10E947C16482F4660C8016DE10E940A -:102E7000B615C8016CE10E947C16482F4360C8015A -:102E80006CE10E94B6151F910F9108950F931F9347 -:102E90008C010E94B914C8016DE10E947C16482F74 -:102EA0004460C8016DE10E94B615C8016CE10E9442 -:102EB0007C16482F4F63C8016CE10E94B61581E073 -:102EC000F80185831F910F910895BF92CF92DF92F1 -:102ED000EF92FF920F931F93DF93CF93CDB7DEB79F -:102EE00028970FB6F894DEBF0FBECDBF6C01B62E8B -:102EF000E982FA820B831C832D833E834F8358879C -:102F0000662389F4F601E6828A8187838B81808734 -:102F10008C8181878D8182878E8183878F81848751 -:102F20008885858707C0F6E0F61708F43DC081E084 -:102F3000681761F4EB2DF0E0EA5BFE4FE491C60107 -:102F40006E2FAE014F5F5F4F25E00BC0E62FF0E024 -:102F5000EA5BFE4FE491C6016E2FAE014F5F5F4FFB -:102F600021E00E9419160B2D10E0F801E45BFE4FE2 -:102F7000E491C6016E2FF60144810E94B615C60188 -:102F800062E00E947C16005C1E4FF801649121E013 -:102F900030E002C0220F331F6A95E2F7482F422B20 -:102FA000C60162E00E94B61528960FB6F894DEBFFF -:102FB0000FBECDBFCF91DF911F910F91FF90EF908A -:102FC000DF90CF90BF9008950F931F93CF93DF931F -:102FD000EC018B0167E10E947C1680FF02C080E05B -:102FE0000DC00115110549F0CE010E94E81490E0D2 -:102FF000959587958770F801808381E0DF91CF9167 -:103000001F910F910895CF93DF93EC0160E00E9430 -:103010007C1681FD10C0CE0160E00E947C16482F16 -:103020004260CE0160E00E94B61565E070E080E08D -:1030300090E00E941820DF91CF9108950F931F9385 -:103040008C01FC01808160E00E94BF20C80160E02B -:103050000E947C16482F4D7FC80160E00E94B61583 -:103060001F910F9108950F931F938C01FC01808194 -:1030700060E00E94BF20C8010E94EC14C8010E94B9 -:10308000F014C80160E00E947C16482F4E7FC801F2 -:1030900060E00E94B6151F910F910895CF93DF93C2 -:1030A000EC010E940318CE0160E00E947C16482FBC -:1030B0004160CE0160E00E94B615CE0167E040E7B6 -:1030C0000E94B6158E819F81892B9885892B9985C1 -:1030D000892B9A85892B9B85892B9C85892B9D859E -:1030E000892B41F0AE014A5F5F4FCE016AE025E0D7 -:1030F0000E941916CE010E94F014CE010E94EC1419 -:10310000888161E00E94BF20DF91CF910895CF9325 -:10311000DF93EC01888161E00E948020898161E079 -:103120000E9480200E94F818888160E00E94BF20E1 -:10313000CE0161E00E946F1465E070E080E090E0F5 -:103140000E941820CE0165E04FE00E94D615CE0106 -:1031500062E070E00E94B416882311F081E08B8356 -:10316000CE0160E070E00E94B416CE0162E070E033 -:103170000E949916CE0167E040E70E94B615CE0185 -:103180006CE40E941116CE010E94F014CE010E9440 -:10319000EC14CE010E940318CE0160E00E947C1660 -:1031A000482F4E7FCE0160E00E94B615DF91CF918F -:1031B0000895882319F48CB5806202C08CB58F7D88 -:1031C0008CBD08959CB5937F982B9CBD08952CB51C -:1031D000382F33702C7F322B3CBD2DB590E0959568 -:1031E00087959595879581702E7F822B8DBD08954B -:1031F0008AE061E00E94BF208AE061E00E948020B6 -:103200008CB580618CBD8CB580648CBD8DE061E037 -:103210000E9480208BE061E00E9480200895FC01E4 -:1032200087ED99E1918B808B758364831382108283 -:103230008FE190E0928381830895FC01838108955A -:103240006F927F928F929F92AF92BF92CF92DF92B6 -:10325000EF92FF920F931F93CF93DF93FB01EA014D -:10326000998180E0288130E0282B392BE081E232FF -:1032700031F0E83221F0E03109F084C04BC04C81DC -:103280004F3329F1403420F44F3109F07BC02FC077 -:103290004F3579F04F3709F075C0B901882777FDB0 -:1032A0008095982F0E945C2420E030E040E85DE3A8 -:1032B0002EC035952795B901882777FD8095982FE1 -:1032C0000E945C2420E030E040E05EE320C03595C1 -:1032D000279535952795B901882777FD8095982FF3 -:1032E0000E945C2420E030E040E85EE310C093E000 -:1032F000359527959A95E1F7B901882777FD80954F -:10330000982F0E945C2420E030E040E05FE30E94C0 -:103310000C2532C0EF8035952795B901882777FDB8 -:103320008095982F0E945C2420E030E040E85EE326 -:103330000E9458233B014C016E2D70E02E81621BD0 -:103340007109882777FD8095982F0E945C245B0186 -:103350006C01FF2400E010E0C801B7010E945A246C -:103360009B01AC01C601B5010E94C1239B01AC01C8 -:10337000C401B3010E9459239B01AC01832F942FF8 -:10338000752F00C0262F382F492F572FB901CA019A -:10339000DF91CF911F910F91FF90EF90DF90CF9031 -:1033A000BF90AF909F908F907F906F9008950895F9 -:1033B000EF92FF920F931F938C017B01FC0184819C -:1033C00095810E94D71CF80184819581B7010E94E4 -:1033D000B61CF8018481958164EB40E00E94711C69 -:1033E000F801848195810E94EC1BF82EF8018481FC -:1033F00095810E94D71C80E0FF2009F481E01F9195 -:103400000F91FF90EF9008950F931F938C01FC0193 -:10341000848195810E94D71CF801848195810E9446 -:10342000B11CF8018481958164E440810E94711C83 -:10343000F801818192818F33910551F08F3591058B -:1034400061F04F9779F46EE570E080E090E00EC097 -:103450006CEB70E080E090E009C067E771E080E02D -:1034600090E004C06EEE72E080E090E00E941820D0 -:103470001F910F910895CF92DF92EF92FF920F93D9 -:103480001F938C016B017A01FC01848195810E945C -:10349000D71CF80184819581B6010E94B61CF80101 -:1034A000848195816EEB40E00E94711CF80184815B -:1034B00095810E94201CF7018083F8018481958109 -:1034C0000E94201CF7018183F801848195810E946C -:1034D000201CF7018283F801848195810E94201CC1 -:1034E000F7018383F801848195810E94201CF701F4 -:1034F0008483F801848195810E94201CF7018583D3 -:10350000F801848195810E94201CF7018683F801CF -:10351000848195810E94201CF7018783F8018481B2 -:1035200095810E94201CF7018087F8018481958194 -:103530000E94D71C1F910F91FF90EF90DF90CF90CA -:1035400008950F931F938A010E943B1AC80168E0F7 -:103550000E94DB1B20E0F8019085891709F421E027 -:10356000822F1F910F910895CF92DF92EF92FF92D9 -:103570000F931F93DF93CF93CDB7DEB729970FB685 -:10358000F894DEBF0FBECDBF8C017B016E010894A5 -:10359000C11CD11CA6010E94A11A882329F460E055 -:1035A00070E08EEF92EC05C0C801B701A6010E9441 -:1035B000201929960FB6F894DEBF0FBECDBFCF916C -:1035C000DF911F910F91FF90EF90DF90CF900895C2 -:1035D0000F931F938B01CB0167E00E94DB1B20E060 -:1035E000F8019781891709F421E0822F1F910F912B -:1035F0000895EF92FF920F931F93CF93DF93EC0107 -:103600007B01042F8C819D810E94CE1B10E007C09E -:10361000CE01B7010E94E81A8823A9F00AC08C8164 -:103620009D81B7010E94221D882329F0101759F4AB -:10363000EFCF81E001C080E0DF91CF911F910F912A -:10364000FF90EF9008951F5F0117A8F3E8CFEF9266 -:10365000FF920F931F93DF93CF93CDB7DEB72897D9 -:103660000FB6F894DEBF0FBECDBF7C01462F8E0192 -:103670000F5F1F4FB8010E94F91AC701B8010E94DD -:10368000B41A28960FB6F894DEBF0FBECDBFCF9107 -:10369000DF911F910F91FF90EF9008950E94271BDB -:1036A00026E636E646EE5FE30E940C2520E030E099 -:1036B00040E052E40E94592308959F92AF92BF9236 -:1036C000CF92DF92EF92FF920F931F93DF93CF93EE -:1036D000CDB7DEB761970FB6F894DEBF0FBECDBF92 -:1036E0008C01FC01848195810E94CE1BF80113821C -:1036F0007E010894E11CF11CE9E0AE2EB12CAC0E69 -:10370000BD1E7FE7C72ED12C992493942DC0C801EC -:10371000B7010E94E81A882339F1F80180818823D3 -:1037200041F4C801B7010E94D819882311F0F801AB -:103730009082C801B701A5010E943B1A898180319E -:1037400021F4F801D282C1820BC08D85282F30E090 -:10375000F801818192818217930714F432832183C7 -:10376000F80183818F5F8383F80184819581B7019C -:103770000E94221D882359F661960FB6F894DEBF89 -:103780000FBECDBFCF91DF911F910F91FF90EF90B2 -:10379000DF90CF90BF90AF909F900895FC0113866B -:1037A0001586148687E090E012863197009711F015 -:1037B0000197FACF0895DC0180E00BC0E82FF0E01C -:1037C0008D9190E0E827F927ED5FFC4FE49161507F -:1037D0008E2F662399F70895DF92EF92FF920F9351 -:1037E0001F93FC01E08001811281F894F80181812E -:1037F0008E2981838281DE2CD0948D21828383E087 -:1038000090E00E94D31FF80181818D2181838AE09D -:1038100090E00E94D31FF8010081789485E390E046 -:103820000E94D31F80E010E0FF240E211F21012BF6 -:1038300009F081E01F910F91FF90EF90DF900895C4 -:10384000EF92FF920F931F93CF93DF937C0101E0E0 -:1038500010E0C0E0D0E0C7010E94EC1B8111102BEA -:10386000000F2196C830D105B1F7812FDF91CF919C -:103870001F910F91FF90EF9008951F93CF93DF93C7 -:10388000FC011081C181D281912F909560FF12C0FF -:10389000F8948A8189238A838981812B89838AE0AC -:1038A00090E00E94D31F8A81812B8A83789487E3DA -:1038B00090E011C0F8948A8189238A838981812BC1 -:1038C000898381E490E00E94D31F8A81812B8A83BF -:1038D000789485E090E00E94D31FDF91CF911F91F3 -:1038E0000895CF92DF92EF92FF920F931F93CF93A1 -:1038F000DF938C01E42EFF24F394C0E0D0E0C62EC9 -:10390000DD2460E08F2D90E08C219D21892B09F032 -:1039100061E0C8010E943D1CFF0C2196C830D10512 -:1039200081F7EE20A9F4F894D8011196ED91FC915D -:10393000129791818C918095982391831196ED91A6 -:10394000FC91129792818C91809598239283789420 -:10395000DF91CF911F910F91FF90EF90DF90CF906B -:1039600008956CEC40E00E94711C0895EF92FF9264 -:103970000F931F93CF93DF938C017B0165E540E0AC -:103980000E94711CC0E0D0E0F701EC0FFD1FC801E0 -:10399000608140E00E94711C2196C830D105A1F7DA -:1039A000DF91CF911F910F91FF90EF900895DF92DB -:1039B000EF92FF920F931F93CF93DF93FC0100814F -:1039C000C181D281F8948981D02ED0948D218983B0 -:1039D00078941DE7E02EFF24115059F182E090E029 -:1039E0000E94D31F888190E08E219F21892BA1F313 -:1039F000F8948A818D218A838981802B89837894A8 -:103A000084EF91E00E94D31FF89489818D218983EE -:103A100080E590E00E94D31F888110E090E0E822CA -:103A2000F922EF2809F411E0789484EA91E00E94E9 -:103A3000D31F812FDF91CF911F910F91FF90EF90B6 -:103A4000DF900895AF92BF92DF92EF92FF920F93B3 -:103A50001F93CF93DF93EC015B018D85882309F0E1 -:103A600061C0CE010E94D71C882329F41B861D86C5 -:103A70001C8690E06BC0CE0160EF40E00E94711C9C -:103A800001E0DD24EE24FF24F394CE010E94EC1B20 -:103A9000182FCE010E94EC1B113019F48130E1F493 -:103AA00039C0181709F58B85081770F460E0FE011E -:103AB000EE0DF11D838190E02F2D30E082239323C2 -:103AC000181619064CF003C060E0081729F00930F9 -:103AD00008F04CC00C874AC0FE01EE0DF11D838139 -:103AE0008F29838361E009C0612FFE01EE0DF11D76 -:103AF0009F2D9095838189238383CE010E943D1C55 -:103B00000F5FFF0C19F4E394FF24F39487E08E1504 -:103B100008F0BBCF013430F0DB86DD2011F581E009 -:103B20008D871FC01B861D861C8690E0DE0120E06D -:103B300030E0F501E20FF31F13968C911397808309 -:103B40002F5F3F4F11962830310599F7892FDF916C -:103B5000CF911F910F91FF90EF90DF90BF90AF90AA -:103B600008958B818823F1F291E0E0CFD02EBDCF74 -:103B7000FF920F931F938C01F62E862F60E00E9418 -:103B800080208F2D90E0FC01EB5CFB4FE491D8018D -:103B9000EC938F5D9B4FFC016491E62FF0E0EE0FFC -:103BA000FF1FE95EFB4F8591949112969C938E9333 -:103BB00011971B961C921B971D961C921D971C9685 -:103BC0001C92F80187E090E012863197009711F07F -:103BD0000197FACF1F910F91FF9008950F931F93B4 -:103BE000CF93DF93BC018230910510F462E070E066 -:103BF000E0910303F091040340E050E0A0E0B0E066 -:103C000024C08081918186179707D0F08617970787 -:103C100071F482819381109729F013969C938E936F -:103C200012972CC0909304038093030327C041157F -:103C3000510519F08417950718F48D01AC01EF01B7 -:103C4000DF01828193819C01F9013097D1F6411502 -:103C50005105F9F0CA01861B970B8430910580F459 -:103C60008A819B810115110521F0F80193838283DC -:103C700004C09093040380930303FE01329645C071 -:103C8000FE01E80FF91F619371930297998388836E -:103C90003CC08091010390910203892B41F48091F3 -:103CA0004801909149019093020380930103409150 -:103CB0004A0150914B014115510541F44DB75EB792 -:103CC0008091460190914701481B590B20910103B7 -:103CD0003091020324173507B0F4CA01821B930BFD -:103CE0008617970780F0AB014E5F5F4F84179507EB -:103CF00050F0420F531F5093020340930103F90108 -:103D00006193719302C0E0E0F0E0CF01DF91CF91C9 -:103D10001F910F910895CF93DF939C01009709F4B1 -:103D20008FC0EC0122971B821A826091030370916D -:103D300004036115710581F488819981820F931FB5 -:103D400020910103309102032817390739F5D093E8 -:103D50000203C093010374C0DB0140E050E0AC17E4 -:103D6000BD0708F1BB83AA83FE0121913191E20FC7 -:103D7000F31FAE17BF0779F48D919C911197280F0F -:103D8000391F2E5F3F4F3983288312968D919C9166 -:103D900013979B838A834115510571F4D0930403D3 -:103DA000C09303034DC012968D919C911397AD0162 -:103DB000009711F0DC01D3CFDA011396DC93CE9398 -:103DC0001297FA0121913191E20FF31FCE17DF070D -:103DD00069F488819981280F391F2E5F3F4FFA01BE -:103DE000318320838A819B8193838283E0E0F0E0AA -:103DF000DB0112968D919C911397009719F0BC01ED -:103E0000FD01F6CFAB014E5F5F4FDB018D919C91C1 -:103E1000840F951F20910103309102032817390761 -:103E200079F4309729F4109204031092030302C02E -:103E30001382128242505040509302034093010378 -:103E4000DF91CF9108951F920F920FB60F92112418 -:103E50002F933F934F935F936F937F938F939F9392 -:103E6000AF93BF93EF93FF938091570290915802C5 -:103E7000892B29F0E0915702F09158020995FF91A2 -:103E8000EF91BF91AF919F918F917F916F915F91D2 -:103E90004F913F912F910F900FBE0F901F9018954B -:103EA0001F920F920FB60F9211242F933F934F93AF -:103EB0005F936F937F938F939F93AF93BF93EF9392 -:103EC000FF938091590290915A02892B29F0E09139 -:103ED0005902F0915A020995FF91EF91BF91AF916C -:103EE0009F918F917F916F915F914F913F912F9112 -:103EF0000F900FBE0F901F9018951F920F920FB644 -:103F00000F9211242F933F938F939F93AF93BF935F -:103F100080915F0290916002A0916102B091620273 -:103F2000309163020196A11DB11D232F2D5F2D3706 -:103F300020F02D570196A11DB11D2093630280939F -:103F40005F0290936002A0936102B093620280913D -:103F50005B0290915C02A0915D02B0915E020196BD -:103F6000A11DB11D80935B0290935C02A0935D0242 -:103F7000B0935E02BF91AF919F918F913F912F912E -:103F80000F900FBE0F901F9018958FB7F894209147 -:103F90005F023091600240916102509162028FBFD6 -:103FA000B901CA010895019739F0880F991F880F48 -:103FB000991F02970197F1F70895789484B582606C -:103FC00084BD84B5816084BD85B5826085BD85B5BD -:103FD000816085BDEEE6F0E0808181608083E1E86C -:103FE000F0E01082808182608083808181608083A4 -:103FF000E0E8F0E0808181608083E1EBF0E08081A7 -:1040000084608083E0EBF0E0808181608083EAE778 -:10401000F0E0808184608083808182608083808101 -:10402000816080838081806880831092C1000895C0 -:10403000EF92FF920F931F93CF93DF937B018C013D -:104040003FB7F89480915B0290915C02A0915D0271 -:10405000B0915E0226B5A89B05C02F3F19F00196CE -:10406000A11DB11D3FBFBA2FA92F982F8827820FFE -:10407000911DA11DB11D52E0880F991FAA1FBB1FE2 -:104080005A95D1F7EC0130C00E9457233FB7F894FE -:1040900080915B0290915C02A0915D02B0915E0202 -:1040A00026B5A89B05C02F3F19F00196A11DB11D93 -:1040B0003FBFBA2FA92F982F8827820F911DA11DCE -:1040C000B11D32E0880F991FAA1FBB1F3A95D1F787 -:1040D0008C1B9D0B885E9340B8F20894E108F108B0 -:1040E00001091109C851DC4FE114F1040105110562 -:1040F00059F6DF91CF911F910F91FF90EF900895A6 -:10410000CF93DF93482F50E0CA018B5C9B4FFC019B -:1041100034914F5D5B4FFA018491882369F190E0FF -:10412000880F991FFC01ED5FFB4FA591B491FC0135 -:10413000E35FFB4FC591D491662351F42FB7F894F8 -:104140008C91932F909589238C93888189230BC020 -:10415000623061F42FB7F8948C91932F9095892356 -:104160008C938881832B88832FBF06C09FB7F894D8 -:104170008C91832B8C939FBFDF91CF910895482F13 -:1041800050E0CA01875B9B4FFC012491CA018B5C04 -:104190009B4FFC0194914F5D5B4FFA0134913323A7 -:1041A00009F440C0222351F1233071F0243028F467 -:1041B0002130A1F0223011F514C02630B1F02730A3 -:1041C000C1F02430D9F404C0809180008F7703C0FF -:1041D000809180008F7D8093800010C084B58F77A0 -:1041E00002C084B58F7D84BD09C08091B0008F77F7 -:1041F00003C08091B0008F7D8093B000E32FF0E08A -:10420000EE0FFF1FE35FFB4FA591B4912FB7F8941A -:10421000662321F48C919095892302C08C91892B7F -:104220008C932FBF0895DC015C968C915C97FD0107 -:10423000E80FF11DE35AFF4F20815C968C915C974B -:1042400090E001968F7390705C968C935C97569675 -:10425000ED91FC91579720835096ED91FC915197E9 -:104260008081806480835B969C915B975C968C9147 -:104270005C97981739F45296ED91FC915397808191 -:104280008F7D80830895DF92EF92FF920F931F93AB -:10429000CF93DF93EC017A018B01D22EE889F98963 -:1042A00082E08083403081EE580780E0680780E03C -:1042B0007807A1F060E079E08DE390E0A801970134 -:1042C0000E946F2521503040404050405695479500 -:1042D0003795279580E12030380798F0E889F989EB -:1042E000108260E874E88EE190E0A80197010E94D6 -:1042F0006F252150304040405040569547953795A6 -:104300002795EC85FD853083EE85FF852083188E0B -:10431000EC89FD89D082EA89FB8980818061808374 -:10432000EA89FB89808188608083EA89FB898081B2 -:1043300080688083EA89FB8980818F7D8083DF911B -:10434000CF911F910F91FF90EF90DF900895FC01A6 -:10435000218D828D30E0205C3F4F281B31092F7367 -:104360003070C9010895FC01918D828D981719F460 -:104370002FEF3FEF06C0828DE80FF11D858D282FAE -:1043800030E0C9010895DC0159969C9159975A96DD -:104390008C915A97981719F42FEF3FEF10C05A9647 -:1043A0008C915A97FD01E80FF11D958D5A968C91CD -:1043B0005A978F5F8F735A968C93292F30E0C901DB -:1043C0000895CF93DF93EC01888D882361F419C0A1 -:1043D0008C9185FF0AC0E889F989808185FF05C035 -:1043E000CE010E941321AA89BB898C9185FD05C04D -:1043F000E889F989808186FD04C00FB607FEE8CF01 -:10440000F4CFDF91CF9108950F931F93CF93DF9354 -:10441000EC01062F9B8D8C8D981791F5E889F98911 -:10442000808185FF2DC0EE89FF896083E889F98945 -:104430008081806480831DC00FB607FC08C0E889B6 -:10444000F989808185FF03C0CE010E9413218C8DE4 -:10445000181791F38B8DFE01E80FF11DE35AFF4F02 -:1044600000831B8FEA89FB8980818062808381E0E1 -:10447000888F81E090E0DF91CF911F910F91089597 -:104480001B8D1F5F1F73E3CF8CEC92E2892B51F0E1 -:1044900080E090E0892B31F00E94CC22882311F03B -:1044A0000E94000008951F920F920FB60F921124E0 -:1044B0002F938F939F93EF93FF93E0917402F0916A -:1044C0007502808182FD16C0E0917A02F0917B0234 -:1044D000208190917D029F5F9F7380917E0298174B -:1044E00071F0E0917D02F0E0EC59FD4F258F909343 -:1044F0007D0205C0E0917A02F0917B02E081FF919C -:10450000EF919F918F912F910F900FBE0F901F9061 -:104510001895109267021092660288EE93E0A0E070 -:10452000B0E08093680290936902A0936A02B0930E -:104530006B0280E591E0909365028093640285ECC4 -:1045400090E0909371028093700284EC90E09093DD -:1045500073028093720280EC90E0909375028093D6 -:10456000740281EC90E0909377028093760282EC63 -:1045700090E0909379028093780286EC90E090939B -:104580007B0280937A0210927D0210927E0210923A -:104590007F0210928002089584E692E00E94A72193 -:1045A00020E0892B09F021E0822F08951F920F92BD -:1045B0000FB60F9211242F933F934F935F936F93F6 -:1045C0007F938F939F93AF93BF93EF93FF9384E673 -:1045D00092E00E941321FF91EF91BF91AF919F91C3 -:1045E0008F917F916F915F914F913F912F910F909C -:1045F0000FBE0F901F901895CF93DF930E94DD1F81 -:104600000E94E409C4E4D2E20E945B092097E1F32E -:104610000E944422F9CF0E94EE1D0895CF92DF92AE -:10462000EF92FF920F931F93CF93DF937C016B0167 -:104630008A01C0E0D0E00FC0D6016D916D01D701B5 -:10464000ED91FC910190F081E02DC7010995C80F13 -:10465000D91F015010400115110571F7CE01DF91EE -:10466000CF911F910F91FF90EF90DF90CF90089521 -:10467000CF93DF93EC016115710519F420E030E070 -:104680000FC0DB010D900020E9F71197A61BB70BB7 -:10469000E881F9810280F381E02DAD0109959C014B -:1046A000C901DF91CF9108950E943823089508959C -:1046B0005058BB27AA270ED075C166D130F06BD1F8 -:1046C00020F031F49F3F11F41EF45BC10EF4E0952D -:1046D000E7FB51C1E92F77D180F3BA17620773075F -:1046E0008407950718F071F49EF58FC10EF4E095DC -:1046F0000B2EBA2FA02D0B01B90190010C01CA019C -:10470000A0011124FF27591B99F0593F50F4503E46 -:1047100068F11A16F040A22F232F342F4427585F38 -:10472000F3CF469537952795A795F0405395C9F750 -:104730007EF41F16BA0B620B730B840BBAF0915008 -:10474000A1F0FF0FBB1F661F771F881FC2F70EC0A7 -:10475000BA0F621F731F841F48F48795779567957A -:10476000B795F7959E3F08F0B3CF9395880F08F063 -:104770009927EE0F979587950895D9D008F481E091 -:1047800008950CD00FC107D140F0FED030F021F4D5 -:104790005F3F19F0F0C0511139C1F3C014D198F343 -:1047A0009923C9F35523B1F3951B550BBB27AA27B2 -:1047B00062177307840738F09F5F5F4F220F331F24 -:1047C000441FAA1FA9F333D00E2E3AF0E0E830D0F0 -:1047D00091505040E695001CCAF729D0FE2F27D0F3 -:1047E000660F771F881FBB1F261737074807AB07C1 -:1047F000B0E809F0BB0B802DBF01FF2793585F4F36 -:104800002AF09E3F510568F0B6C000C15F3FECF34F -:10481000983EDCF3869577956795B795F7959F5FFA -:10482000C9F7880F911D9695879597F90895E1E04E -:10483000660F771F881FBB1F621773078407BA07AD -:1048400020F0621B730B840BBA0BEE1F88F7E09508 -:10485000089504D06894B111D9C00895BCD088F0EF -:104860009F5790F0B92F9927B751A0F0D1F0660F5C -:10487000771F881F991F1AF0BA95C9F712C0B13077 -:1048800081F0C3D0B1E00895C0C0672F782F88278A -:10489000B85F39F0B93FCCF3869577956795B395B6 -:1048A000D9F73EF490958095709561957F4F8F4F25 -:1048B0009F4F0895E89409C097FB3EF4909580952A -:1048C000709561957F4F8F4F9F4F9923A9F0F92FD6 -:1048D00096E9BB279395F695879577956795B79554 -:1048E000F111F8CFFAF4BB0F11F460FF1BC06F5F3A -:1048F0007F4F8F4F9F4F16C0882311F096E911C04C -:10490000772321F09EE8872F762F05C0662371F06C -:1049100096E8862F70E060E02AF09A95660F771F80 -:10492000881FDAF7880F9695879597F90895990F5C -:104930000008550FAA0BE0E8FEEF16161706E80769 -:10494000F907C0F012161306E407F50798F0621B8A -:10495000730B840B950B39F40A2661F0232B242B5F -:10496000252B21F408950A2609F4A140A6958FEF7E -:10497000811D811D089597F99F6780E870E060E0D0 -:1049800008959FEF80EC089500240A9416161706E8 -:1049900018060906089500240A9412161306140630 -:1049A00005060895092E0394000C11F4882352F093 -:1049B000BB0F40F4BF2B11F460FF04C06F5F7F4F4B -:1049C0008F4F9F4F089557FD9058440F551F59F032 -:1049D0005F3F71F04795880F97FB991F61F09F3FEC -:1049E00079F087950895121613061406551FF2CF15 -:1049F0004695F1DF08C0161617061806991FF1CF65 -:104A000086957105610508940895E894BB2766278B -:104A10007727CB0197F908950BD0C4CFB5DF28F0E5 -:104A2000BADF18F0952309F0A6CFABCF1124EECF53 -:104A3000CADFA0F3959FD1F3950F50E0551F629FF9 -:104A4000F001729FBB27F00DB11D639FAA27F00DE7 -:104A5000B11DAA1F649F6627B00DA11D661F829F0E -:104A60002227B00DA11D621F739FB00DA11D621FF3 -:104A7000839FA00D611D221F749F3327A00D611D10 -:104A8000231F849F600D211D822F762F6A2F1124F2 -:104A90009F5750408AF0E1F088234AF0EE0FFF1F45 -:104AA000BB1F661F771F881F91505040A9F79E3F7C -:104AB000510570F060CFAACF5F3FECF3983EDCF376 -:104AC000869577956795B795F795E7959F5FC1F7B9 -:104AD000FE2B880F911D9695879597F90895A1E271 -:104AE0001A2EAA1BBB1BFD010DC0AA1FBB1FEE1F68 -:104AF000FF1FA217B307E407F50720F0A21BB30BB3 -:104B0000E40BF50B661F771F881F991F1A9469F72E -:104B100060957095809590959B01AC01BD01CF018A -:104B20000895EE0FFF1F0590F491E02D0994AA1B44 -:104B3000BB1B51E107C0AA1FBB1FA617B70710F088 -:104B4000A61BB70B881F991F5A95A9F780959095BA -:104B5000BC01CD0108952F923F924F925F926F92C8 -:104B60007F928F929F92AF92BF92CF92DF92EF92FD -:104B7000FF920F931F93CF93DF93CDB7DEB7CA1B7E -:104B8000DB0B0FB6F894DEBF0FBECDBF09942A88A9 -:104B9000398848885F846E847D848C849B84AA8451 -:104BA000B984C884DF80EE80FD800C811B81AA81DE -:104BB000B981CE0FD11D0FB6F894DEBF0FBECDBFA9 -:084BC000ED010895F894FFCF08 -:104BC80054656D70657261747572652053656E7396 -:104BD8006F7200312E30006661696C006F6B0000E7 -:104BE800312E346231202831383834386132290086 -:104BF80073656E736F7200726570656174657200BB -:104C0800307500000100800005030000000000006E -:0C4C180004220E23A721C321B321E121B7 +:101390008091370290913802C817D90724F4C031E0 +:1013A000D10509F09FCF609140017091410180917A +:1013B0004201909143010E94262080E891E00E9422 +:1013C000870D609144017091450180E891E00E9491 +:1013D0008E0CDF91CF911F910F91FF90EF90DF90D6 +:1013E000CF900895AF92BF92CF92EF92FF920F935A +:1013F000CF93DF938EE691E00E946B1B80E891E033 +:1014000060E070E04FEF20E00FEFE3E0EE2EF12C14 +:101410005CE4C52E32E0A32EB12C0E94C11280E8FC +:1014200091E060E071E043E151E020E00E94BF11F3 +:101430008EE691E00E942B198093370210923802B9 +:10144000C0E0D0E008C080E891E06C2F46E020E0EA +:101450000E940D1221968091370290913802C81790 +:10146000D9071CF4C031D10571F7DF91CF910F91ED +:10147000FF90EF90CF90BF90AF900895FC01148241 +:101480000895FC011482668345830895FC016683F8 +:101490000895FC01808187708860808381818F70CE +:1014A000806181836783CF010895CF92DF92EF92AD +:1014B000FF920F931F937C01F7E0CF2ED12CC80E23 +:1014C000D91ECB01BA0142E086010E94D802F60182 +:1014D00001900020E9F73197EC19FD09EE0FEE0FAE +:1014E000EE0FD7018C918770E82BEC9311968C91BD +:1014F00011978F7011968C93C7011F910F91FF90D8 +:10150000EF90DF90CF900895CF93DF93EC01DB0154 +:101510000D900020E9F71197A61B4A2F50E09A0181 +:10152000B3E0220F331FBA95E1F788818770822BD1 +:10153000888389818F708983A93110F049E150E057 +:10154000CE0107960E944503CE01DF91CF91089509 +:10155000FC01818182958F7090E0009731F00197B6 +:1015600011F080E0089587810895CF0107960E94C9 +:10157000F9020895FC01818182958F7090E00097B7 +:1015800061F0059729F020E030E040E050E00EC027 +:10159000278130854185528509C0CF0107960E9479 +:1015A0001703AC01CB01DA019C01AD01B901CA01FD +:1015B0000895CF93DF93FC01EB01818182958F70B9 +:1015C00031F43796CB01BF010E943E0336C061154E +:1015D000710599F18330B9F0843028F4813041F0FD +:1015E000823059F508C08430A9F0853031F51CC02F +:1015F000878190E002C087819085BE014AE050E07B +:101600000E9470031AC0878190854AE050E00E94D2 +:10161000070413C06781708581859285AE012AE039 +:1016200030E00E94910309C06781708581859285B1 +:10163000AE012AE030E00E94E203CE01DF91CF91BB +:1016400008959C01F999FECF32BD21BDF89A80B56D +:101650000895FC0181890895EF92FF920F931F93E3 +:10166000DF93CF93CDB7DEB7CC52D1400FB6F8940D +:10167000DEBF0FBECDBFC95CDE4FE880F980C75327 +:10168000D140CB5CDE4FA881B981C553D14054967F +:101690008C91882389F1EDB7FEB73A970FB6F8948D +:1016A000FEBF0FBEEDBF31968E010F5F1F4FADB76E +:1016B000BEB712961C930E9311978BE291E0938321 +:1016C000828388E690E09583848383E090E097832B +:1016D000868389E090E0918780870E941D04EDB7A2 +:1016E000FEB73A960FB6F894FEBF0FBEEDBF88E680 +:1016F00092E0B8010E946223FE01E75CFE4FCB5CE2 +:10170000DE4FA881B981C553D14054968C919E017A +:101710002F5F3F4F882359F0C9016CE370E0A701A8 +:101720009F010E944E048AE08CAF1DAE07C0C90124 +:101730006BE271E0A7019F010E944E0488E692E0EF +:10174000BE016F5F7F4F0E94622388E692E00E9495 +:10175000EF21C45DDE4F0FB6F894DEBF0FBECDBFE4 +:10176000CF91DF911F910F91FF90EF900895CF934C +:10177000DF93EB010CC08AE592E069E070E040E0A5 +:1017800050E020E030E00E940714C054DF41809117 +:10179000480190914901892B89F08FE1C034D80725 +:1017A00054F7B3C08AE592E068E070E040E050E0B2 +:1017B00020E030E00E940714C05ADF4080914801C9 +:1017C00090914901892B81F087E0C03DD80764F0F2 +:1017D0008AE592E067E070E040E050E020E030E031 +:1017E0000E940714C05DD740809148019091490143 +:1017F000892B81F083E0C83ED80764F08AE592E047 +:1018000066E070E040E050E020E030E00E94071425 +:10181000C85ED3408091480190914901892B81F0A5 +:1018200081E0C43FD80764F08AE592E065E070E0AB +:1018300040E050E020E030E00E940714C45FD14057 +:101840008091480190914901892B79F0CA3FD105D7 +:1018500064F08AE592E064E070E040E050E020E06F +:1018600030E00E940714CA5FD040809148019091F7 +:101870004901892B79F0CD37D10564F08AE592E0F2 +:1018800063E070E040E050E020E030E00E940714A8 +:10189000C857D0408091480190914901892B71F03F +:1018A000C034D1055CF08AE592E062E070E040E08F +:1018B00050E020E030E00E940714EC97809148014E +:1018C00090914901892B71F0C032D1055CF08AE515 +:1018D00092E061E070E040E050E020E030E00E9403 +:1018E00007146E978091480190914901892B99F0D6 +:1018F00060978CF08AE592E060E070E040E050E0B4 +:1019000020E030E00E94071406C08FE0C03AD807FC +:101910000CF454CF47CFDF91CF910895EF92FF920F +:101920000F931F938C017B0188E692E00E94EF21C8 +:10193000C8010E942C1881E090E0909349018093A7 +:101940004801C801B7010E94B70B1F910F91FF908A +:10195000EF9008955F926F927F928F929F92AF92D5 +:10196000BF92CF92DF92EF92FF920F931F93CF938C +:10197000DF937C01562EEA01822E8881982E969460 +:1019800096949694D7011E969C919B83887F826043 +:101990008883DE2CCF2CC7010E941118C7010E943A +:1019A000411830E040E050E060E070E080E090E01E +:1019B000052DF1E03D3F08F4F0E0E1E0423E08F49F +:1019C000E0E031EEA32EAF0EE060F1E0593A08F40A +:1019D000F0E028EAB22EBE0EE1E0B8EABB1608F04D +:1019E000E0E0FE2BA1E0693A08F4A0E0BF2FB85573 +:1019F000E1E0B83A08F0E0E0AE2BF1E0772309F43B +:101A0000F0E0E1E0882309F4E0E08D2D9C2D1CEF4F +:101A100095014B2F5A2F6F2F7E2F0E945216892D22 +:101A200090E04A9714F040E202C0492D495F8D2DA5 +:101A30009C2DBE01282D0E940516682E8D2D9C2DF3 +:101A40000E945C18BA80AB809C808E8019817D805A +:101A50000881662029F497E1C92E91E0D92E04C0AF +:101A60008CE1C82E81E0D82EB701695A7F4FCE0194 +:101A70000E94D90AEDB7FEB77A970FB6F894FEBF69 +:101A80000FBEEDBF3196ADB7BEB71296FC92EE9287 +:101A900011972DED30E033832283B4821582A68224 +:101AA0001782508611869286138684861586212F8A +:101AB00030E02770307037872687708A118A129538 +:101AC0001F70128B138A069506950695048B158A4E +:101AD000D78AC68A918F808F0E942C0BEDB7FEB7F4 +:101AE0007A960FB6F894FEBF0FBEEDBF862DDF913C +:101AF000CF911F910F91FF90EF90DF90CF90BF900B +:101B0000AF909F908F907F906F905F9008954F92CD +:101B10005F926F927F928F929F92AF92BF92CF927D +:101B2000DF92EF92FF920F931F93DF93CF930F9269 +:101B3000CDB7DEB74C01582E492C8E010F5F1F4FD9 +:101B4000B8010E94F217882309F4D5C28981873031 +:101B500008F0D1C2C4010E940215482F65E1662E2B +:101B6000712C680C791CC401B3010E94F815C401E2 +:101B70006981A80121E00E949414D40155960C912A +:101B800006950695069510E0F401E00FF11F148EFE +:101B9000F401E788D08CC18CB38CF688A28CB40198 +:101BA000695A7F4FC3010E94D90A2DB73EB726510B +:101BB00030400FB6F8943EBF0FBE2DBFEDB7FEB755 +:101BC0003196ADB7BEB712969C928E92119720EBCC +:101BD00030E033832283E4821582D6821782C08666 +:101BE0001186B28613862F2D30E02770307035872E +:101BF0002487A6861786F2945FE0F522F08A118A80 +:101C0000138B028B958B848B0E942C0BF40185899E +:101C100087702DB73EB72A5E3F4F0FB6F8943EBF90 +:101C20000FBE2DBF8230B1F000D000D0ADB7BEB72F +:101C300012969C928E9211978FE990E014969C9345 +:101C40008E9313970E942C0B80E00F900F900F90B3 +:101C50000F9052C2F4012689022F0770128DE78877 +:101C6000F08C318D9289992379F1033069F5173021 +:101C700059F50E94D31F7370807090700E942620C7 +:101C8000D4011E968C911E9757968C935797599610 +:101C9000EC9259978FEF5B968C935B9788E05A96FE +:101CA0008C935A9756968C915697807F8360569660 +:101CB0008C935697C30150966C910E94490AAC01CF +:101CC000C4016E2D21E0F5C1F4018685381709F0B5 +:101CD000DBC123FF21C0D401D696759681E2019025 +:101CE0000D928150E1F7D401D7968C91D797877FD9 +:101CF000D7968C93D7971E968C911E97D8968C93D7 +:101D0000D89757968C915797DA968C93B4016A5C62 +:101D10007F4FC4010E945011F4018289882319F178 +:101D20008785F81601F12E2D30E081E790E0880ECE +:101D3000991ED401ED91FC918FE89FEF880E991EBA +:101D4000E20FF31F80818F1571F0F082F999FECFB9 +:101D50001FBA2D5F3F4F32BD21BDF0BC0FB6F894C6 +:101D6000FA9AF99A0FBE033009F081C1183009F0D0 +:101D700053C0D40154968C91882309F04DC0C301FF +:101D80000E94A80A482F282F30E0F401808990E0B3 +:101D90000197281739070CF0AEC14F5F408B278998 +:101DA0002787F999FECF1FBA81E090E092BD81BDEF +:101DB00020BD0FB6F894FA9AF99A0FBED401509646 +:101DC0003C91F999FECF1FBA82E090E092BD81BDAF +:101DD00030BD0FB6F894FA9AF99A0FBEEDB7FEB778 +:101DE00038970FB6F894FEBF0FBEEDBF3196ADB772 +:101DF000BEB712969C928E9211978AE890E09383D8 +:101E0000828324831582368317820E942C0B80E004 +:101E1000EDB7FEB73896A3C0EE2009F028C1163002 +:101E200009F4A3C0173038F4113009F40FC114308D +:101E300009F061C112C01A3009F4B0C01D3009F0B8 +:101E40005AC128E088E190E00FB6F894A8958093F5 +:101E500060000FBE20936000FFCFD4011E968C91CE +:101E60008F3F09F048C1C3010E94A80AF401868788 +:101E70008F3FA9F400D000D0ADB7BEB712969C92A8 +:101E80008E92119784E890E014969C938E9313970A +:101E90000E942C0B0F900F900F900F90FFCF282FC8 +:101EA00030E040E050E060E070E080E090E0E22E62 +:101EB00001E03D3F08F400E011E0423E08F410E08C +:101EC0000F511060E1E0593A08F4E0E01855F1E0F4 +:101ED000183A08F0F0E0EF2BF1E0693A08F4F0E08E +:101EE000BE2FB855E1E0B83A08F0E0E0FE2BA1E0E3 +:101EF000772309F4A0E0E1E0882309F4E0E0852DF0 +:101F0000942D61E04CEFF42E2B2F3F2F4A2F5E2FA4 +:101F10000E947317F4012685F999FECF1FBA12BCEF +:101F200011BC20BD0FB6F894FA9AF99A0FBE00D0F2 +:101F300000D000D0EDB7FEB73196ADB7BEB7129660 +:101F40009C928E9211978DE790E0938382832483F5 +:101F500015820E942C0B80E0EDB7FEB736960FB6C7 +:101F6000F894FEBF0FBEEDBFC7C0C3010E94A80A10 +:101F700020E08D3409F421E0D40151968C915197E1 +:101F8000821709F4B8C051962C93F999FECF1FBA65 +:101F900083E091E092BD81BD20BDA8C0F40182899B +:101FA000882309F4A8C0C3010E94A80A833409F059 +:101FB000A2C000D000D0ADB7BEB712969C928E9250 +:101FC000119783E790E014969C938E9313970E9449 +:101FD0002C0B20E030E00F900F900F900F90D40169 +:101FE000AF58BF4F9FEFED91FC911197E20FF31F98 +:101FF00080818F3F81F09083F999FECF1FBA2D5FCA +:102000003F4F32BD21BD2350304090BD0FB6F894F4 +:10201000FA9AF99A0FBE2F5F3F4F2230310519F718 +:10202000F4018685878B118E8FEF838F8AE0828FF4 +:102030008689807F8360868BC3016FE171E00E9497 +:10204000840ABC01C4010E94501155C023E730E04E +:10205000820E931ED4010D911C910115110509F4F6 +:102060004AC0C3010E94BA0AF801099544C0E5E7D5 +:10207000F0E08E0E9F1ED401ED91FC91309711F08F +:10208000C301099581E038C09923A9F18981813084 +:1020900091F584010F581F4FD801ED91FC91E30F8A +:1020A000F11D6081862F81508E3F30F4C401A30161 +:1020B00020E00E94AA0C1FC0C401F4016785A3019F +:1020C00020E00E94AA0C6E2D70E0D801ED91FC91E9 +:1020D000E60FF71F80818F1571F0F082F999FECF1E +:1020E0001FBA6D5F7F4F72BD61BDF0BC0FB6F89433 +:1020F000FA9AF99A0FBE80E00F90CF91DF911F916D +:102100000F91FF90EF90DF90CF90BF90AF909F9096 +:102110008F907F906F905F904F900895EF92FF9215 +:102120000F931F93CF93DF93EC010E94D31F7B018A +:102130008C0103C0CE010E94870D0E94D31F6E192F +:102140007F09800B910B605D77408040904090F359 +:10215000DF91CF911F910F91FF90EF900895EF9233 +:10216000FF920F931F938C0100591F4FF8011082AB +:10217000005710409FEFF801908B8685878B918FD9 +:10218000938F87E0828F8689807F8360868B75E15D +:10219000E72EF12CE00EF11EC7016FE171E00E9405 +:1021A000840AC8016FEFA70121E00E94AA0CC801B0 +:1021B0000E948E101F910F91FF90EF900895EF9263 +:1021C000FF920F931F93CF93DF93EC0100D000D0C9 +:1021D000EDB7FEB79283818383E191E0948383839B +:1021E0000E942C0B0F900F900F900F908E85282F30 +:1021F00030E040E050E060E070E080E090E0E22E0F +:1022000001E03D3F08F400E011E0423E08F410E038 +:102210000F511060E1E0593A08F4E0E01855F1E0A0 +:10222000183A08F0F0E0EF2BF1E0693A08F4F0E03A +:10223000BE2FB855E1E0B83A08F0E0E0FE2BA1E08F +:10224000772309F4A0E0E1E0882309F4E0E0CE017F +:1022500061E02CEFF22E2B2F3F2F4A2F5E2F0E9492 +:1022600073178E858F8B198E8FEF8B8F83E08A8FFC +:102270008E89807F83608E8BCE0145966FE171E001 +:102280000E94840ABC01CE010E945011CE010E941E +:102290008E10DF91CF911F910F91FF90EF900895D5 +:1022A0001F93CF93DF93EC01DB0120E011968C911B +:1022B00011978770833009F421E08E858F3F59F4A0 +:1022C000222329F015968C911597833021F0CE01A9 +:1022D0000E94DF102CC08A89882331F114969C91CA +:1022E0001497CF58DF4FE881F981C157D040E90FEB +:1022F000F11D6081862F81508E3F30F4992321F0AB +:10230000CE01AD0120E00DC0222371F015968C9115 +:102310001597843049F49F3F39F4CE016FEFAD013A +:1023200021E00E94AA0C27C08C89882311F080E04C +:1023300022C0CE016F85AD0120E00E94AA0C182FAB +:10234000882329F011E0C059DF4F188213C08B8910 +:10235000882351F0C059DF4F8881C057D040863064 +:1023600018F0CE010E94AF10FE01E059FF4F8081AE +:102370008F5F8083812FDF91CF911F910895FF920E +:102380000F931F93CF93DF93EC018A01F22E611517 +:102390007105E1F08E858F8B198E8FEF8B8F8BE01F +:1023A0008A8F30E02170307053E0220F331F5A952E +:1023B000E1F78E89807F8360822B8E8BCE014596DC +:1023C0000E94840ABC01CE010E9450110115110522 +:1023D000F1F08E858F8B198E8FEF8B8F8CE08A8F2B +:1023E0002F2D30E02170307043E0220F331F4A95CB +:1023F000E1F78E89807F8360822B8E8BCE0145969C +:10240000B8010E94840ABC01CE010E945011DF91E4 +:10241000CF911F910F91FF9008950F931F938C01FF +:10242000FC018685878B118E638F428F30E021708F +:10243000307063E0220F331F6A95E1F78689807F51 +:10244000822B868BC801459660E271E00E94840A67 +:10245000BC01C8010E9450111F910F910895DC0129 +:10246000FB011E968C911E97828350E04170507044 +:1024700083E0440F551F8A95E1F78181807F816059 +:10248000842B8183CD010E945011811181E0089538 +:102490000F931F938C0180E091E00E9419230F5845 +:1024A0001F4FF8018083918363E070E040E051E0CA +:1024B00021E23BE00E9454031F910F910895EF9297 +:1024C000FF920F931F93CF93DF93EC01E62EF42E30 +:1024D0008901C059DF4F1882C057D040CE010E94F9 +:1024E0009518CE010E947A14882389F400D000D078 +:1024F000EDB7FEB7D283C18380E291E094838383FA +:102500000E942C0B0F900F900F900F90FFCFCE01D9 +:1025100061E00E94EE15CE0162E040E00E940C17DF +:10252000CE010E943817CE016F2D0E941F16CE01DA +:102530006E2D0E94F316CE01B8010E94C216CE0184 +:1025400065E04FE00E94E415CE0162E070E00E9479 +:10255000A716CE010E945417CE0162E0EE24EA9441 +:102560002CEFF22E01EE18EA28EA30E040E050E0CD +:102570000E947317DF91CF911F910F91FF90EF9001 +:1025800008958F929F92AF92BF92CF92DF92EF9277 +:10259000FF920F931F93CF93DF93EC014B01D42E47 +:1025A000122F88E692E040E052EC61E070E026E015 +:1025B0000E9451211C8A1A8A112311F081E08A8B12 +:1025C000CB58DF4F99828882C557D0408A898823AB +:1025D00019F0CE010E944812CE01B7014C2D950191 +:1025E0000E945F12CE010E9660E070E043E050E082 +:1025F00021E23BE00E945403CE01419663E271E088 +:1026000041E050E021E23BE00E94540389898F3F82 +:1026100011F481E0898B0F3F19F00F871B8A02C0EC +:1026200081E08B8B8FEFD812DE868B89882331F087 +:102630008F858F3F19F4CE010E94AF108E858F3F9A +:1026400019F4CE010E94DF108A89882319F420E34F +:1026500031E002C027E331E0ADB7BEB718970FB63F +:10266000F894BEBF0FBEADBFEDB7FEB73196129660 +:10267000DC93CE9311978DE291E09383828335832F +:1026800024838E85868317820E942C0BDC2ECD2E10 +:102690008DB79EB708960FB6F8949EBF0FBE8DBF3C +:1026A0008E85282F30E040E050E060E070E080E070 +:1026B00090E0E22E01E03D3F08F400E011E0423EF0 +:1026C00008F410E00F511060E1E0593A08F4E0E03E +:1026D0001855F1E0183A08F0F0E0EF2BF1E0693A14 +:1026E00008F4F0E0BE2FB855E1E0B83A08F0E0E0B9 +:1026F000FE2BA1E0772309F4A0E0E1E0882309F4B0 +:10270000E0E08D2D9C2D60E04CEFF42E2B2F3F2F21 +:102710004A2F5E2F0E9473178E85282F30E040E0ED +:1027200050E060E070E080E090E0E22E01E03D3FAC +:1027300008F400E011E0423E08F410E00F51106090 +:10274000E1E0593A08F4E0E01855F1E0183A08F0F1 +:10275000F0E0EF2BF1E0693A08F4F0E0BE2FB85555 +:10276000E1E0B83A08F0E0E0FE2BA1E0772309F4BD +:10277000A0E0E1E0882309F4E0E08D2D9C2D61E0EC +:102780003CEFF32E2B2F3F2F4A2F5E2F0E94731703 +:102790008A89882311F441E101C042E1CE016FEF43 +:1027A00020E00E940D128E858F8B198E8FEF8B8FFC +:1027B00086E08A8F8E89807F83608E8BCE014596DE +:1027C0006F850E94490ABC01CE010E945011CE01C2 +:1027D0000E948E10DF91CF911F910F91FF90EF908B +:1027E000DF90CF90BF90AF909F908F9008950F9300 +:1027F0001F938C010E946914C80145960E943E0AED +:10280000C801C6960E943E0A1F910F910895411576 +:10281000510529F480917A008F7780937A006A308D +:102820007105B9F063FD02C0E8E001C0E8E2862F5F +:102830008770E82B88E190E00FB6F894A895809314 +:1028400060000FBEE0936000809160008064809320 +:102850006000232BA1F483B7817F846083BFF89449 +:1028600083B7816083BF85B7806685BF8F7D85BF55 +:102870007894889583B78E7F83BF78940EC083B792 +:10288000817F846083BFF89483B7816083BF78942D +:10289000889583B78E7F83BF7894452B29F48091E8 +:1028A0007A00806880937A0008951F920F920FB685 +:1028B0000F9211248F9388E10FB6F89480936000F3 +:1028C000109260000FBE8F910F900FBE0F901F905F +:1028D0001895FC01608341831282138280E2848315 +:1028E0001582168217821086118612861386148628 +:1028F00015860895FC0183810895FF920F931F931D +:102900008C01F62E81E00E94E71880E00E94F0180A +:1029100084E00E94F518F80181816F2D0E94CD207E +:102920001F910F91FF900895EF92FF920F931F93C5 +:10293000CF93DF93EC01162F042FF52EE22E60E0EB +:102940000E947D141770186A1EBD0DB407FEFDCFDE +:10295000802F9F2D9C01F9012E2D213208F020E2BD +:102960008EB508C080818EBD0DB407FEFDCF3196B7 +:102970008EB521502223B1F7CE0161E00E947D1473 +:10298000DF91CF911F910F91FF90EF900895CF931A +:10299000DF93EC0160E00E947D1480E58EBD0DB4F4 +:1029A00007FEFDCF8EB583E78EBD0DB407FEFDCFCC +:1029B0008EB5CE0161E00E947D14DF91CF91089524 +:1029C0001F93CF93DF93EC01162F60E00E947D14DC +:1029D0001EBD0DB407FEFDCF1EB5CE0161E00E9405 +:1029E0007D14812FDF91CF911F9108956FEF0E9489 +:1029F000E014089561EE0E94E014089562EE0E94D2 +:102A0000E01408951F93CF93DF93EC0160E00E94E0 +:102A10007D1480E68EBD0DB407FEFDCF8EB58FEF21 +:102A20008EBD0DB407FEFDCF1EB5CE0161E00E9444 +:102A30007D14113220F0CE010E94FE1410E0812F8F +:102A4000DF91CF911F910895EF92FF920F931F9303 +:102A5000CF93DF93EC01F62EE72E9C81142F941771 +:102A600008F4192F8D81882311F000E002C0092F8E +:102A7000011BCE0160E00E947D1481E68EBD0DB485 +:102A800007FEFDCF8F2D9E2D9C01F901FEB49FEF17 +:102A900007C09EBD0DB407FEFDCF8EB581931150CA +:102AA0001123B9F707C08EBD0DB407FEFDCF9EB54B +:102AB000015001C08FEF0023B1F7CE0161E00E9409 +:102AC0007D148F2DDF91CF911F910F91FF90EF908B +:102AD0000895DF92EF92FF920F931F93CF93DF93AE +:102AE000EC01E62ED72EF22E9C81142F941708F4B9 +:102AF000192F8D81882311F000E002C0092F011BDE +:102B0000CE0160E00E947D14FEBC0DB407FEFDCF37 +:102B10008E2D9D2D9C01F901FEB408C080818EBDD3 +:102B20000DB407FEFDCF31968EB511501123B1F7CC +:102B300006C01EBC0DB407FEFDCF8EB501500023AC +:102B4000C1F7CE0161E00E947D148F2DDF91CF91FE +:102B50001F910F91FF90EF90DF900895CF93DF9337 +:102B6000EC01222311F420EA01C020EBCE010E94E7 +:102B70006915888161E00E94CD20888160E00E9413 +:102B8000CD20DF91CF9108950F931F93CF93DF93C3 +:102B9000EC01162F042F60E00E947D141F7110625B +:102BA0001EBD0DB407FEFDCF1EB50EBD0DB407FE54 +:102BB000FDCF8EB5CE0161E00E947D14812FDF91A3 +:102BC000CF911F910F910895262F4F702295207F4E +:102BD000242B64E0422F0E94C4150895662319F047 +:102BE00061E04FE302C061E040E00E94C415089537 +:102BF0000F931F938C010E942415C80167E040E7E2 +:102C00000E94C4151F910F910895CF93DF93EC019B +:102C10000E94AE15CE010E94F6148073D9F3CE0146 +:102C200067E040E70E94C41584FD02C081E004C053 +:102C3000CE010E94FA1480E0DF91CF910895462FD3 +:102C4000603808F04FE765E00E94C4150895EF92E0 +:102C5000FF920F931F93CF93DF937C01162FEA010E +:102C6000022F60E00E947D141F7110621EBD0DB422 +:102C700007FEFDCF1EB508C088818EBD0DB407FECE +:102C8000FDCF21968EB501500023B1F7C70161E059 +:102C90000E947D14812FDF91CF911F910F91FF90A2 +:102CA000EF900895EF92FF920F931F93DF93CF93CE +:102CB000CDB7DEB728970FB6F894DEBF0FBECDBFF5 +:102CC0007C0109831A832B833C834D835E836F834E +:102CD00078878E010F5F1F4F6AE0A80125E00E94F0 +:102CE0002716C70160E1A80125E00E942716C70149 +:102CF00061E1F70144810E94C41528960FB6F8944B +:102D0000DEBF0FBECDBFCF91DF911F910F91FF901E +:102D1000EF9008951F93CF93DF93EC01162F60E09F +:102D20000E947D141F711EBD0DB407FEFDCF8EB530 +:102D30008FEF8EBD0DB407FEFDCF1EB5CE0161E055 +:102D40000E947D14812FDF91CF911F9108950F93E1 +:102D50001F93CF93DF938C01EB0160E00E948A16F2 +:102D6000482F437F209729F0219711F4486001C034 +:102D70004C60C80160E00E94C415DF91CF911F91A3 +:102D80000F910895FF920F931F93CF93DF93EC0160 +:102D90008B0166E00E948A161A82F7EDFF2EF82258 +:102DA0000230110511F480E206C00130110529F44A +:102DB00081E08A8388E0F82A01C01A82CE0166E0A9 +:102DC0004F2D0E94C415CE0166E00E948A168F1511 +:102DD00011F481E002C01A8280E0DF91CF911F914F +:102DE0000F91FF9008951F93CF93DF93EC01162F5F +:102DF00066E00E948A16143010F04CE003C0412FA8 +:102E0000440F4F5F887F482BCE0166E00E94C415B7 +:102E1000DF91CF911F9108950F931F93CF93DF936D +:102E2000EC01162F042F6730F0F461E00E948A163F +:102E3000282F002351F081E090E002C0880F991FF5 +:102E40001A95E2F7422F482B0AC081E090E002C0B9 +:102E5000880F991F1A95E2F7482F40954223CE011B +:102E600061E00E94C415DF91CF911F910F910895E9 +:102E70000F931F938C010E94C714C8016DE10E943B +:102E80008A16482F4660C8016DE10E94C415C8012A +:102E90006CE10E948A16482F4360C8016CE10E94D1 +:102EA000C4151F910F9108950F931F938C010E94D9 +:102EB000C714C8016DE10E948A16482F4460C801FA +:102EC0006DE10E94C415C8016CE10E948A16482F6A +:102ED0004F63C8016CE10E94C41581E0F80185834D +:102EE0001F910F910895BF92CF92DF92EF92FF92C0 +:102EF0000F931F93DF93CF93CDB7DEB728970FB60D +:102F0000F894DEBF0FBECDBF6C01B62EE982FA8207 +:102F10000B831C832D833E834F835887662389F45C +:102F2000F601E6828A8187838B8180878C81818705 +:102F30008D8182878E8183878F818487888585872D +:102F400007C0F6E0F61708F43DC081E0681761F4A9 +:102F5000EB2DF0E0EA5BFE4FE491C6016E2FAE016F +:102F60004F5F5F4F25E00BC0E62FF0E0EA5BFE4FBE +:102F7000E491C6016E2FAE014F5F5F4F21E00E94CA +:102F800027160B2D10E0F801E45BFE4FE491C6011B +:102F90006E2FF60144810E94C415C60162E00E94B2 +:102FA0008A16005C1E4FF801649121E030E002C0F7 +:102FB000220F331F6A95E2F7482F422BC60162E0C9 +:102FC0000E94C41528960FB6F894DEBF0FBECDBF81 +:102FD000CF91DF911F910F91FF90EF90DF90CF90F5 +:102FE000BF9008950F931F93CF93DF93EC018B0154 +:102FF00067E10E948A1680FF02C080E00DC00115C3 +:10300000110549F0CE010E94F61490E09595879540 +:103010008770F801808381E0DF91CF911F910F913C +:103020000895CF93DF93EC0160E00E948A1681FD42 +:1030300010C0CE0160E00E948A16482F4260CE0187 +:1030400060E00E94C41565E070E080E090E00E94BE +:103050002620DF91CF9108950F931F938C01FC01DF +:10306000808160E00E94CD20C80160E00E948A1645 +:10307000482F4D7FC80160E00E94C4151F910F9139 +:1030800008950F931F938C01FC01808160E00E94E2 +:10309000CD20C8010E94FA14C8010E94FE14C80184 +:1030A00060E00E948A16482F4E7FC80160E00E94AF +:1030B000C4151F910F910895CF93DF93EC010E94E7 +:1030C0001118CE0160E00E948A16482F4160CE019F +:1030D00060E00E94C415CE0167E040E70E94C4157D +:1030E0008E819F81892B9885892B9985892B9A853B +:1030F000892B9B85892B9C85892B9D85892B41F06C +:10310000AE014A5F5F4FCE016AE025E00E942716BC +:10311000CE010E94FE14CE010E94FA14888161E063 +:103120000E94CD20DF91CF910895CF93DF93EC01E2 +:10313000888161E00E948E20898161E00E948E205A +:103140000E940619888160E00E94CD20CE0161E0D6 +:103150000E947D1465E070E080E090E00E942620EF +:10316000CE0165E04FE00E94E415CE0162E070E020 +:103170000E94C216882311F081E08B83CE0160E0AB +:1031800070E00E94C216CE0162E070E00E94A716B5 +:10319000CE0167E040E70E94C415CE016CE40E94B6 +:1031A0001F16CE010E94FE14CE010E94FA14CE0119 +:1031B0000E941118CE0160E00E948A16482F4E7FAF +:1031C000CE0160E00E94C415DF91CF91089588235D +:1031D00019F48CB5806202C08CB58F7D8CBD0895CA +:1031E0009CB5937F982B9CBD08952CB5382F3370D8 +:1031F0002C7F322B3CBD2DB590E09595879595950C +:10320000879581702E7F822B8DBD08958AE061E0C5 +:103210000E94CD208AE061E00E948E208CB5806102 +:103220008CBD8CB580648CBD8DE061E00E948E20E9 +:103230008BE061E00E948E200895FC0185EE99E10B +:10324000918B808B75836483138210828FE190E071 +:10325000928381830895FC01838108956F927F9208 +:103260008F929F92AF92BF92CF92DF92EF92FF9296 +:103270000F931F93CF93DF93FB01EA01998180E0C5 +:10328000288130E0282B392BE081E23231F0E8321E +:1032900021F0E03109F084C04BC04C814F3329F15B +:1032A000403420F44F3109F07BC02FC04F3579F006 +:1032B0004F3709F075C0B901882777FD8095982FA1 +:1032C0000E946A2420E030E040E85DE32EC035959E +:1032D0002795B901882777FD8095982F0E946A2449 +:1032E00020E030E040E05EE320C03595279535953D +:1032F0002795B901882777FD8095982F0E946A2429 +:1033000020E030E040E85EE310C093E0359527957B +:103310009A95E1F7B901882777FD8095982F0E944B +:103320006A2420E030E040E05FE30E941A2532C0CA +:10333000EF8035952795B901882777FD8095982FDF +:103340000E946A2420E030E040E85EE30E946623A9 +:103350003B014C016E2D70E02E81621B71098827A4 +:1033600077FD8095982F0E946A245B016C01FF24F1 +:1033700000E010E0C801B7010E9468249B01AC0185 +:10338000C601B5010E94CF239B01AC01C401B3016A +:103390000E9467239B01AC01832F942F752F00C0DF +:1033A000262F382F492F572FB901CA01DF91CF910E +:1033B0001F910F91FF90EF90DF90CF90BF90AF9053 +:1033C0009F908F907F906F9008950895EF92FF9255 +:1033D0000F931F938C017B01FC01848195810E94D6 +:1033E000E51CF80184819581B7010E94C41CF80195 +:1033F0008481958164EB40E00E947F1CF801848108 +:1034000095810E94FA1BF82EF801848195810E9413 +:10341000E51C80E0FF2009F481E01F910F91FF90EF +:10342000EF9008950F931F938C01FC018481958187 +:103430000E94E51CF801848195810E94BF1CF8015F +:103440008481958164E440810E947F1CF801818120 +:1034500092818F33910551F08F35910561F04F972F +:1034600079F46EE570E080E090E00EC06CEB70E007 +:1034700080E090E009C067E771E080E090E004C080 +:103480006EEE72E080E090E00E9426201F910F9186 +:103490000895CF92DF92EF92FF920F931F938C01CA +:1034A0006B017A01FC01848195810E94E51CF80181 +:1034B00084819581B6010E94C41CF80184819581A4 +:1034C0006EEB40E00E947F1CF801848195810E9490 +:1034D0002E1CF7018083F801848195810E942E1CA7 +:1034E000F7018183F801848195810E942E1CF701E8 +:1034F0008283F801848195810E942E1CF7018383C9 +:10350000F801848195810E942E1CF7018483F801C3 +:10351000848195810E942E1CF7018583F8018481A6 +:1035200095810E942E1CF7018683F8018481958184 +:103530000E942E1CF7018783F801848195810E94E7 +:103540002E1CF7018087F801848195810E94E51C7B +:103550001F910F91FF90EF90DF90CF9008950F9300 +:103560001F938A010E94491AC80168E00E94E91B62 +:1035700020E0F8019085891709F421E0822F1F913E +:103580000F910895CF92DF92EF92FF920F931F93C6 +:10359000DF93CF93CDB7DEB729970FB6F894DEBF90 +:1035A0000FBECDBF8C017B016E010894C11CD11CE4 +:1035B000A6010E94AF1A882329F460E070E08EEF24 +:1035C00092EC05C0C801B701A6010E942E192996E8 +:1035D0000FB6F894DEBF0FBECDBFCF91DF911F9124 +:1035E0000F91FF90EF90DF90CF9008950F931F936E +:1035F0008B01CB0167E00E94E91B20E0F801978175 +:10360000891709F421E0822F1F910F910895EF92FD +:10361000FF920F931F93CF93DF93EC017B01042F55 +:103620008C819D810E94DC1B10E007C0CE01B70198 +:103630000E94F61A8823A9F00AC08C819D81B701E7 +:103640000E94301D882329F0101759F4EFCF81E034 +:1036500001C080E0DF91CF911F910F91FF90EF901B +:1036600008951F5F0117A8F3E8CFEF92FF920F9321 +:103670001F93DF93CF93CDB7DEB728970FB6F8949B +:10368000DEBF0FBECDBF7C01462F8E010F5F1F4FE7 +:10369000B8010E94071BC701B8010E94C21A2896F0 +:1036A0000FB6F894DEBF0FBECDBFCF91DF911F9153 +:1036B0000F91FF90EF9008950E94351B26E636E6A5 +:1036C00046EE5FE30E941A2520E030E040E052E43D +:1036D0000E94672308959F92AF92BF92CF92DF928C +:1036E000EF92FF920F931F93DF93CF93CDB7DEB787 +:1036F00061970FB6F894DEBF0FBECDBF8C01FC0101 +:10370000848195810E94DC1BF80113827E0108945C +:10371000E11CF11CE9E0AE2EB12CAC0EBD1E7FE722 +:10372000C72ED12C992493942DC0C801B7010E94B3 +:10373000F61A882339F1F8018081882341F4C80101 +:10374000B7010E94E619882311F0F8019082C801A0 +:10375000B701A5010E94491A8981803121F4F8013D +:10376000D282C1820BC08D85282F30E0F801818183 +:1037700092818217930714F432832183F8018381A5 +:103780008F5F8383F80184819581B7010E94301D8A +:10379000882359F661960FB6F894DEBF0FBECDBFF1 +:1037A000CF91DF911F910F91FF90EF90DF90CF901D +:1037B000BF90AF909F900895FC01138615861486E4 +:1037C00087E090E012863197009711F00197FACFC9 +:1037D0000895DC0180E00BC0E82FF0E08D9190E0CF +:1037E000E827F927ED5FFC4FE49161508E2F6623A7 +:1037F00099F70895DF92EF92FF920F931F93FC01C8 +:10380000E08001811281F894F80181818E29818301 +:103810008281DE2CD0948D21828383E090E00E940F +:10382000E11FF80181818D2181838AE090E00E946F +:10383000E11FF8010081789485E390E00E94E11F88 +:1038400080E010E0FF240E211F21012B09F081E010 +:103850001F910F91FF90EF90DF900895EF92FF92EC +:103860000F931F93CF93DF937C0101E010E0C0E042 +:10387000D0E0C7010E94FA1B8111102B000F219686 +:10388000C830D105B1F7812FDF91CF911F910F91F2 +:10389000FF90EF9008951F93CF93DF93FC01108169 +:1038A000C181D281912F909560FF12C0F8948A81D6 +:1038B00089238A838981812B89838AE090E00E9411 +:1038C000E11F8A81812B8A83789487E390E011C07D +:1038D000F8948A8189238A838981812B898381E471 +:1038E00090E00E94E11F8A81812B8A83789485E091 +:1038F00090E00E94E11FDF91CF911F910895CF9238 +:10390000DF92EF92FF920F931F93CF93DF938C017F +:10391000E42EFF24F394C0E0D0E0C62EDD2460E066 +:103920008F2D90E08C219D21892B09F061E0C80149 +:103930000E944B1CFF0C2196C830D10581F7EE2068 +:10394000A9F4F894D8011196ED91FC911297918108 +:103950008C918095982391831196ED91FC9112970B +:1039600092818C918095982392837894DF91CF9166 +:103970001F910F91FF90EF90DF90CF9008956CEC26 +:1039800040E00E947F1C0895EF92FF920F931F93D7 +:10399000CF93DF938C017B0165E540E00E947F1CA3 +:1039A000C0E0D0E0F701EC0FFD1FC801608140E0EE +:1039B0000E947F1C2196C830D105A1F7DF91CF91DD +:1039C0001F910F91FF90EF900895DF92EF92FF9279 +:1039D0000F931F93CF93DF93FC010081C181D281AC +:1039E000F8948981D02ED0948D21898378941DE715 +:1039F000E02EFF24115059F182E090E00E94E11F77 +:103A0000888190E08E219F21892BA1F3F8948A81EF +:103A10008D218A838981802B8983789484EF91E03A +:103A20000E94E11FF89489818D21898380E590E0CF +:103A30000E94E11F888110E090E0E822F922EF283F +:103A400009F411E0789484EA91E00E94E11F812F4B +:103A5000DF91CF911F910F91FF90EF90DF9008952C +:103A6000AF92BF92DF92EF92FF920F931F93CF938B +:103A7000DF93EC015B018D85882309F061C0CE01E5 +:103A80000E94E51C882329F41B861D861C8690E075 +:103A90006BC0CE0160EF40E00E947F1C01E0DD249E +:103AA000EE24FF24F394CE010E94FA1B182FCE01BE +:103AB0000E94FA1B113019F48130E1F439C0181753 +:103AC00009F58B85081770F460E0FE01EE0DF11D1D +:103AD000838190E02F2D30E082239323181619065E +:103AE0004CF003C060E0081729F0093008F04CC022 +:103AF0000C874AC0FE01EE0DF11D83818F2983835F +:103B000061E009C0612FFE01EE0DF11D9F2D909522 +:103B1000838189238383CE010E944B1C0F5FFF0C9E +:103B200019F4E394FF24F39487E08E1508F0BBCFDB +:103B3000013430F0DB86DD2011F581E08D871FC078 +:103B40001B861D861C8690E0DE0120E030E0F5013A +:103B5000E20FF31F13968C91139780832F5F3F4FD3 +:103B600011962830310599F7892FDF91CF911F9158 +:103B70000F91FF90EF90DF90BF90AF9008958B81F1 +:103B80008823F1F291E0E0CFD02EBDCFFF920F93CA +:103B90001F938C01F62E862F60E00E948E208F2DC1 +:103BA00090E0FC01EB5CFB4FE491D801EC938F5D5E +:103BB0009B4FFC016491E62FF0E0EE0FFF1FE95EE2 +:103BC000FB4F8591949112969C938E9311971B961F +:103BD0001C921B971D961C921D971C961C92F80117 +:103BE00087E090E012863197009711F00197FACFA5 +:103BF0001F910F91FF9008950F931F93CF93DF9321 +:103C0000BC018230910510F462E070E0E09107039E +:103C1000F091080340E050E0A0E0B0E024C08081D3 +:103C2000918186179707D0F08617970771F48281E4 +:103C30009381109729F013969C938E9312972CC022 +:103C4000909308038093070327C04115510519F08D +:103C50008417950718F48D01AC01EF01DF01828113 +:103C600093819C01F9013097D1F641155105F9F086 +:103C7000CA01861B970B8430910580F48A819B8151 +:103C80000115110521F0F8019383828304C09093FC +:103C9000080380930703FE01329645C0FE01E80F3A +:103CA000F91F619371930297998388833CC0809137 +:103CB000050390910603892B41F480914C0190916A +:103CC0004D01909306038093050340914E0150915E +:103CD0004F014115510541F44DB75EB780914A013E +:103CE00090914B01481B590B20910503309106031D +:103CF00024173507B0F4CA01821B930B8617970768 +:103D000080F0AB014E5F5F4F8417950750F0420F74 +:103D1000531F5093060340930503F9016193719378 +:103D200002C0E0E0F0E0CF01DF91CF911F910F9151 +:103D30000895CF93DF939C01009709F48FC0EC01A5 +:103D400022971B821A826091070370910803611504 +:103D5000710581F488819981820F931F2091050359 +:103D6000309106032817390739F5D0930603C0931D +:103D7000050374C0DB0140E050E0AC17BD0708F15B +:103D8000BB83AA83FE0121913191E20FF31FAE178D +:103D9000BF0779F48D919C911197280F391F2E5FE1 +:103DA0003F4F3983288312968D919C9113979B8363 +:103DB0008A834115510571F4D0930803C09307031A +:103DC0004DC012968D919C911397AD01009711F003 +:103DD000DC01D3CFDA011396DC93CE931297FA016C +:103DE00021913191E20FF31FCE17DF0769F488812B +:103DF0009981280F391F2E5F3F4FFA0131832083AD +:103E00008A819B8193838283E0E0F0E0DB0112965C +:103E10008D919C911397009719F0BC01FD01F6CF8D +:103E2000AB014E5F5F4FDB018D919C91840F951F1D +:103E300020910503309106032817390779F430974C +:103E400029F4109208031092070302C01382128211 +:103E5000425050405093060340930503DF91CF91A9 +:103E600008951F920F920FB60F9211242F933F9334 +:103E70004F935F936F937F938F939F93AF93BF9372 +:103E8000EF93FF9380915B0290915C02892B29F064 +:103E9000E0915B02F0915C020995FF91EF91BF9177 +:103EA000AF919F918F917F916F915F914F913F91D2 +:103EB0002F910F900FBE0F901F9018951F920F9289 +:103EC0000FB60F9211242F933F934F935F936F93ED +:103ED0007F938F939F93AF93BF93EF93FF938091C3 +:103EE0005D0290915E02892B29F0E0915D02F091D4 +:103EF0005E020995FF91EF91BF91AF919F918F91D4 +:103F00007F916F915F914F913F912F910F900FBED5 +:103F10000F901F9018951F920F920FB60F921124B9 +:103F20002F933F938F939F93AF93BF93809163029F +:103F300090916402A0916502B0916602309167028F +:103F40000196A11DB11D232F2D5F2D3720F02D5778 +:103F50000196A11DB11D2093670280936302909387 +:103F60006402A0936502B093660280915F02909113 +:103F70006002A0916102B09162020196A11DB11D83 +:103F800080935F0290936002A0936102B0936202FB +:103F9000BF91AF919F918F913F912F910F900FBE45 +:103FA0000F901F9018958FB7F8942091630230916D +:103FB000640240916502509166028FBFB901CA0147 +:103FC0000895019739F0880F991F880F991F02975C +:103FD0000197F1F70895789484B5826084BD84B523 +:103FE000816084BD85B5826085BD85B5816085BDF4 +:103FF000EEE6F0E0808181608083E1E8F0E010820D +:10400000808182608083808181608083E0E8F0E04D +:10401000808181608083E1EBF0E080818460808337 +:10402000E0EBF0E0808181608083EAE7F0E080816E +:1040300084608083808182608083808181608083CE +:104040008081806880831092C1000895EF92FF9272 +:104050000F931F93CF93DF937B018C013FB7F894AD +:1040600080915F0290916002A0916102B091620222 +:1040700026B5A89B05C02F3F19F00196A11DB11DC3 +:104080003FBFBA2FA92F982F8827820F911DA11DFE +:10409000B11D52E0880F991FAA1FBB1F5A95D1F777 +:1040A000EC0130C00E9465233FB7F89480915F0215 +:1040B00090916002A0916102B091620226B5A89B26 +:1040C00005C02F3F19F00196A11DB11D3FBFBA2FAA +:1040D000A92F982F8827820F911DA11DB11D32E0B5 +:1040E000880F991FAA1FBB1F3A95D1F78C1B9D0BF8 +:1040F000885E9340B8F20894E108F10801091109BB +:10410000C851DC4FE114F1040105110559F6DF91A6 +:10411000CF911F910F91FF90EF900895CF93DF9370 +:10412000482F50E0CA018B5C9B4FFC0134914F5DDE +:104130005B4FFA018491882369F190E0880F991F01 +:10414000FC01ED5FFB4FA591B491FC01E35FFB4FD8 +:10415000C591D491662351F42FB7F8948C91932F85 +:10416000909589238C93888189230BC0623061F4F8 +:104170002FB7F8948C91932F909589238C938881F5 +:10418000832B88832FBF06C09FB7F8948C91832B15 +:104190008C939FBFDF91CF910895482F50E0CA01C3 +:1041A000875B9B4FFC012491CA018B5C9B4FFC01F8 +:1041B00094914F5D5B4FFA013491332309F440C071 +:1041C000222351F1233071F0243028F42130A1F062 +:1041D000223011F514C02630B1F02730C1F0243060 +:1041E000D9F404C0809180008F7703C08091800053 +:1041F0008F7D8093800010C084B58F7702C084B516 +:104200008F7D84BD09C08091B0008F7703C08091FD +:10421000B0008F7D8093B000E32FF0E0EE0FFF1F22 +:10422000E35FFB4FA591B4912FB7F894662321F477 +:104230008C919095892302C08C91892B8C932FBFF0 +:104240000895DC015C968C915C97FD01E80FF11DEF +:10425000E35AFF4F20815C968C915C9790E0019629 +:104260008F7390705C968C935C975696ED91FC9151 +:10427000579720835096ED91FC91519780818064EF +:1042800080835B969C915B975C968C915C9798176A +:1042900039F45296ED91FC91539780818F7D808304 +:1042A0000895DF92EF92FF920F931F93CF93DF93C6 +:1042B000EC017A018B01D22EE889F98982E08083B2 +:1042C000403081EE580780E0680780E07807A1F071 +:1042D00060E079E08DE390E0A80197010E947D25E0 +:1042E000215030404040504056954795379527958E +:1042F00080E12030380798F0E889F989108260E879 +:1043000074E88EE190E0A80197010E947D2521507C +:104310003040404050405695479537952795EC855D +:10432000FD853083EE85FF852083188EEC89FD891D +:10433000D082EA89FB89808180618083EA89FB8958 +:10434000808188608083EA89FB898081806880839E +:10435000EA89FB8980818F7D8083DF91CF911F91D6 +:104360000F91FF90EF90DF900895FC01218D828DD9 +:1043700030E0205C3F4F281B31092F733070C9019A +:104380000895FC01918D828D981719F42FEF3FEF5E +:1043900006C0828DE80FF11D858D282F30E0C90100 +:1043A0000895DC0159969C9159975A968C915A9789 +:1043B000981719F42FEF3FEF10C05A968C915A9727 +:1043C000FD01E80FF11D958D5A968C915A978F5FDC +:1043D0008F735A968C93292F30E0C9010895CF939B +:1043E000DF93EC01888D882361F419C08C9185FFDF +:1043F0000AC0E889F989808185FF05C0CE010E9445 +:104400002121AA89BB898C9185FD05C0E889F9899C +:10441000808186FD04C00FB607FEE8CFF4CFDF91A0 +:10442000CF9108950F931F93CF93DF93EC01062F45 +:104430009B8D8C8D981791F5E889F989808185FF8E +:104440002DC0EE89FF896083E889F98980818064C5 +:1044500080831DC00FB607FC08C0E889F9898081F8 +:1044600085FF03C0CE010E9421218C8D181791F386 +:104470008B8DFE01E80FF11DE35AFF4F00831B8F68 +:10448000EA89FB8980818062808381E0888F81E076 +:1044900090E0DF91CF911F910F9108951B8D1F5FC9 +:1044A0001F73E3CF8AED92E2892B51F080E090E018 +:1044B000892B31F00E94DA22882311F00E9400003B +:1044C00008951F920F920FB60F9211242F938F937E +:1044D0009F93EF93FF93E0917802F09179028081AE +:1044E00082FD16C0E0917E02F0917F0220819091C2 +:1044F00081029F5F9F7380918202981771F0E09113 +:104500008102F0E0E859FD4F258F9093810205C0AC +:10451000E0917E02F0917F02E081FF91EF919F9107 +:104520008F912F910F900FBE0F901F9018951092A2 +:104530006B0210926A0288EE93E0A0E0B0E08093F4 +:104540006C0290936D02A0936E02B0936F0284E5AB +:1045500091E0909369028093680285EC90E09093DB +:1045600075028093740284EC90E0909377028093BC +:10457000760280EC90E0909379028093780281EC4F +:1045800090E090937B0280937A0282EC90E090938B +:104590007D0280937C0286EC90E090937F02809372 +:1045A0007E02109281021092820210928302109277 +:1045B0008402089588E692E00E94B52120E0892BCC +:1045C00009F021E0822F08951F920F920FB60F92EB +:1045D00011242F933F934F935F936F937F938F9308 +:1045E0009F93AF93BF93EF93FF9388E692E00E946F +:1045F0002121FF91EF91BF91AF919F918F917F9179 +:104600006F915F914F913F912F910F900FBE0F903F +:104610001F901895CF93DF930E94EB1F0E94F20921 +:10462000C2E5D2E20E945B092097E1F30E94522288 +:10463000F9CF0E94FC1D0895CF92DF92EF92FF9276 +:104640000F931F93CF93DF937C016B018A01C0E02E +:10465000D0E00FC0D6016D916D01D701ED91FC91B5 +:104660000190F081E02DC7010995C80FD91F0150B5 +:1046700010400115110571F7CE01DF91CF911F9107 +:104680000F91FF90EF90DF90CF900895CF93DF933D +:10469000EC016115710519F420E030E00FC0DB0179 +:1046A0000D900020E9F71197A61BB70BE881F9815F +:1046B0000280F381E02DAD0109959C01C901DF91D4 +:1046C000CF9108950E944623089508955058BB271E +:1046D000AA270ED075C166D130F06BD120F031F42D +:1046E0009F3F11F41EF45BC10EF4E095E7FB51C14E +:1046F000E92F77D180F3BA1762077307840795070C +:1047000018F071F49EF58FC10EF4E0950B2EBA2FC0 +:10471000A02D0B01B90190010C01CA01A0011124C7 +:10472000FF27591B99F0593F50F4503E68F11A1673 +:10473000F040A22F232F342F4427585FF3CF469504 +:1047400037952795A795F0405395C9F77EF41F1626 +:10475000BA0B620B730B840BBAF09150A1F0FF0FF0 +:10476000BB1F661F771F881FC2F70EC0BA0F621FDC +:10477000731F841F48F4879577956795B795F795CC +:104780009E3F08F0B3CF9395880F08F09927EE0F5E +:10479000979587950895D9D008F481E008950CD0B5 +:1047A0000FC107D140F0FED030F021F45F3F19F087 +:1047B000F0C0511139C1F3C014D198F39923C9F352 +:1047C0005523B1F3951B550BBB27AA276217730717 +:1047D000840738F09F5F5F4F220F331F441FAA1FCB +:1047E000A9F333D00E2E3AF0E0E830D0915050408B +:1047F000E695001CCAF729D0FE2F27D0660F771F39 +:10480000881FBB1F261737074807AB07B0E809F01A +:10481000BB0B802DBF01FF2793585F4F2AF09E3FAF +:10482000510568F0B6C000C15F3FECF3983EDCF381 +:10483000869577956795B795F7959F5FC9F7880F28 +:10484000911D9695879597F90895E1E0660F771F7A +:10485000881FBB1F621773078407BA0720F0621B0B +:10486000730B840BBA0BEE1F88F7E095089504D004 +:104870006894B111D9C00895BCD088F09F5790F0CA +:10488000B92F9927B751A0F0D1F0660F771F881F75 +:10489000991F1AF0BA95C9F712C0B13081F0C3D090 +:1048A000B1E00895C0C0672F782F8827B85F39F02E +:1048B000B93FCCF3869577956795B395D9F73EF4D4 +:1048C00090958095709561957F4F8F4F9F4F08957C +:1048D000E89409C097FB3EF490958095709561959A +:1048E0007F4F8F4F9F4F9923A9F0F92F96E9BB2750 +:1048F0009395F695879577956795B795F111F8CFCC +:10490000FAF4BB0F11F460FF1BC06F5F7F4F8F4F36 +:104910009F4F16C0882311F096E911C0772321F02C +:104920009EE8872F762F05C0662371F096E8862FC4 +:1049300070E060E02AF09A95660F771F881FDAF71B +:10494000880F9695879597F90895990F0008550F48 +:10495000AA0BE0E8FEEF16161706E807F907C0F005 +:1049600012161306E407F50798F0621B730B840B0D +:10497000950B39F40A2661F0232B242B252B21F4E7 +:1049800008950A2609F4A140A6958FEF811D811D87 +:10499000089597F99F6780E870E060E008959FEFC1 +:1049A00080EC089500240A941616170618060906C6 +:1049B000089500240A941216130614060506089595 +:1049C000092E0394000C11F4882352F0BB0F40F41D +:1049D000BF2B11F460FF04C06F5F7F4F8F4F9F4F5D +:1049E000089557FD9058440F551F59F05F3F71F0DF +:1049F0004795880F97FB991F61F09F3F79F0879546 +:104A00000895121613061406551FF2CF4695F1DFCE +:104A100008C0161617061806991FF1CF869571055E +:104A2000610508940895E894BB2766277727CB0192 +:104A300097F908950BD0C4CFB5DF28F0BADF18F08E +:104A4000952309F0A6CFABCF1124EECFCADFA0F398 +:104A5000959FD1F3950F50E0551F629FF001729F13 +:104A6000BB27F00DB11D639FAA27F00DB11DAA1F32 +:104A7000649F6627B00DA11D661F829F2227B00D7F +:104A8000A11D621F739FB00DA11D621F839FA00D0A +:104A9000611D221F749F3327A00D611D231F849F5A +:104AA000600D211D822F762F6A2F11249F575040B1 +:104AB0008AF0E1F088234AF0EE0FFF1FBB1F661F4C +:104AC000771F881F91505040A9F79E3F510570F005 +:104AD00060CFAACF5F3FECF3983EDCF386957795E5 +:104AE0006795B795F795E7959F5FC1F7FE2B880F00 +:104AF000911D9695879597F90895A1E21A2EAA1B04 +:104B0000BB1BFD010DC0AA1FBB1FEE1FFF1FA2177D +:104B1000B307E407F50720F0A21BB30BE40BF50B7A +:104B2000661F771F881F991F1A9469F76095709503 +:104B3000809590959B01AC01BD01CF010895EE0FCA +:104B4000FF1F0590F491E02D0994AA1BBB1B51E1B6 +:104B500007C0AA1FBB1FA617B70710F0A61BB70BED +:104B6000881F991F5A95A9F780959095BC01CD0192 +:104B700008952F923F924F925F926F927F928F9201 +:104B80009F92AF92BF92CF92DF92EF92FF920F93DC +:104B90001F93CF93DF93CDB7DEB7CA1BDB0B0FB6E6 +:104BA000F894DEBF0FBECDBF09942A8839884888A3 +:104BB0005F846E847D848C849B84AA84B984C88439 +:104BC000DF80EE80FD800C811B81AA81B981CE0F30 +:104BD000D11D0FB6F894DEBF0FBECDBFED01089515 +:044BE000F894FFCF77 +:104BE40054656D70657261747572652053656E737A +:104BF4006F7200312E30006661696C006F6B0000CB +:104C0400312E346231202831383834386132290069 +:104C140073656E736F72007265706561746572009E +:104C240030750000307500000100800009030000A9 +:104C34000000000012221C23B521D121C121EF2143 :00000001FF diff --git a/NodeJsController/DallasTemperatureSensor/DallasTemperatureSensor.ino b/NodeJsController/DallasTemperatureSensor/DallasTemperatureSensor.ino new file mode 100644 index 0000000..3ac3888 --- /dev/null +++ b/NodeJsController/DallasTemperatureSensor/DallasTemperatureSensor.ino @@ -0,0 +1,69 @@ +// Example sketch showing how to send in OneWire temperature readings +#include +#include +#include +#include + +#define ONE_WIRE_BUS 3 // Pin where dallase sensor is connected +#define MAX_ATTACHED_DS18B20 16 +unsigned long WAIT_TIME = 30000; // Wait for feedback from controller after sending and before going to sleep (in milliseconds) +unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) +OneWire oneWire(ONE_WIRE_BUS); +DallasTemperature sensors(&oneWire); +MySensor gw; +float lastTemperature[MAX_ATTACHED_DS18B20]; +int numSensors=0; +boolean receivedConfig = false; +boolean metric = true; +// Initialize temperature message +MyMessage msg(0,V_TEMP); + +void setup() +{ + // Startup OneWire + sensors.begin(); + + // Startup and initialize MySensors library. Set callback for incoming messages. + gw.begin(); + + // Send the sketch version information to the gateway and Controller + gw.sendSketchInfo("Temperature Sensor", "1.0"); + + // Fetch the number of attached temperature sensors + numSensors = sensors.getDeviceCount(); + + // Present all sensors to controller + for (int i=0; i(static_cast((gw.getConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; + + // Only send data if temperature has changed and no error + if (lastTemperature[i] != temperature && temperature != -127.00) { + + // Send in the new temperature + gw.send(msg.setSensor(i).set(temperature,1)); + lastTemperature[i]=temperature; + } + } + delay(WAIT_TIME); + gw.process(); + gw.sleep(SLEEP_TIME); +} + + diff --git a/NodeJsController/DropDb.js b/NodeJsController/DropDb.js new file mode 100644 index 0000000..128168b --- /dev/null +++ b/NodeJsController/DropDb.js @@ -0,0 +1,17 @@ +const dbAddress = '127.0.0.1'; +const dbPort = 27017; +const dbName = 'MySensorsDb'; + +var dbc = require('mongodb').MongoClient; +dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err, db) { + if (err) { + console.log('Error connecting to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName); + } else { + db.dropDatabase(function(err, result) { + if (err) + console.log('Error dropping database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName); + else + console.log('Successfully dropped database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName); + }); + } +}); diff --git a/NodeJsController/NodeJsController.js b/NodeJsController/NodeJsController.js index 25a53c4..2a127fa 100644 --- a/NodeJsController/NodeJsController.js +++ b/NodeJsController/NodeJsController.js @@ -264,6 +264,21 @@ function saveProtocol(sender, payload, db) { }); } +function saveSensor(sender, sensor, type, db) { + db.collection('node', function(err, c) { + c.update({ + 'id': sender + }, { + $addToSet: { + sensor: type + } + }, function(err, result) { + if (err) + console.log("Error writing sensor to database"); + }); + }); +} + function saveValue(sender, sensor, type, payload, db) { var cn = "Value-" + sender.toString() + "-" + sensor.toString(); db.createCollection(cn, function(err, c) { @@ -389,7 +404,8 @@ function sendFirmwareConfigResponse(destination, fwtype, fwversion, db, gw) { }, { $set: { 'type': fwtype, - 'version': fwversion + 'version': fwversion, + 'reboot': 0 } }, { upsert: true @@ -416,20 +432,22 @@ function sendFirmwareConfigResponse(destination, fwtype, fwversion, db, gw) { }, function(err, result) { if (err) console.log('Error finding firmware for type ' + fwtype); - if (!result) + else if (!result) console.log('No firmware found for type ' + fwtype); - var payload = []; - pushWord(payload, result.type); - pushWord(payload, result.version); - pushWord(payload, result.blocks); - pushWord(payload, result.crc); - var sensor = NODE_SENSOR_ID; - var command = C_STREAM; - var acknowledge = 0; // no ack - var type = 1; // I_FIRMWARE_CONFIG_RESPONSE - var td = encode(destination, sensor, command, acknowledge, type, payload); - console.log('-> ' + td.toString()); - gw.write(td); + else { + var payload = []; + pushWord(payload, result.type); + pushWord(payload, result.version); + pushWord(payload, result.blocks); + pushWord(payload, result.crc); + var sensor = NODE_SENSOR_ID; + var command = C_STREAM; + var acknowledge = 0; // no ack + var type = ST_FIRMWARE_CONFIG_RESPONSE; + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); + } }); }); } @@ -459,15 +477,43 @@ function sendFirmwareResponse(destination, fwtype, fwversion, fwblock, db, gw) { }); } -function rebootNode(destination, gw) { +function saveRebootRequest(destination, db) { + db.collection('node', function(err, c) { + c.update({ + 'id': destination + }, { + $set: { + 'reboot': 1 + } + }, function(err, result) { + if (err) + console.log("Error writing reboot request to database"); + }); + }); +} + +function checkRebootRequest(destination, db, gw) { + db.collection('node', function(err, c) { + c.find({ + 'id': destination + }, function(err, item) { + if (err) + console.log('Error checking reboot request'); + else if (item.reboot == 1) + sendRebootMessage(destination, gw); + }); + }); +} + +function sendRebootMessage(destination, gw) { var sensor = NODE_SENSOR_ID; - var command = C_INTERNAL; - var acknowledge = 0; // no ack - var type = I_REBOOT; - var payload = ""; - var td = encode(destination, sensor, command, acknowledge, type, payload); - console.log('-> ' + td.toString()); - gw.write(td); + var command = C_INTERNAL; + var acknowledge = 0; // no ack + var type = I_REBOOT; + var payload = ""; + var td = encode(destination, sensor, command, acknowledge, type, payload); + console.log('-> ' + td.toString()); + gw.write(td); } function rfReceived(data, db, gw) { @@ -481,7 +527,7 @@ function rfReceived(data, db, gw) { var type = +datas[3]; var rawpayload = datas[4].trim(); var payload; - if (command == 4) { + if (command == C_STREAM) { payload = []; for (var i = 0; i < rawpayload.length; i+=2) payload.push(parseInt(rawpayload.substring(i, i + 2), 16)); @@ -491,7 +537,9 @@ function rfReceived(data, db, gw) { // decision on appropriate response switch (command) { case C_PRESENTATION: - saveProtocol(sender, payload, db); + if (sensor == NODE_SENSOR_ID) + saveProtocol(sender, payload, db); + saveSensor(sender, sensor, type, db); break; case C_SET: saveValue(sender, sensor, type, payload, db); @@ -560,6 +608,7 @@ function rfReceived(data, db, gw) { } break; } + checkRebootRequest(sender, db, gw); } } @@ -574,8 +623,8 @@ dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err db.createCollection('firmware', function(err, collection) { }); // ToDo : check for new hex files / only load if new / get type and version from filename - for (var i = 0; i < hexFiles.length; i++) - loadFirmware(i, 1, hexFiles[i], db); + for (var i = 0; i < fwHexFiles.length; i++) + loadFirmware(i, 1, fwHexFiles[i], db); var gw; if (gwType == 'Ethernet') { diff --git a/NodeJsController/Reboot.js b/NodeJsController/Reboot.js index 4a521e1..2afb2b9 100644 --- a/NodeJsController/Reboot.js +++ b/NodeJsController/Reboot.js @@ -1,45 +1,28 @@ var NodeIdToReboot = 1; -var gwType = 'Ethernet'; -var gwAddress = '10.0.1.99'; -var gwPort = 9999; +const dbAddress = '127.0.0.1'; +const dbPort = 27017; +const dbName = 'MySensorsDb'; -var BROADCAST_ADDRESS = 255; -var NODE_SENSOR_ID = 255; - -function encode(destination, sensor, command, acknowledge, type, payload, binary) { - var msg = destination.toString(10) + ";" + sensor.toString(10) + ";" + command.toString(10) + ";" + acknowledge.toString(10) + ";" + type.toString(10) + ";"; - if (binary) { - for (var i = 0; i < payload.length; i++) { - if (payload[i] < 16) - msg += "0"; - msg += payload[i].toString(16); - } - } else { - msg += payload; +var dbc = require('mongodb').MongoClient; +dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err, db) { + if (err) { + console.log('Error connecting to database at mongodb://' + dbAddress + ':' + dbPort + '/' + dbName); + } else { + db.collection('node', function(err, c) { + c.update({ + 'id': NodeIdToReboot + }, { + $set: { + 'reboot': 1 + } + }, function(err, result) { + if (err) + console.log("Error writing reboot request to database"); + else + console.log("Reboot request saved to database"); + }); + }); } - msg += '\n'; - return msg.toString(); -} - -function rebootNode(destination, gw) { - var sensor = NODE_SENSOR_ID; - var command = 3; // C_INTERNAL - var acknowledge = 0; // no ack - var type = 13; // I_REBOOT - var payload = ""; - var td = encode(destination, sensor, command, acknowledge, type, payload, false); - console.log('-> ' + td.toString()); - gw.write(td); -} - -var gw = require('net').Socket(); -gw.connect(gwPort, gwAddress); -gw.setEncoding('ascii'); -gw.on('connect', function() { - console.log('connected to ethernet gateway at ' + gwAddress + ":" + gwPort); - rebootNode(NodeIdToReboot, gw); - console.log('reboot request submitted for ' + NodeIdToReboot); -}).on('end', function() { - console.log('disconnected from gateway'); }); + From 7fb39d69d41baefd5ef1f6d2470441057df8e1b1 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 27 Jul 2014 16:55:41 +0200 Subject: [PATCH 277/326] Fix problems found after verification of local commits. --- libraries/MySensors/MyGateway.cpp | 2 +- libraries/MySensors/MyMessage.cpp | 3 +-- libraries/MySensors/MyMessage.h | 8 ++++---- .../MySensors/examples/PressureSensor/PressureSensor.ino | 1 - .../RelayWithButtonActuator/RelayWithButtonActuator.ino | 4 ++++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index f9ce0e9..9fde171 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -160,7 +160,7 @@ void MyGateway::parseAndSend(char *commandBuffer) { msg.sensor = sensor; msg.type = type; mSetCommand(msg,command); - mSetRequestAck(msg,ack==0?false:true); + mSetRequestAck(msg,ack?1:0); mSetAck(msg,false); msg.set(value); ok = sendRoute(msg); diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 89addeb..6f973bb 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -35,10 +35,9 @@ const char* MyMessage::getString() const { char* MyMessage::getString(char *buffer) const { uint8_t payloadType = miGetPayloadType(); - if (payloadType == P_STRING) { strncpy(buffer, data, miGetLength()); - buffer[miGetLength()] = '\0'; + buffer[miGetLength()] = 0; return buffer; } else if (buffer != NULL) { switch(payloadType) { diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 2c5dc21..2453531 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -67,6 +67,7 @@ typedef enum { #define BIT_MASK(len) ( BIT(len)-1 ) // Create a bitfield mask of length starting at bit 'start'. #define BF_MASK(start, len) ( BIT_MASK(len)<<(start) ) + // Prepare a bitmask for insertion or combining. #define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) ) // Extract a bitfield of length len starting at bit 'start' from y. @@ -87,7 +88,7 @@ typedef enum { #define mSetRequestAck(_msg,_rack) BF_SET(_msg.command_ack_payload, _rack, 3, 1) #define mGetRequestAck(_msg) BF_GET(_msg.command_ack_payload, 3, 1) -#define mSetAck(_msg,_ack) BF_SET(_msg.command_ack_payload, _ack, 4, 1) +#define mSetAck(_msg,_ackMsg) BF_SET(_msg.command_ack_payload, _ackMsg, 4, 1) #define mGetAck(_msg) BF_GET(_msg.command_ack_payload, 4, 1) #define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 5, 3) @@ -95,9 +96,6 @@ typedef enum { // internal access for special fields -#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 4, 4) -#define miGetPayloadType() BF_GET(command_ack_payload, 4, 4) - #define miSetLength(_length) BF_SET(version_length, _length, 3, 5) #define miGetLength() BF_GET(version_length, 3, 5) @@ -107,6 +105,8 @@ typedef enum { #define miSetAck(_ack) BF_SET(command_ack_payload, _ack, 4, 1) #define miGetAck() BF_GET(command_ack_payload, 4, 1) +#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 5, 3) +#define miGetPayloadType() BF_GET(command_ack_payload, 5, 3) diff --git a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino index f4cde74..94aa7e3 100644 --- a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino +++ b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino @@ -6,7 +6,6 @@ #define BARO_CHILD 0 #define TEMP_CHILD 1 -#define LIGHT_SENSOR_ANALOG_PIN 0 unsigned long SLEEP_TIME = 60000; // Sleep time between reads (in seconds) Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor diff --git a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino index e8b4317..94424bb 100644 --- a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino +++ b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino @@ -66,6 +66,10 @@ void loop() void incomingMessage(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. + if (message.isAck()) { + Serial.println("This is an ack from gateway"); + } + if (message.type==V_LIGHT) { // Change relay state state = message.getBool(); From 61122532adbf86d5faaa57192f3015b548cbeace Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 13 Aug 2014 09:39:45 +0200 Subject: [PATCH 278/326] Float values sent in binary format over the air Move base radio id to MyConfig.h --- libraries/MySensors/MyConfig.h | 2 ++ libraries/MySensors/MyMessage.cpp | 23 +++++++++++++++++------ libraries/MySensors/MyMessage.h | 10 +++++++--- libraries/MySensors/MySensor.h | 2 -- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index f959650..9cb8cb6 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -8,6 +8,8 @@ #define RF24_DATARATE RF24_250KBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps #define RF24_PA_LEVEL RF24_PA_MAX //Sensor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_MAX=0dBm #define RF24_PA_LEVEL_GW RF24_PA_LEVEL //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. +#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) // This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network. + /*** * Enable/Disable debug logging diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp index 3e4d17c..8c5492a 100644 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -80,6 +80,9 @@ char* MyMessage::getString(char *buffer) const { case P_ULONG32: ultoa(ulValue, buffer, 10); break; + case P_FLOAT32: + dtostrf(fValue,2,fPrecision,buffer); + break; case P_CUSTOM: return getStream(buffer); break; @@ -105,8 +108,15 @@ bool MyMessage::getBool() const { return getInt(); } -double MyMessage::getDouble() const { - return strtod(data, NULL); +float MyMessage::getFloat() const { + switch(miGetPayloadType()) { + case P_FLOAT32: + return fValue; + case P_STRING: + return atof(data); + default: + return 0; + } } long MyMessage::getLong() const { @@ -195,10 +205,11 @@ MyMessage& MyMessage::set(uint8_t value) { } -MyMessage& MyMessage::set(double value, uint8_t decimals) { - dtostrf(value,2,decimals,data); - miSetLength(strlen(data)); - miSetPayloadType(P_STRING); +MyMessage& MyMessage::set(float value, uint8_t decimals) { + miSetLength(5); // 32 bit float + persi + miSetPayloadType(P_FLOAT32); + fValue=value; + fPrecision = decimals; return *this; } diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 67ff9b5..b1d8c04 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -63,7 +63,7 @@ typedef enum { } stream; typedef enum { - P_STRING, P_BYTE, P_INT16, P_UINT16, P_LONG32, P_ULONG32, P_CUSTOM + P_STRING, P_BYTE, P_INT16, P_UINT16, P_LONG32, P_ULONG32, P_CUSTOM, P_FLOAT32 } payload; @@ -139,7 +139,7 @@ public: void* getCustom() const; uint8_t getByte() const; bool getBool() const; - double getDouble() const; + float getFloat() const; long getLong() const; unsigned long getULong() const; int getInt() const; @@ -157,7 +157,7 @@ public: MyMessage& set(void* payload, uint8_t length); MyMessage& set(const char* value); MyMessage& set(uint8_t value); - MyMessage& set(double value, uint8_t decimals); + MyMessage& set(float value, uint8_t decimals); MyMessage& set(unsigned long value); MyMessage& set(long value); MyMessage& set(unsigned int value); @@ -192,6 +192,10 @@ struct long lValue; unsigned int uiValue; int iValue; + struct { + float fValue; + uint8_t fPrecision; // Number of decimals when serializing + }; char data[MAX_PAYLOAD + 1]; } __attribute__((packed)); #ifdef __cplusplus diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 7db57b6..45544ba 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -57,8 +57,6 @@ #define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) // First free address for sketch static configuration // This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). -// This is also act as base value for sensor nodeId -#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) #define GATEWAY_ADDRESS ((uint8_t)0) #define BROADCAST_ADDRESS ((uint8_t)0xFF) #define TO_ADDR(x) (BASE_RADIO_ID + x) From 9f48f091e3693a2f49214a50b3fe96ef50ce4485 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 13 Aug 2014 20:02:10 +0200 Subject: [PATCH 279/326] Rename PING to the more suitable FIND_PARENT --- libraries/MySensors/MyMessage.h | 10 +++++++--- libraries/MySensors/MySensor.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index b1d8c04..aa3f56c 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -44,7 +44,7 @@ typedef enum { // Type of internal messages (for internal messages) typedef enum { I_BATTERY_LEVEL, I_TIME, I_VERSION, I_ID_REQUEST, I_ID_RESPONSE, - I_INCLUSION_MODE, I_CONFIG, I_PING, I_PING_ACK, + I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE, I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION, I_REBOOT } internal; @@ -185,17 +185,21 @@ struct // Each message can transfer a payload. We add one extra byte for string // terminator \0 to be "printable" this is not transferred OTA - // This union is used to simplify the construction of the binary transferred int/long values. + // This union is used to simplify the construction of the binary data types transferred. union { uint8_t bValue; unsigned long ulValue; long lValue; unsigned int uiValue; int iValue; - struct { + struct { // Float messages 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]; } __attribute__((packed)); #ifdef __cplusplus diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 0308f6c..61d23d0 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -112,7 +112,7 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ RF24::setCRCLength(RF24_CRC_16); RF24::enableDynamicPayloads(); - // All nodes listen to broadcast pipe (for PING-ACK messages) + // All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages) RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); } @@ -145,7 +145,7 @@ void MySensor::findParentNode() { nc.distance = 255; // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set(""); + build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set(""); sendWrite(BROADCAST_ADDRESS, msg, true); // Wait for ping response. @@ -285,12 +285,12 @@ boolean MySensor::process() { uint8_t last = msg.last; uint8_t destination = msg.destination; - if (repeaterMode && command == C_INTERNAL && type == I_PING) { + if (repeaterMode && command == C_INTERNAL && type == I_FIND_PARENT) { // Relaying nodes should always answer ping messages // Wait a random delay of 0-2 seconds to minimize collision // between ping ack messages from other relaying nodes delay(millis() & 0x3ff); - sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK, false).set(nc.distance), true); + sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true); return false; } else if (destination == nc.nodeId) { // Check if sender requests an ack back. @@ -311,8 +311,8 @@ boolean MySensor::process() { } if (command == C_INTERNAL) { - if (type == I_PING_ACK && !isGateway) { - // We've received a reply to a PING message. Check if the distance is + if (type == I_FIND_PARENT_RESPONSE && !isGateway) { + // We've received a reply to a FIND_PARENT message. Check if the distance is // shorter than we already have. uint8_t distance = msg.getByte(); if (distance Date: Wed, 13 Aug 2014 20:02:45 +0200 Subject: [PATCH 280/326] Add dimmable LED actuator example --- .../DimmableLEDActuator.ino | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 libraries/MySensors/examples/DimmableLEDActuator/DimmableLEDActuator.ino diff --git a/libraries/MySensors/examples/DimmableLEDActuator/DimmableLEDActuator.ino b/libraries/MySensors/examples/DimmableLEDActuator/DimmableLEDActuator.ino new file mode 100644 index 0000000..23188f1 --- /dev/null +++ b/libraries/MySensors/examples/DimmableLEDActuator/DimmableLEDActuator.ino @@ -0,0 +1,107 @@ +/*** + * 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. + * + * DESCRIPTION + * This sketch provides a Dimmable LED Light using PWM and based Henrik Ekblad + * Vera Arduino Sensor project. + * Developed by Bruce Lacey, inspired by Hek's MySensor's example sketches. + * + * The circuit uses a MOSFET for Pulse-Wave-Modulation to dim the attached LED or LED strip. + * The MOSFET Gate pin is connected to Arduino pin 3 (LED_PIN), the MOSFET Drain pin is connected + * to the LED negative terminal and the MOSFET Source pin is connected to ground. + * + * This sketch is extensible to support more than one MOSFET/PWM dimmer per circuit. + * + * REVISION HISTORY + * Version 1.0 - February 15, 2014 - Bruce Lacey + * Version 1.1 - August 13, 2014 - Converted to 1.4 (hek) + * + ***/ +#define SN "DimmableLED" +#define SV "1.1" + +#include +#include + +#define LED_PIN 3 // Arduino pin attached to MOSFET Gate pin +#define FADE_DELAY 10 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim) + +MySensor gw(9,10); + +static int currentLevel = 0; // Current dim level... +MyMessage dimmerMsg(0, V_DIMMER); +MyMessage lightMsg(0, V_LIGHT); + + +/*** + * Dimmable LED initialization method + */ +void setup() +{ + Serial.println( SN ); + gw.begin( incomingMessage ); + + // Register the LED Dimmable Light with the gateway + gw.present( 0, S_DIMMER ); + + gw.sendSketchInfo(SN, SV); + // Pull the gateway's current dim level - restore light level upon sendor node power-up + gw.request( 0, V_DIMMER ); +} + +/*** + * Dimmable LED main processing loop + */ +void loop() +{ + gw.process(); +} + + + +void incomingMessage(const MyMessage &message) { + if (message.type == V_LIGHT || message.type == 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 ); + + // Clip incoming level to valid range of 0 to 100 + requestedLevel = requestedLevel > 100 ? 100 : requestedLevel; + requestedLevel = requestedLevel < 0 ? 0 : requestedLevel; + + Serial.print( "Changing level to " ); + Serial.print( requestedLevel ); + Serial.print( ", from " ); + Serial.println( currentLevel ); + + fadeToLevel( requestedLevel ); + + // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value... + gw.send(lightMsg.set(currentLevel > 0 ? 1 : 0)); + + // hek comment: Is this really nessesary? + gw.send( dimmerMsg.set(currentLevel) ); + + + } +} + +/*** + * This method provides a graceful fade up/down effect + */ +void fadeToLevel( int toLevel ) { + + int delta = ( toLevel - currentLevel ) < 0 ? -1 : 1; + + while ( currentLevel != toLevel ) { + currentLevel += delta; + analogWrite( LED_PIN, (int)(currentLevel / 100. * 255) ); + delay( FADE_DELAY ); + } +} + From b08e168d16dc89f6bd4e9fefbd4f4f2e8367cc93 Mon Sep 17 00:00:00 2001 From: Damme Date: Wed, 13 Aug 2014 21:08:51 +0200 Subject: [PATCH 281/326] Create MQTTGateway.ino MQTTGateway Beta 0.1 --- MQTTGateway/MQTTGateway.ino | 83 +++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 MQTTGateway/MQTTGateway.ino diff --git a/MQTTGateway/MQTTGateway.ino b/MQTTGateway/MQTTGateway.ino new file mode 100644 index 0000000..d59e4b6 --- /dev/null +++ b/MQTTGateway/MQTTGateway.ino @@ -0,0 +1,83 @@ +// MyMQTT Gateway 0.1b +// +// Created by Daniel Wiegert +// Based on Mysensors Ethernet Gateway by Henrik Ekblad +// http://www.mysensors.org +// +// Requires MySensors lib 1.4b +// +// Don't forget to look at the definitions in MyMQTT.h! +// Don't forget to configure Radio pins, IP and MAC-address! +// +// Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light +// NodeID and SensorID is Decimal number. +// Last segment is translation of the sensor type, look inside MyMQTT.cpp for +// the definitions. User can change this to their needs. +// +// Features: +// Supports automatic nodeID delegation +// Recieve sketchname and version Example: (openhab item) +//String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} +//String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} +// ... +// +// Todo: +// DOCUMENTATION... +// Special commands : clear or set EEPROM Values +// Special commands : Reboot +// ... + + +#include +#include +#include + + +// Use this for IBOARD modded to use standard MISO/MOSI/SCK More information see; +// http://forum.mysensors.org/topic/224/iboard-cheap-single-board-ethernet-arduino-with-radio/5 +#define RADIO_CE_PIN 3 // radio chip enable +#define RADIO_SPI_SS_PIN 8 // radio SPI serial select + +//Use this for default configured pro mini / nano etc : +//#define RADIO_CE_PIN 5 // radio chip enable +//#define RADIO_SPI_SS_PIN 6 // radio SPI serial select + +#define IP_PORT 1883 // MQTT Listening port +IPAddress myIp ( 192, 168, 0, 234 ); // Configure your static ip-address here +byte mac[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - Change this! +// * NOTE: Keep first byte at x2, x6, xA or xE (replace x with any hex value) for using Locally Administered Address Ranges. + +EthernetServer server = EthernetServer(IP_PORT); +MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN); + +void processEthernetMessages() { + char inputString[MQTT_MAX_PACKET_SIZE] = ""; + int inputSize = 0; + EthernetClient client = server.available(); + if (client) { + while (client.available()) { + char inChar = client.read(); + inputString[inputSize] = inChar; + inputSize++; + } + gw.processMQTTMessage(inputString, inputSize); + } +} + +void writeEthernet(char *writeBuffer, int *writeSize) { + server.write(writeBuffer, *writeSize); // Should this really be *writeSize? +} + +int main(void) { + init(); + Ethernet.begin(mac, myIp); + delay(1000); // Wait for Ethernet to get configured. + gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet); + server.begin(); + while (1) { + processEthernetMessages(); + gw.processRadioMessage(); + } +} + + From 9eca431b91b53e618f448f70952397c0b6226277 Mon Sep 17 00:00:00 2001 From: Damme Date: Wed, 13 Aug 2014 21:11:45 +0200 Subject: [PATCH 282/326] Create MyMQTT.cpp MQTTGateway beta 0.1 --- libraries/MySensors/MyMQTT.cpp | 312 +++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 libraries/MySensors/MyMQTT.cpp diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp new file mode 100644 index 0000000..71d49a5 --- /dev/null +++ b/libraries/MySensors/MyMQTT.cpp @@ -0,0 +1,312 @@ +/* +The MySensors library adds a new layer on top of the RF24 library. +It handles radio network routing, relaying and ids. + +Created by Henrik Ekblad +Modified by Daniel Wiegert +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 "MyMQTT.h" + +char broker[] PROGMEM = MQTT_BROKER_PREFIX; +char S_0[] PROGMEM = "Temperature"; +char S_1[] PROGMEM = "Humidity"; +char S_2[] PROGMEM = "Light"; +char S_3[] PROGMEM = "Dimmer"; +char S_4[] PROGMEM = "Pressure"; +char S_5[] PROGMEM = "Forecast"; +char S_6[] PROGMEM = "Rain"; +char S_7[] PROGMEM = "Rainrate"; +char S_8[] PROGMEM = "Wind"; +char S_9[] PROGMEM = "Gust"; +char S_10[] PROGMEM = "Direction"; +char S_11[] PROGMEM = "UV"; +char S_12[] PROGMEM = "Weight"; +char S_13[] PROGMEM = "Distance"; +char S_14[] PROGMEM = "Impedance"; +char S_15[] PROGMEM = "Armed"; +char S_16[] PROGMEM = "Tripped"; +char S_17[] PROGMEM = "Watt"; +char S_18[] PROGMEM = "KWH"; +char S_19[] PROGMEM = "Scene_ON"; +char S_20[] PROGMEM = "Scene_OFF"; +char S_21[] PROGMEM = "Heater"; +char S_22[] PROGMEM = "Heater_SW"; +char S_23[] PROGMEM = "LightLVL"; +char S_24[] PROGMEM = "Var1"; +char S_25[] PROGMEM = "Var2"; +char S_26[] PROGMEM = "Var3"; +char S_27[] PROGMEM = "Var4"; +char S_28[] PROGMEM = "Var5,"; +char S_29[] PROGMEM = "Up"; +char S_30[] PROGMEM = "Down"; +char S_31[] PROGMEM = "Stop"; +char S_32[] PROGMEM = "IR_Send"; +char S_33[] PROGMEM = "IR_Receive"; +char S_34[] PROGMEM = "Flow"; +char S_35[] PROGMEM = "Volume"; +char S_36[] PROGMEM = "Lock_Status"; +char S_37[] PROGMEM = "Volt_Batt"; // Does not exist in mysensors lib! +char S_38[] PROGMEM = ""; +char S_39[] PROGMEM = ""; +char S_40[] PROGMEM = ""; +char S_41[] PROGMEM = ""; +char S_42[] PROGMEM = ""; +char S_43[] PROGMEM = ""; +char S_44[] PROGMEM = ""; +char S_45[] PROGMEM = ""; +char S_46[] PROGMEM = ""; +char S_47[] PROGMEM = ""; +char S_48[] PROGMEM = ""; +char S_49[] PROGMEM = "Sketch_name"; // Does not exist in mysensors lib! +char S_50[] PROGMEM = "Sketch_version"; // Does not exist in mysensors lib! + + +PROGMEM const char *sType[] = +{ S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, + S_11, S_12, S_13, S_14, S_15, S_16, S_17, S_18, S_19, S_20, + S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, + S_31, S_32, S_33, S_34, S_35, S_36, S_37, S_38, S_39, S_40, + S_41, S_42, S_43, S_44, S_45, S_46, S_47, S_48, S_49, S_50}; + + +MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin) : +MySensor(_cepin, _cspin) { +} + +void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *, int *)) { + Serial.begin(BAUD_RATE); + repeaterMode = true; + isGateway = true; + MQTTClient = false; + setupRepeaterMode(); + + if (inDataCallback != NULL) { + useWriteCallback = true; + dataCallback = inDataCallback; + } + else { + useWriteCallback = false; + } + + nc.nodeId = 0; + nc.distance = 0; + + // Start up the radio library + setupRadio(paLevel, channel, dataRate); + RF24::openReadingPipe(WRITE_PIPE, BASE_RADIO_ID); + RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID); + RF24::startListening(); + // Send startup log message on serial + //Serial.print(PSTR("Started\n"));//TODO: progmem gives error..? error: sType causes a section type conflict with __c + Serial.print("Started\n");//TODO: fix this... +} + +void MyMQTT::processRadioMessage() { + if (process()) { + // A new message was received from one of the sensors + MyMessage message = getLastMessage(); + // Pass along the message from sensors to serial line + SendMQTT(message); + } + +} + +void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { + char *str, *p, *value=NULL; + char i = 0; + buffer[0]= 0; + buffsize = 0; +#ifdef TCPDUMP + Serial.print("<<"); + char buf[4]; + for (int a=0; a> 4 == MQTTCONNECT) { + buffer[buffsize++] = MQTTCONNACK << 4; + buffer[buffsize++] = 0x02; // Remaining length + buffer[buffsize++] = 0x00; // Connection accepted + buffer[buffsize++] = 0x00; // Reserved + MQTTClient=true; + } + if ((byte)inputString[0] >> 4 == MQTTPINGREQ) { + buffer[buffsize++] = MQTTPINGRESP << 4; + buffer[buffsize++] = 0x00; + } + if ((byte)inputString[0] >> 4 == MQTTSUBSCRIBE) { + buffer[buffsize++] = MQTTSUBACK << 4; // Just ack everything, we actually dont really care! + buffer[buffsize++] = 0x03; // Remaining length + buffer[buffsize++] = (byte)inputString[2]; // Message ID MSB + buffer[buffsize++] = (byte)inputString[3]; // Message ID LSB + buffer[buffsize++] = MQTTQOS0; // QOS level + } + if ((byte)inputString[0] >> 4 == MQTTUNSUBSCRIBE) { + buffer[buffsize++] = MQTTUNSUBACK << 4; + buffer[buffsize++] = 0x02; // Remaining length + buffer[buffsize++] = (byte)inputString[2]; // Message ID MSB + buffer[buffsize++] = (byte)inputString[3]; // Message ID LSB + } + if ((byte)inputString[0] >> 4 == MQTTDISCONNECT) { + MQTTClient=false; + } + if (buffsize > 0) { +#ifdef TCPDUMP + Serial.print(">>"); + char buf[4]; + for (int a=0; a> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { + buffer[0]= 0; + buffsize = 0; + if ((byte)inputString[0] >> 4 == MQTTSUBSCRIBE) { + strncat(buffer,inputString+6,inputString[5]); + } else { + strncat(buffer,inputString+4,inputString[3]); + } + msg.sender = GATEWAY_ADDRESS; + for (str = strtok_r(buffer, "/", &p); // split using semicolon + str && i < 4; str = strtok_r(NULL, "/", &p) //get subsequent tokens (?) + ) { + switch (i) { + case 0: // Radioid (destination) + if (strcmp_P(str,broker)!=0) { + return; //Message not for us or malformatted! + } + break; + case 1: + msg.destination = atoi(str); + break; + case 2: + msg.sensor = atoi(str); + break; + case 3: + char match=0; + for (int j=0; strcpy_P(convBuf, (char*)pgm_read_word(&(sType[j]))) ; j++) { + if (strcmp(str,convBuf)==0) { + match=j; + break; + } + } + msg.type = match; + break; + } + i++; + } + if ((char)inputString[1] > (char)(inputString[3]+2) && !((byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { + strcpy(convBuf,inputString+(inputString[3]+4)); + msg.set(convBuf); + } else { + msg.set(""); + } + msg.sender = GATEWAY_ADDRESS; + mSetCommand(msg,C_SET); + mSetRequestAck(msg,false); + mSetAck(msg,false); + mSetVersion(msg, PROTOCOL_VERSION); + sendRoute(msg); + } +} + + +void MyMQTT::SendMQTT(MyMessage &msg) { +//serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); + buffsize = 0; + if (!MQTTClient) return; + if (msg.isAck()) { +Serial.println("msg is ack!"); + if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { + // TODO: sending ACK request on id_response fucks node up. doesn't work. + // The idea was to confirm id and save to EEPROM_LATEST_NODE_ADDRESS. + } + } else { + // we have to check every message if its a newly assigned id or not. + // Ack on I_ID_RESPONSE does not work, and checking on C_PRESENTATION isn't reliable. + char newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; + if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID; + if (msg.sender==newNodeID) { + saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); + } + if (mGetCommand(msg)==C_INTERNAL && msg.type==I_CONFIG) { // CONFIG + // As for now there is only one 'config' request. + // We force SI! Resistance is futile! + // + // Todo : Support for more config types, Maybe just read from own EEPROM? + // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to + // EEPROM_CONTROLLER_CONFIG_ADDRESS & EEPROM_LOCAL_CONFIG_ADDRESS + msg.destination = msg.sender; //NodeID + msg.sender = GATEWAY_ADDRESS; + msg.sensor = 255; //SensorID + msg.type = I_CONFIG; //msgType + mSetCommand(msg,C_INTERNAL); //Subtype + mSetRequestAck(msg,false); + mSetAck(msg,false); + msg.set("M"); + mSetVersion(msg, PROTOCOL_VERSION); + sendRoute(msg); + } else if (mGetCommand(msg)==C_PRESENTATION && (msg.type==S_ARDUINO_NODE || msg.type==S_ARDUINO_REPEATER_NODE)) { + //Doesnt work to check new sensorID here, this message does not always arrive.. See above. + } else if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { + char newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; + if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID; + if (newNodeID >= MQTT_LAST_SENSORID) return; // Sorry no more id's left :( + msg.destination = msg.sender; //NodeID + msg.sender = GATEWAY_ADDRESS; + msg.sensor = 255; //SensorID + msg.type = I_ID_RESPONSE; //MsgType + mSetCommand(msg,C_INTERNAL); //Subtype + mSetRequestAck(msg,false); //Request ack doesn't work, node/gateway gets stuck in enless loop. + mSetAck(msg,false); + msg.set((uint8_t)newNodeID); //Payload + mSetVersion(msg, PROTOCOL_VERSION); + sendRoute(msg); +// if (sendRoute(msg)) saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); // If send OK save to eeprom. DOES NOT ALWAYS RETURN 'OK'!? + } else if (mGetCommand(msg)!=0) { + if (mGetCommand(msg)==3) msg.type=msg.type+38; + buffer[buffsize++] = MQTTPUBLISH << 4; // 0: + buffer[buffsize++] = 0x09; // 1: Remaining length with no payload, we'll set this later to correct value, buffsize -2 + buffer[buffsize++] = 0x00; // 2: Length MSB (Remaing length can never exceed ff,so MSB must be 0!) + buffer[buffsize++] = 0x08; // 3: Length LSB (ADDR), We'll set this later + strcpy_P(buffer+4, broker); + buffsize+=strlen_P(broker); + buffsize+=sprintf(&buffer[buffsize],"/%i/%i/",msg.sender,msg.sensor); + buffsize+=strncpysType_retL(buffer,msg.type,buffsize); + buffer[3]=buffsize-4; // Set correct address length on byte 4. +#ifdef DEBUG + Serial.println((char*)&buffer[4]); +#endif + msg.getString(convBuf); + for (int a=0; a>"); + char buf[4]; + for (int a=0; a Date: Wed, 13 Aug 2014 21:12:06 +0200 Subject: [PATCH 283/326] Create MyMQTT.h MQTTGateway beta 0.1 --- libraries/MySensors/MyMQTT.h | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 libraries/MySensors/MyMQTT.h diff --git a/libraries/MySensors/MyMQTT.h b/libraries/MySensors/MyMQTT.h new file mode 100644 index 0000000..5ddc907 --- /dev/null +++ b/libraries/MySensors/MyMQTT.h @@ -0,0 +1,80 @@ +/* +The MySensors library adds a new layer on top of the RF24 library. +It handles radio network routing, relaying and ids. + +Created by Henrik Ekblad +Modified by Daniel Wiegert +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 MyMQTT_h +#define MyMQTT_h + +#include "MySensor.h" + + +/////////////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUG +#define TCPDUMP // Dump TCP packages. +#endif + +#define MQTT_FIRST_SENSORID 20 // If you want manually configured nodes below this value. +#define MQTT_LAST_SENSORID 254 // 254 is max! 255 reserved. +#define MQTT_BROKER_PREFIX "MyMQTT" // First prefix in MQTT tree, keep short! +#define MQTT_SEND_SUBSCRIPTION 1 // Send empty payload (request) to node upon MQTT client subscribe request. +// NOTE above : Beware to check if there is any length on payload in your incommingMessage code: +// Example: if (msg.type==V_LIGHT && strlen(msg.getString())>0) otherwise the code might do strange things. + +/////////////////////////////////////////////////////////////////////////////////// + +#define EEPROM_LATEST_NODE_ADDRESS EEPROM_LOCAL_CONFIG_ADDRESS+0 +#define MQTT_MAX_PACKET_SIZE 100 + +#define MQTTPROTOCOLVERSION 3 +#define MQTTCONNECT 1 // Client request to connect to Server +#define MQTTCONNACK 2 // Connect Acknowledgment +#define MQTTPUBLISH 3 // Publish message +#define MQTTPUBACK 4 // Publish Acknowledgment +#define MQTTPUBREC 5 // Publish Received (assured delivery part 1) +#define MQTTPUBREL 6 // Publish Release (assured delivery part 2) +#define MQTTPUBCOMP 7 // Publish Complete (assured delivery part 3) +#define MQTTSUBSCRIBE 8 // Client Subscribe request +#define MQTTSUBACK 9 // Subscribe Acknowledgment +#define MQTTUNSUBSCRIBE 10 // Client Unsubscribe request +#define MQTTUNSUBACK 11 // Unsubscribe Acknowledgment +#define MQTTPINGREQ 12 // PING Request +#define MQTTPINGRESP 13 // PING Response +#define MQTTDISCONNECT 14 // Client is Disconnecting +#define MQTTReserved 15 // Reserved + +#define MQTTQOS0 (0 << 1) +#define MQTTQOS1 (1 << 1) +#define MQTTQOS2 (2 << 1) + +class MyMQTT : +public MySensor { +public: + MyMQTT(uint8_t _cepin=9, uint8_t _cspin=10); + + void begin(rf24_pa_dbm_e paLevel=RF24_PA_LEVEL_GW, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE, void (*dataCallback)(char *, int *)=NULL); + void processRadioMessage(); + void processMQTTMessage(char *inputString, int inputPos); + +private: + bool MQTTClient; + char buffer[MQTT_MAX_PACKET_SIZE]; + int buffsize; + char convBuf[MAX_PAYLOAD*2+1]; + boolean useWriteCallback; + void (*dataCallback)(char *, int *); + void SendMQTT(MyMessage &msg); + char strncpysType_retL(char *str, char index, char start); +}; + + + +#endif + From 7d501307ee7a73f7fc8d68dc613fa3a12793cebf Mon Sep 17 00:00:00 2001 From: Damme Date: Wed, 13 Aug 2014 21:25:03 +0200 Subject: [PATCH 284/326] missed \n -> debug(PSTR("version mismatch\n")); --- libraries/MySensors/MySensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 61d23d0..11fcf18 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -275,7 +275,7 @@ boolean MySensor::process() { msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), msg.getString(convBuf)); if(!(mGetVersion(msg) == PROTOCOL_VERSION)) { - debug(PSTR("version mismatch")); + debug(PSTR("version mismatch\n")); return false; } From 3a63ae619d04bc26f144b1febe17f86c16ea301e Mon Sep 17 00:00:00 2001 From: Damme Date: Wed, 13 Aug 2014 21:29:18 +0200 Subject: [PATCH 285/326] Check if incomming message contains anything. --- .../RelayWithButtonActuator/RelayWithButtonActuator.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino index 94424bb..77b5f8d 100644 --- a/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino +++ b/libraries/MySensors/examples/RelayWithButtonActuator/RelayWithButtonActuator.ino @@ -70,7 +70,7 @@ void incomingMessage(const MyMessage &message) { Serial.println("This is an ack from gateway"); } - if (message.type==V_LIGHT) { + if (message.type == V_LIGHT && strlen(msg.getString()) != 0) { // Change relay state state = message.getBool(); digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF); From 172d58782ce86bababb216b00822cf5fb230e99a Mon Sep 17 00:00:00 2001 From: Damme Date: Wed, 13 Aug 2014 22:48:06 +0200 Subject: [PATCH 286/326] Little better readme... --- MQTTGateway/MQTTGateway.ino | 87 +++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/MQTTGateway/MQTTGateway.ino b/MQTTGateway/MQTTGateway.ino index d59e4b6..816c2b0 100644 --- a/MQTTGateway/MQTTGateway.ino +++ b/MQTTGateway/MQTTGateway.ino @@ -1,32 +1,63 @@ -// MyMQTT Gateway 0.1b -// -// Created by Daniel Wiegert -// Based on Mysensors Ethernet Gateway by Henrik Ekblad -// http://www.mysensors.org -// -// Requires MySensors lib 1.4b -// -// Don't forget to look at the definitions in MyMQTT.h! -// Don't forget to configure Radio pins, IP and MAC-address! -// -// Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light -// NodeID and SensorID is Decimal number. -// Last segment is translation of the sensor type, look inside MyMQTT.cpp for -// the definitions. User can change this to their needs. -// -// Features: -// Supports automatic nodeID delegation -// Recieve sketchname and version Example: (openhab item) -//String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} -//String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} -// ... -// -// Todo: -// DOCUMENTATION... -// Special commands : clear or set EEPROM Values -// Special commands : Reboot -// ... +/* MyMQTT Gateway 0.1b + Created by Daniel Wiegert + Based on Mysensors Ethernet Gateway by Henrik Ekblad + Requires MySensors lib 1.4b + http://www.mysensors.org + + * Don't forget to look at the definitions in MyMQTT.h! + * Don't forget to configure Radio pins, IP and MAC-address! + + * Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light + NodeID and SensorID is number (0-255). + Last segment is translation of the sensor type, look inside MyMQTT.cpp for + the definitions. User can change this to their needs. + +Example openhab setup: http://www.openhab.org/ + +* openhab.cfg +--- +mqtt:mysensor.url=tcp://192.168.0.234:1883 +mqtt:mysensor.clientId=MQTT +--- + +* items/test.items +--- +Group test +Number Temp_test "Temp [%.1f °C]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Temperature:state:default]"} +Number Hum_test "Hum [%.1f %%]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Humidity:state:default]"} +Number test_test "test [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/3/#:state:default]"} +Switch sw1 "sw 1" (test) {<[mysensor:MyMQTT/21/1/Light:command:MAP(1.map)]"} +--- (Note; # = Wildcard character) + +* sitemap/test.site +--- +sitemap demo label="Menu" +Frame label="Openhab" { + Group item=test label="Test group" +} +--- + +* transform/1.map +--- +1=ON +0=OFF +--- + +* Features: + - Supports automatic nodeID delegation + - Recieve sketchname and version Example: (openhab item) +String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} +String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} +[...] + +* Todo: + - DOCUMENTATION... + - Special commands + Read and set EEPROM Values + Send Reboot, And reboot gateway itself. +... +*/ #include #include From 5ebc1c812940458b77d863f1c1d24ce392ab2125 Mon Sep 17 00:00:00 2001 From: Daniel Wiegert Date: Fri, 15 Aug 2014 14:56:07 +0200 Subject: [PATCH 287/326] Minor changes + reinserted led-blinking --- MQTTGateway/MQTTGateway.ino | 118 ++++++++++++++---------------- libraries/MySensors/MyMQTT.cpp | 126 +++++++++++++++++++++++---------- libraries/MySensors/MyMQTT.h | 30 +++++--- 3 files changed, 163 insertions(+), 111 deletions(-) mode change 100644 => 100755 MQTTGateway/MQTTGateway.ino mode change 100644 => 100755 libraries/MySensors/MyMQTT.cpp mode change 100644 => 100755 libraries/MySensors/MyMQTT.h diff --git a/MQTTGateway/MQTTGateway.ino b/MQTTGateway/MQTTGateway.ino old mode 100644 new mode 100755 index 816c2b0..868cc54 --- a/MQTTGateway/MQTTGateway.ino +++ b/MQTTGateway/MQTTGateway.ino @@ -1,77 +1,56 @@ -/* MyMQTT Gateway 0.1b - - Created by Daniel Wiegert - Based on Mysensors Ethernet Gateway by Henrik Ekblad - Requires MySensors lib 1.4b - http://www.mysensors.org - - * Don't forget to look at the definitions in MyMQTT.h! - * Don't forget to configure Radio pins, IP and MAC-address! - - * Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light - NodeID and SensorID is number (0-255). - Last segment is translation of the sensor type, look inside MyMQTT.cpp for - the definitions. User can change this to their needs. - -Example openhab setup: http://www.openhab.org/ - -* openhab.cfg ---- -mqtt:mysensor.url=tcp://192.168.0.234:1883 -mqtt:mysensor.clientId=MQTT ---- - -* items/test.items ---- -Group test -Number Temp_test "Temp [%.1f °C]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Temperature:state:default]"} -Number Hum_test "Hum [%.1f %%]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Humidity:state:default]"} -Number test_test "test [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/3/#:state:default]"} -Switch sw1 "sw 1" (test) {<[mysensor:MyMQTT/21/1/Light:command:MAP(1.map)]"} ---- (Note; # = Wildcard character) - -* sitemap/test.site ---- -sitemap demo label="Menu" -Frame label="Openhab" { - Group item=test label="Test group" -} ---- - -* transform/1.map ---- -1=ON -0=OFF ---- - -* Features: - - Supports automatic nodeID delegation - - Recieve sketchname and version Example: (openhab item) -String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} -String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} -[...] - -* Todo: - - DOCUMENTATION... - - Special commands - Read and set EEPROM Values - Send Reboot, And reboot gateway itself. -... +// MyMQTT Gateway 0.1b +// +// Created by Daniel Wiegert +// Based on Mysensors Ethernet Gateway by Henrik Ekblad +// http://www.mysensors.org +// +// Requires MySensors lib 1.4b +// +// Don't forget to look at the definitions in MyMQTT.h! +// Don't forget to configure Radio pins, IP and MAC-address! +// +// Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light +// NodeID and SensorID is Decimal number. +// Last segment is translation of the sensor type, look inside MyMQTT.cpp for +// the definitions. User can change this to their needs. +// +// Features: +// Supports automatic nodeID delegation +// Recieve sketchname and version Example: (openhab item) +//String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} +//String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} +// ... +// +// Todo: +// DOCUMENTATION... +// Special commands : clear or set EEPROM Values +// Special commands : Reboot +// ... +/* +Sketch uses 23,666 bytes (77%) of program storage space. Maximum is 30,720 bytes. +Global variables use 766 bytes (37%) of dynamic memory, leaving 1,282 bytes for local variables. Maximum is 2,048 bytes. */ #include #include #include +#include // Use this for IBOARD modded to use standard MISO/MOSI/SCK More information see; // http://forum.mysensors.org/topic/224/iboard-cheap-single-board-ethernet-arduino-with-radio/5 -#define RADIO_CE_PIN 3 // radio chip enable -#define RADIO_SPI_SS_PIN 8 // radio SPI serial select +//#define RADIO_CE_PIN 3 // radio chip enable +//#define RADIO_SPI_SS_PIN 8 // radio SPI serial select +//#define RADIO_ERROR_LED_PIN A2 // Error led pin +//#define RADIO_RX_LED_PIN A1 // Receive led pin +//#define RADIO_TX_LED_PIN A0 // the PCB, on board LED //Use this for default configured pro mini / nano etc : -//#define RADIO_CE_PIN 5 // radio chip enable -//#define RADIO_SPI_SS_PIN 6 // radio SPI serial select +#define RADIO_CE_PIN 5 // radio chip enable +#define RADIO_SPI_SS_PIN 6 // radio SPI serial select +#define RADIO_ERROR_LED_PIN 7 // Error led pin +#define RADIO_RX_LED_PIN 8 // Receive led pin +#define RADIO_TX_LED_PIN 9 // the PCB, on board LED #define IP_PORT 1883 // MQTT Listening port IPAddress myIp ( 192, 168, 0, 234 ); // Configure your static ip-address here @@ -80,6 +59,8 @@ byte mac[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - Change th EthernetServer server = EthernetServer(IP_PORT); MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN); +// Uncomment this constructor if you have leds and include button attached to your gateway +// MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); void processEthernetMessages() { char inputString[MQTT_MAX_PACKET_SIZE] = ""; @@ -99,16 +80,23 @@ void writeEthernet(char *writeBuffer, int *writeSize) { server.write(writeBuffer, *writeSize); // Should this really be *writeSize? } +void ledTimersInterrupt() { + gw.ledTimersInterrupt(); +} + int main(void) { init(); Ethernet.begin(mac, myIp); delay(1000); // Wait for Ethernet to get configured. gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet); server.begin(); + if (gw.isLedMode()) { + // Add led timer interrupt + MsTimer2::set(300, ledTimersInterrupt); + MsTimer2::start(); + } while (1) { processEthernetMessages(); gw.processRadioMessage(); } } - - diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp old mode 100644 new mode 100755 index 71d49a5..7d8c9a3 --- a/libraries/MySensors/MyMQTT.cpp +++ b/libraries/MySensors/MyMQTT.cpp @@ -66,31 +66,39 @@ char S_50[] PROGMEM = "Sketch_version"; // Does not exist in mysensors lib! PROGMEM const char *sType[] = -{ S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, +{ S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, S_11, S_12, S_13, S_14, S_15, S_16, S_17, S_18, S_19, S_20, - S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, + S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, S_31, S_32, S_33, S_34, S_35, S_36, S_37, S_38, S_39, S_40, S_41, S_42, S_43, S_44, S_45, S_46, S_47, S_48, S_49, S_50}; - -MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin) : + +MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin, uint8_t _rx, uint8_t _tx, uint8_t _er) : MySensor(_cepin, _cspin) { + if (_rx != NULL) { + pinRx = _rx; + pinMode(pinRx, OUTPUT); + ledMode = true; + } + if (_tx != NULL) { + pinTx = _tx; + pinMode(pinTx, OUTPUT); + ledMode = true; + } + if (_er != NULL) { + pinEr = _er; + pinMode(pinEr, OUTPUT); + ledMode = true; + } } void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *, int *)) { Serial.begin(BAUD_RATE); repeaterMode = true; isGateway = true; - MQTTClient = false; + MQTTClientConnected = false; setupRepeaterMode(); - - if (inDataCallback != NULL) { - useWriteCallback = true; - dataCallback = inDataCallback; - } - else { - useWriteCallback = false; - } + dataCallback = inDataCallback; nc.nodeId = 0; nc.distance = 0; @@ -110,6 +118,7 @@ void MyMQTT::processRadioMessage() { // A new message was received from one of the sensors MyMessage message = getLastMessage(); // Pass along the message from sensors to serial line + rxBlink(3); SendMQTT(message); } @@ -121,7 +130,7 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { buffer[0]= 0; buffsize = 0; #ifdef TCPDUMP - Serial.print("<<"); + Serial.print("<<"); char buf[4]; for (int a=0; a> 4 == MQTTPINGREQ) { buffer[buffsize++] = MQTTPINGRESP << 4; @@ -150,18 +159,18 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { buffer[buffsize++] = (byte)inputString[3]; // Message ID LSB } if ((byte)inputString[0] >> 4 == MQTTDISCONNECT) { - MQTTClient=false; + MQTTClientConnected=false; } if (buffsize > 0) { #ifdef TCPDUMP - Serial.print(">>"); + Serial.print(">>"); char buf[4]; - for (int a=0; a> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { + if ((byte)inputString[0] >> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { buffer[0]= 0; buffsize = 0; if ((byte)inputString[0] >> 4 == MQTTSUBSCRIBE) { @@ -171,7 +180,7 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { } msg.sender = GATEWAY_ADDRESS; for (str = strtok_r(buffer, "/", &p); // split using semicolon - str && i < 4; str = strtok_r(NULL, "/", &p) //get subsequent tokens (?) + str && i < 4; str = strtok_r(NULL, "/", &p) //get subsequent tokens (?) ) { switch (i) { case 0: // Radioid (destination) @@ -188,11 +197,11 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { case 3: char match=0; for (int j=0; strcpy_P(convBuf, (char*)pgm_read_word(&(sType[j]))) ; j++) { - if (strcmp(str,convBuf)==0) { - match=j; + if (strcmp(str,convBuf)==0) { + match=j; break; } - } + } msg.type = match; break; } @@ -201,7 +210,7 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { if ((char)inputString[1] > (char)(inputString[3]+2) && !((byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { strcpy(convBuf,inputString+(inputString[3]+4)); msg.set(convBuf); - } else { + } else { msg.set(""); } msg.sender = GATEWAY_ADDRESS; @@ -209,7 +218,8 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { mSetRequestAck(msg,false); mSetAck(msg,false); mSetVersion(msg, PROTOCOL_VERSION); - sendRoute(msg); + txBlink(1); + if (!sendRoute(msg)) errBlink(1); } } @@ -217,11 +227,11 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { void MyMQTT::SendMQTT(MyMessage &msg) { //serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); buffsize = 0; - if (!MQTTClient) return; + if (!MQTTClientConnected) return; if (msg.isAck()) { Serial.println("msg is ack!"); if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { - // TODO: sending ACK request on id_response fucks node up. doesn't work. + // TODO: sending ACK request on id_response fucks node up. doesn't work. // The idea was to confirm id and save to EEPROM_LATEST_NODE_ADDRESS. } } else { @@ -233,11 +243,11 @@ Serial.println("msg is ack!"); saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); } if (mGetCommand(msg)==C_INTERNAL && msg.type==I_CONFIG) { // CONFIG - // As for now there is only one 'config' request. + // As for now there is only one 'config' request. // We force SI! Resistance is futile! // - // Todo : Support for more config types, Maybe just read from own EEPROM? - // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to + // Todo : Support for more config types, Maybe just read from own EEPROM? + // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to // EEPROM_CONTROLLER_CONFIG_ADDRESS & EEPROM_LOCAL_CONFIG_ADDRESS msg.destination = msg.sender; //NodeID msg.sender = GATEWAY_ADDRESS; @@ -248,7 +258,8 @@ Serial.println("msg is ack!"); mSetAck(msg,false); msg.set("M"); mSetVersion(msg, PROTOCOL_VERSION); - sendRoute(msg); + txBlink(1); + if (!sendRoute(msg)) errBlink(1); } else if (mGetCommand(msg)==C_PRESENTATION && (msg.type==S_ARDUINO_NODE || msg.type==S_ARDUINO_REPEATER_NODE)) { //Doesnt work to check new sensorID here, this message does not always arrive.. See above. } else if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { @@ -264,11 +275,12 @@ Serial.println("msg is ack!"); mSetAck(msg,false); msg.set((uint8_t)newNodeID); //Payload mSetVersion(msg, PROTOCOL_VERSION); - sendRoute(msg); + txBlink(1); + if (!sendRoute(msg)) errBlink(1); // if (sendRoute(msg)) saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); // If send OK save to eeprom. DOES NOT ALWAYS RETURN 'OK'!? } else if (mGetCommand(msg)!=0) { if (mGetCommand(msg)==3) msg.type=msg.type+38; - buffer[buffsize++] = MQTTPUBLISH << 4; // 0: + buffer[buffsize++] = MQTTPUBLISH << 4; // 0: buffer[buffsize++] = 0x09; // 1: Remaining length with no payload, we'll set this later to correct value, buffsize -2 buffer[buffsize++] = 0x00; // 2: Length MSB (Remaing length can never exceed ff,so MSB must be 0!) buffer[buffsize++] = 0x08; // 3: Length LSB (ADDR), We'll set this later @@ -289,17 +301,59 @@ Serial.println("msg is ack!"); #ifdef TCPDUMP Serial.print(">>"); char buf[4]; - for (int a=0; a Date: Fri, 15 Aug 2014 14:59:41 +0200 Subject: [PATCH 288/326] Forgot Readme changes --- MQTTGateway/MQTTGateway.ino | 89 +++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/MQTTGateway/MQTTGateway.ino b/MQTTGateway/MQTTGateway.ino index 868cc54..9cbc641 100755 --- a/MQTTGateway/MQTTGateway.ino +++ b/MQTTGateway/MQTTGateway.ino @@ -1,32 +1,63 @@ -// MyMQTT Gateway 0.1b -// -// Created by Daniel Wiegert -// Based on Mysensors Ethernet Gateway by Henrik Ekblad -// http://www.mysensors.org -// -// Requires MySensors lib 1.4b -// -// Don't forget to look at the definitions in MyMQTT.h! -// Don't forget to configure Radio pins, IP and MAC-address! -// -// Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light -// NodeID and SensorID is Decimal number. -// Last segment is translation of the sensor type, look inside MyMQTT.cpp for -// the definitions. User can change this to their needs. -// -// Features: -// Supports automatic nodeID delegation -// Recieve sketchname and version Example: (openhab item) -//String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} -//String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} -// ... -// -// Todo: -// DOCUMENTATION... -// Special commands : clear or set EEPROM Values -// Special commands : Reboot -// ... -/* +/* MyMQTT Gateway 0.1b + + Created by Daniel Wiegert + Based on Mysensors Ethernet Gateway by Henrik Ekblad + Requires MySensors lib 1.4b + http://www.mysensors.org + + * Don't forget to look at the definitions in MyMQTT.h! + * Don't forget to configure Radio pins, IP and MAC-address! + + * Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light + NodeID and SensorID is number (0-255). + Last segment is translation of the sensor type, look inside MyMQTT.cpp for + the definitions. User can change this to their needs. + +Example openhab setup: http://www.openhab.org/ + +* openhab.cfg +--- +mqtt:mysensor.url=tcp://192.168.0.234:1883 +mqtt:mysensor.clientId=MQTT +--- + +* items/test.items +--- +Group test +Number Temp_test "Temp [%.1f °C]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Temperature:state:default]"} +Number Hum_test "Hum [%.1f %%]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Humidity:state:default]"} +Number test_test "test [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/3/#:state:default]"} +Switch sw1 "sw 1" (test) {<[mysensor:MyMQTT/21/1/Light:command:MAP(1.map)]"} +--- (Note; # = Wildcard character) + +* sitemap/test.site +--- +sitemap demo label="Menu" +Frame label="Openhab" { + Group item=test label="Test group" +} +--- + +* transform/1.map +--- +1=ON +0=OFF +--- + +* Features: + - Supports automatic nodeID delegation + - Recieve sketchname and version Example: (openhab item) +String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} +String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} +[...] + +* Todo: + - DOCUMENTATION... + - Special commands + Read and set EEPROM Values + Send Reboot, And reboot gateway itself. +... + Sketch uses 23,666 bytes (77%) of program storage space. Maximum is 30,720 bytes. Global variables use 766 bytes (37%) of dynamic memory, leaving 1,282 bytes for local variables. Maximum is 2,048 bytes. */ From f822f1aa310bda62aafe1f0b0f7246aa7466f35d Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 15 Aug 2014 23:40:45 +0200 Subject: [PATCH 289/326] New example skecth which controls a lock either from controller or by using a secret knock. --- .../SecretKnockSensor/SecretKnockSensor.ino | 346 ++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino diff --git a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino new file mode 100644 index 0000000..6fa8b59 --- /dev/null +++ b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino @@ -0,0 +1,346 @@ +/* Secret Knock Trinket + Code for running a secret knock lock on the Adafruit Trinket. + + See full instructions here: + https://learn.adafruit.com/secret-knock-activated-drawer-lock/ + Version 13.10.31 Built with Arduino IDE 1.0.5 + + By Steve Hoefer http://grathio.com + Adapted to MySensors by Henrik Ekblad + + Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0 + http://creativecommons.org/licenses/by-nc-sa/3.0/us/ + (In short: Do what you want, as long as you credit me, don't relicense it, and don't sell it or use it in anything you sell without contacting me.) + + ------Wiring------ + Pin 0: Program button used for recording a new Knock (connect Pin0 -> button -> GND) + Pin 1: Optional: Connect LED here (remember resisor in series) + Pin 2: Optional: Piezo element (for beeps). + Pin A1 (Analog 1): A sound sensor for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg + Pin 4: Connects to a 1. Relay which open door or lock or 2. transistor or that opens a solenoid lock when HIGH (see adafruit guide for this option). + + Connect radio according as usual(you can skip IRQ pin) + http://www.mysensors.org/build/connect_radio +*/ + +#include +#include + + +#define CHILD_ID 99 // Id of the sensor child + +const byte eepromValid = 121; // If the first byte in eeprom is this then the data is valid. + +/*Pin definitions*/ +const int programButton = 0; // (Digital 0) Record A New Knock button. +const int ledPin = 1; // (Digital 1) The LED pin (if any) +const int knockSensor = 5; // (Digital 5) for using the microphone digital output (tune knob to register knock) +const int audioOut = 2; // (Digital 2) for using the peizo as an output device. (Thing that goes beep.) +const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock. + +/*Tuning constants. Changing the values below changes the behavior of the device.*/ +int threshold = 3; // Minimum signal from the piezo to register as a knock. Higher = less sensitive. Typical values 1 - 10 +const int rejectValue = 25; // If an individual knock is off by this percentage of a knock we don't unlock. Typical values 10-30 +const int averageRejectValue = 15; // If the average timing of all the knocks is off by this percent we don't unlock. Typical values 5-20 +const int knockFadeTime = 150; // Milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.) +const int lockOperateTime = 2500; // Milliseconds that we operate the lock solenoid latch before releasing it. +const int maximumKnocks = 20; // Maximum number of knocks to listen for. +const int knockComplete = 1200; // Longest time to wait for a knock before we assume that it's finished. (milliseconds) + +byte secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initial setup: "Shave and a Hair Cut, two bits." +int knockReadings[maximumKnocks]; // When someone knocks this array fills with the delays between knocks. +int knockSensorValue = 0; // Last reading of the knock sensor. +boolean programModeActive = false; // True if we're trying to program a new knock. + +bool lockStatus; +MySensor gw; +MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS); + + +void setup() { + + pinMode(ledPin, OUTPUT); + pinMode(knockSensor, INPUT); + pinMode(lockPin, OUTPUT); + pinMode(programButton, INPUT); + digitalWrite(programButton, HIGH); // Enable internal pull up + + gw.begin(incomingMessage); + + gw.sendSketchInfo("Secret Knock", "1.0"); + gw.present(CHILD_ID, S_LOCK); + + readSecretKnock(); // Load the secret knock (if any) from EEPROM. + + digitalWrite(lockPin, HIGH); // Unlock the door for a bit when we power up. For system check and to allow a way in if the key is forgotten + delay(500); // Wait a short time + + lockStatus = gw.loadState(0); // Read last lock status from eeprom + setLockState(lockStatus, true); // Now set the last known state and send it to controller + + delay(500); // This delay is here because the solenoid lock returning to place can otherwise trigger and inadvertent knock. +} + +void loop() { + gw.process(); // Process incomming messages + // Listen for any knock at all. + knockSensorValue = digitalRead(knockSensor); + if (digitalRead(programButton) == LOW){ // is the program button pressed? + delay(100); // Cheap debounce. + if (digitalRead(programButton) == LOW){ + if (programModeActive == false){ // If we're not in programming mode, turn it on. + programModeActive = true; // Remember we're in programming mode. + digitalWrite(ledPin, HIGH); // Turn on the red light too so the user knows we're programming. + chirp(500, 1500); // And play a tone in case the user can't see the LED. + chirp(500, 1000); + } else { // If we are in programing mode, turn it off. + programModeActive = false; + digitalWrite(ledPin, LOW); + chirp(500, 1000); // Turn off the programming LED and play a sad note. + chirp(500, 1500); + delay(500); + } + while (digitalRead(programButton) == HIGH){ + delay(10); // Hang around until the button is released. + } + } + delay(250); // Another cheap debounce. Longer because releasing the button can sometimes be sensed as a knock. + } + + + if (knockSensorValue == 0) { + if (programModeActive == true){ // Blink the LED when we sense a knock. + digitalWrite(ledPin, LOW); + } else { + digitalWrite(ledPin, HIGH); + } + knockDelay(); + if (programModeActive == true){ // Un-blink the LED. + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + } + listenToSecretKnock(); // We have our first knock. Go and see what other knocks are in store... + } + +} + +// Records the timing of knocks. +void listenToSecretKnock(){ + int i = 0; + // First reset the listening array. + for (i=0; i < maximumKnocks; i++){ + knockReadings[i] = 0; + } + + int currentKnockNumber = 0; // Position counter for the array. + int startTime = millis(); // Reference for when this knock started. + int now = millis(); + + do { // Listen for the next knock or wait for it to timeout. + knockSensorValue = digitalRead(knockSensor); + + if (knockSensorValue == 0){ // Here's another knock. Save the time between knocks. + Serial.println("knock"); + + now=millis(); + knockReadings[currentKnockNumber] = now - startTime; + currentKnockNumber ++; + startTime = now; + + if (programModeActive==true){ // Blink the LED when we sense a knock. + digitalWrite(ledPin, LOW); + } else { + digitalWrite(ledPin, HIGH); + } + knockDelay(); + if (programModeActive == true){ // Un-blink the LED. + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + } + } + + now = millis(); + + // Stop listening if there are too many knocks or there is too much time between knocks. + } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks)); + Serial.println("end"); + + //we've got our knock recorded, lets see if it's valid + if (programModeActive == false){ // Only do this if we're not recording a new knock. + if (validateKnock() == true){ + // Lock/unlock door + setLockState(!lockStatus, true); + } else { + Serial.println("fail unlock"); + + // knock is invalid. Blink the LED as a warning to others. + for (i=0; i < 4; i++){ + digitalWrite(ledPin, HIGH); + delay(50); + digitalWrite(ledPin, LOW); + delay(50); + } + } + } else { // If we're in programming mode we still validate the lock because it makes some numbers we need, we just don't do anything with the return. + validateKnock(); + } +} + + +// Unlocks the door. +void setLockState(bool state, bool send){ + if (state) + Serial.println("open lock"); + else + Serial.println("close lock"); + if (send) + gw.send(lockMsg.set(state)); + + digitalWrite(ledPin, state); + digitalWrite(lockPin, state); + gw.saveState(0,state); + lockStatus = state; + delay(500); // This delay is here because releasing the latch can cause a vibration that will be sensed as a knock. +} + +// Checks to see if our knock matches the secret. +// Returns true if it's a good knock, false if it's not. +boolean validateKnock(){ + int i = 0; + + int currentKnockCount = 0; + int secretKnockCount = 0; + int maxKnockInterval = 0; // We use this later to normalize the times. + + for (i=0;i 0){ + currentKnockCount++; + } + if (secretCode[i] > 0){ + secretKnockCount++; + } + + if (knockReadings[i] > maxKnockInterval){ // Collect normalization data while we're looping. + maxKnockInterval = knockReadings[i]; + } + } + + // If we're recording a new knock, save the info and get out of here. + if (programModeActive == true){ + for (i=0; i < maximumKnocks; i++){ // Normalize the time between knocks. (the longest time = 100) + secretCode[i] = map(knockReadings[i], 0, maxKnockInterval, 0, 100); + } + saveSecretKnock(); // save the result to EEPROM + programModeActive = false; + playbackKnock(maxKnockInterval); + return false; + } + + if (currentKnockCount != secretKnockCount){ // Easiest check first. If the number of knocks is wrong, don't unlock. + return false; + } + + /* Now we compare the relative intervals of our knocks, not the absolute time between them. + (ie: if you do the same pattern slow or fast it should still open the door.) + This makes it less picky, which while making it less secure can also make it + less of a pain to use if you're tempo is a little slow or fast. + */ + int totaltimeDifferences = 0; + int timeDiff = 0; + for (i=0; i < maximumKnocks; i++){ // Normalize the times + knockReadings[i]= map(knockReadings[i], 0, maxKnockInterval, 0, 100); + timeDiff = abs(knockReadings[i] - secretCode[i]); + if (timeDiff > rejectValue){ // Individual value too far out of whack. No access for this knock! + return false; + } + totaltimeDifferences += timeDiff; + } + // It can also fail if the whole thing is too inaccurate. + if (totaltimeDifferences / secretKnockCount > averageRejectValue){ + return false; + } + + return true; +} + + +// reads the secret knock from EEPROM. (if any.) +void readSecretKnock(){ + byte reading; + int i; + reading = gw.loadState(1); + if (reading == eepromValid){ // only read EEPROM if the signature byte is correct. + for (int i=0; i < maximumKnocks ;i++){ + secretCode[i] = gw.loadState(i+2); + } + } +} + + +//saves a new pattern too eeprom +void saveSecretKnock(){ + gw.saveState(1, 0); // clear out the signature. That way we know if we didn't finish the write successfully. + for (int i=0; i < maximumKnocks; i++){ + gw.saveState(i+2, secretCode[i]); + } + gw.saveState(1, eepromValid); // all good. Write the signature so we'll know it's all good. +} + +// Plays back the pattern of the knock in blinks and beeps +void playbackKnock(int maxKnockInterval){ + digitalWrite(ledPin, LOW); + delay(1000); + digitalWrite(ledPin, HIGH); + chirp(200, 1800); + for (int i = 0; i < maximumKnocks ; i++){ + digitalWrite(ledPin, LOW); + // only turn it on if there's a delay + if (secretCode[i] > 0){ + delay(map(secretCode[i], 0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was. Roughly. + digitalWrite(ledPin, HIGH); + chirp(200, 1800); + } + } + digitalWrite(ledPin, LOW); +} + +// Deals with the knock delay thingy. +void knockDelay(){ + int itterations = (knockFadeTime / 20); // Wait for the peak to dissipate before listening to next one. + for (int i=0; i < itterations; i++){ + delay(10); + analogRead(knockSensor); // This is done in an attempt to defuse the analog sensor's capacitor that will give false readings on high impedance sensors. + delay(10); + } +} + +// Plays a non-musical tone on the piezo. +// playTime = milliseconds to play the tone +// delayTime = time in microseconds between ticks. (smaller=higher pitch tone.) +void chirp(int playTime, int delayTime){ + long loopTime = (playTime * 1000L) / delayTime; + pinMode(audioOut, OUTPUT); + for(int i=0; i < loopTime; i++){ + digitalWrite(audioOut, HIGH); + delayMicroseconds(delayTime); + digitalWrite(audioOut, LOW); + } + pinMode(audioOut, INPUT); +} + + + +void incomingMessage(const MyMessage &message) { + // We only expect one type of message from controller. But we better check anyway. + if (message.type==V_LOCK_STATUS) { + // Change relay state + setLockState(message.getBool(), false); + + // Write some debug info + Serial.print("Incoming lock status:"); + Serial.println(message.getBool()); + } +} + From 95d899b77ed81464abd23461ddf46df7fa160101 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sat, 16 Aug 2014 00:21:33 +0200 Subject: [PATCH 290/326] Updated comments --- .../SecretKnockSensor/SecretKnockSensor.ino | 675 +++++++++--------- 1 file changed, 338 insertions(+), 337 deletions(-) diff --git a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino index 6fa8b59..35b3566 100644 --- a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino +++ b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino @@ -1,346 +1,347 @@ -/* Secret Knock Trinket - Code for running a secret knock lock on the Adafruit Trinket. + /* Secret Knock Sensor - See full instructions here: - https://learn.adafruit.com/secret-knock-activated-drawer-lock/ - Version 13.10.31 Built with Arduino IDE 1.0.5 - - By Steve Hoefer http://grathio.com - Adapted to MySensors by Henrik Ekblad - - Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0 - http://creativecommons.org/licenses/by-nc-sa/3.0/us/ - (In short: Do what you want, as long as you credit me, don't relicense it, and don't sell it or use it in anything you sell without contacting me.) - - ------Wiring------ - Pin 0: Program button used for recording a new Knock (connect Pin0 -> button -> GND) - Pin 1: Optional: Connect LED here (remember resisor in series) - Pin 2: Optional: Piezo element (for beeps). - Pin A1 (Analog 1): A sound sensor for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg - Pin 4: Connects to a 1. Relay which open door or lock or 2. transistor or that opens a solenoid lock when HIGH (see adafruit guide for this option). - - Connect radio according as usual(you can skip IRQ pin) - http://www.mysensors.org/build/connect_radio -*/ - -#include -#include - - -#define CHILD_ID 99 // Id of the sensor child - -const byte eepromValid = 121; // If the first byte in eeprom is this then the data is valid. - -/*Pin definitions*/ -const int programButton = 0; // (Digital 0) Record A New Knock button. -const int ledPin = 1; // (Digital 1) The LED pin (if any) -const int knockSensor = 5; // (Digital 5) for using the microphone digital output (tune knob to register knock) -const int audioOut = 2; // (Digital 2) for using the peizo as an output device. (Thing that goes beep.) -const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock. - -/*Tuning constants. Changing the values below changes the behavior of the device.*/ -int threshold = 3; // Minimum signal from the piezo to register as a knock. Higher = less sensitive. Typical values 1 - 10 -const int rejectValue = 25; // If an individual knock is off by this percentage of a knock we don't unlock. Typical values 10-30 -const int averageRejectValue = 15; // If the average timing of all the knocks is off by this percent we don't unlock. Typical values 5-20 -const int knockFadeTime = 150; // Milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.) -const int lockOperateTime = 2500; // Milliseconds that we operate the lock solenoid latch before releasing it. -const int maximumKnocks = 20; // Maximum number of knocks to listen for. -const int knockComplete = 1200; // Longest time to wait for a knock before we assume that it's finished. (milliseconds) - -byte secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initial setup: "Shave and a Hair Cut, two bits." -int knockReadings[maximumKnocks]; // When someone knocks this array fills with the delays between knocks. -int knockSensorValue = 0; // Last reading of the knock sensor. -boolean programModeActive = false; // True if we're trying to program a new knock. - -bool lockStatus; -MySensor gw; -MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS); - - -void setup() { - - pinMode(ledPin, OUTPUT); - pinMode(knockSensor, INPUT); - pinMode(lockPin, OUTPUT); - pinMode(programButton, INPUT); - digitalWrite(programButton, HIGH); // Enable internal pull up - - gw.begin(incomingMessage); - - gw.sendSketchInfo("Secret Knock", "1.0"); - gw.present(CHILD_ID, S_LOCK); - - readSecretKnock(); // Load the secret knock (if any) from EEPROM. - - digitalWrite(lockPin, HIGH); // Unlock the door for a bit when we power up. For system check and to allow a way in if the key is forgotten - delay(500); // Wait a short time - - lockStatus = gw.loadState(0); // Read last lock status from eeprom - setLockState(lockStatus, true); // Now set the last known state and send it to controller - - delay(500); // This delay is here because the solenoid lock returning to place can otherwise trigger and inadvertent knock. -} - -void loop() { - gw.process(); // Process incomming messages - // Listen for any knock at all. - knockSensorValue = digitalRead(knockSensor); - if (digitalRead(programButton) == LOW){ // is the program button pressed? - delay(100); // Cheap debounce. - if (digitalRead(programButton) == LOW){ - if (programModeActive == false){ // If we're not in programming mode, turn it on. - programModeActive = true; // Remember we're in programming mode. - digitalWrite(ledPin, HIGH); // Turn on the red light too so the user knows we're programming. - chirp(500, 1500); // And play a tone in case the user can't see the LED. - chirp(500, 1000); - } else { // If we are in programing mode, turn it off. - programModeActive = false; - digitalWrite(ledPin, LOW); - chirp(500, 1000); // Turn off the programming LED and play a sad note. - chirp(500, 1500); - delay(500); - } - while (digitalRead(programButton) == HIGH){ - delay(10); // Hang around until the button is released. - } - } - delay(250); // Another cheap debounce. Longer because releasing the button can sometimes be sensed as a knock. - } - - - if (knockSensorValue == 0) { - if (programModeActive == true){ // Blink the LED when we sense a knock. - digitalWrite(ledPin, LOW); - } else { - digitalWrite(ledPin, HIGH); - } - knockDelay(); - if (programModeActive == true){ // Un-blink the LED. - digitalWrite(ledPin, HIGH); - } else { - digitalWrite(ledPin, LOW); - } - listenToSecretKnock(); // We have our first knock. Go and see what other knocks are in store... - } - -} - -// Records the timing of knocks. -void listenToSecretKnock(){ - int i = 0; - // First reset the listening array. - for (i=0; i < maximumKnocks; i++){ - knockReadings[i] = 0; - } - - int currentKnockNumber = 0; // Position counter for the array. - int startTime = millis(); // Reference for when this knock started. - int now = millis(); - - do { // Listen for the next knock or wait for it to timeout. - knockSensorValue = digitalRead(knockSensor); - - if (knockSensorValue == 0){ // Here's another knock. Save the time between knocks. - Serial.println("knock"); - - now=millis(); - knockReadings[currentKnockNumber] = now - startTime; - currentKnockNumber ++; - startTime = now; - - if (programModeActive==true){ // Blink the LED when we sense a knock. - digitalWrite(ledPin, LOW); - } else { - digitalWrite(ledPin, HIGH); - } - knockDelay(); - if (programModeActive == true){ // Un-blink the LED. - digitalWrite(ledPin, HIGH); - } else { - digitalWrite(ledPin, LOW); - } - } - - now = millis(); - - // Stop listening if there are too many knocks or there is too much time between knocks. - } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks)); - Serial.println("end"); - - //we've got our knock recorded, lets see if it's valid - if (programModeActive == false){ // Only do this if we're not recording a new knock. - if (validateKnock() == true){ - // Lock/unlock door - setLockState(!lockStatus, true); - } else { - Serial.println("fail unlock"); - - // knock is invalid. Blink the LED as a warning to others. - for (i=0; i < 4; i++){ - digitalWrite(ledPin, HIGH); - delay(50); - digitalWrite(ledPin, LOW); - delay(50); - } - } - } else { // If we're in programming mode we still validate the lock because it makes some numbers we need, we just don't do anything with the return. - validateKnock(); - } -} - - -// Unlocks the door. -void setLockState(bool state, bool send){ - if (state) - Serial.println("open lock"); - else - Serial.println("close lock"); - if (send) - gw.send(lockMsg.set(state)); - - digitalWrite(ledPin, state); - digitalWrite(lockPin, state); - gw.saveState(0,state); - lockStatus = state; - delay(500); // This delay is here because releasing the latch can cause a vibration that will be sensed as a knock. -} - -// Checks to see if our knock matches the secret. -// Returns true if it's a good knock, false if it's not. -boolean validateKnock(){ - int i = 0; - - int currentKnockCount = 0; - int secretKnockCount = 0; - int maxKnockInterval = 0; // We use this later to normalize the times. - - for (i=0;i 0){ - currentKnockCount++; - } - if (secretCode[i] > 0){ - secretKnockCount++; - } + See original instructions here (note: The MySensors adopted code might differ in wiring. The instructions below is correct): + https://learn.adafruit.com/secret-knock-activated-drawer-lock/ + Version 13.10.31 Built with Arduino IDE 1.0.5 + + By Steve Hoefer http://grathio.com + Adapted to MySensors by Henrik Ekblad + + Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0 + http://creativecommons.org/licenses/by-nc-sa/3.0/us/ + (In short: Do what you want, as long as you credit me, don't relicense it, and don't sell it or use it in anything you sell without contacting me.) + + ------Wiring------ + Pin 0: Program button used for recording a new Knock (connect Pin0 -> button -> GND) + Pin 1: Optional: Connect LED here (remember resisor in series) + Pin 2: Optional: Piezo element (for beeps). + Pin 1: A sound sensor (digital output) for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg + Pin 4: Connects to either 1. Relay which open door or lock or + 2. transistor that opens a solenoid lock when HIGH (see adafruit guide for this option). + + + Connect radio according as usual(you can skip IRQ pin) + http://www.mysensors.org/build/connect_radio + */ + + #include + #include - if (knockReadings[i] > maxKnockInterval){ // Collect normalization data while we're looping. - maxKnockInterval = knockReadings[i]; + + #define CHILD_ID 99 // Id of the sensor child + + const byte eepromValid = 121; // If the first byte in eeprom is this then the data is valid. + + /*Pin definitions*/ + const int programButton = 0; // (Digital 0) Record A New Knock button. + const int ledPin = 1; // (Digital 1) The LED pin (if any) + const int knockSensor = 5; // (Digital 5) for using the microphone digital output (tune knob to register knock) + const int audioOut = 2; // (Digital 2) for using the peizo as an output device. (Thing that goes beep.) + const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock. + + /*Tuning constants. Changing the values below changes the behavior of the device.*/ + int threshold = 3; // Minimum signal from the piezo to register as a knock. Higher = less sensitive. Typical values 1 - 10 + const int rejectValue = 25; // If an individual knock is off by this percentage of a knock we don't unlock. Typical values 10-30 + const int averageRejectValue = 15; // If the average timing of all the knocks is off by this percent we don't unlock. Typical values 5-20 + const int knockFadeTime = 150; // Milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.) + const int lockOperateTime = 2500; // Milliseconds that we operate the lock solenoid latch before releasing it. + const int maximumKnocks = 20; // Maximum number of knocks to listen for. + const int knockComplete = 1200; // Longest time to wait for a knock before we assume that it's finished. (milliseconds) + + byte secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initial setup: "Shave and a Hair Cut, two bits." + int knockReadings[maximumKnocks]; // When someone knocks this array fills with the delays between knocks. + int knockSensorValue = 0; // Last reading of the knock sensor. + boolean programModeActive = false; // True if we're trying to program a new knock. + + bool lockStatus; + MySensor gw; + MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS); + + + void setup() { + + pinMode(ledPin, OUTPUT); + pinMode(knockSensor, INPUT); + pinMode(lockPin, OUTPUT); + pinMode(programButton, INPUT); + digitalWrite(programButton, HIGH); // Enable internal pull up + + gw.begin(incomingMessage); + + gw.sendSketchInfo("Secret Knock", "1.0"); + gw.present(CHILD_ID, S_LOCK); + + readSecretKnock(); // Load the secret knock (if any) from EEPROM. + + digitalWrite(lockPin, HIGH); // Unlock the door for a bit when we power up. For system check and to allow a way in if the key is forgotten + delay(500); // Wait a short time + + lockStatus = gw.loadState(0); // Read last lock status from eeprom + setLockState(lockStatus, true); // Now set the last known state and send it to controller + + delay(500); // This delay is here because the solenoid lock returning to place can otherwise trigger and inadvertent knock. } - } - - // If we're recording a new knock, save the info and get out of here. - if (programModeActive == true){ - for (i=0; i < maximumKnocks; i++){ // Normalize the time between knocks. (the longest time = 100) - secretCode[i] = map(knockReadings[i], 0, maxKnockInterval, 0, 100); + + void loop() { + gw.process(); // Process incomming messages + // Listen for any knock at all. + knockSensorValue = digitalRead(knockSensor); + if (digitalRead(programButton) == LOW){ // is the program button pressed? + delay(100); // Cheap debounce. + if (digitalRead(programButton) == LOW){ + if (programModeActive == false){ // If we're not in programming mode, turn it on. + programModeActive = true; // Remember we're in programming mode. + digitalWrite(ledPin, HIGH); // Turn on the red light too so the user knows we're programming. + chirp(500, 1500); // And play a tone in case the user can't see the LED. + chirp(500, 1000); + } else { // If we are in programing mode, turn it off. + programModeActive = false; + digitalWrite(ledPin, LOW); + chirp(500, 1000); // Turn off the programming LED and play a sad note. + chirp(500, 1500); + delay(500); + } + while (digitalRead(programButton) == HIGH){ + delay(10); // Hang around until the button is released. + } + } + delay(250); // Another cheap debounce. Longer because releasing the button can sometimes be sensed as a knock. + } + + + if (knockSensorValue == 0) { + if (programModeActive == true){ // Blink the LED when we sense a knock. + digitalWrite(ledPin, LOW); + } else { + digitalWrite(ledPin, HIGH); + } + knockDelay(); + if (programModeActive == true){ // Un-blink the LED. + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + } + listenToSecretKnock(); // We have our first knock. Go and see what other knocks are in store... + } + + } + + // Records the timing of knocks. + void listenToSecretKnock(){ + int i = 0; + // First reset the listening array. + for (i=0; i < maximumKnocks; i++){ + knockReadings[i] = 0; + } + + int currentKnockNumber = 0; // Position counter for the array. + int startTime = millis(); // Reference for when this knock started. + int now = millis(); + + do { // Listen for the next knock or wait for it to timeout. + knockSensorValue = digitalRead(knockSensor); + + if (knockSensorValue == 0){ // Here's another knock. Save the time between knocks. + Serial.println("knock"); + + now=millis(); + knockReadings[currentKnockNumber] = now - startTime; + currentKnockNumber ++; + startTime = now; + + if (programModeActive==true){ // Blink the LED when we sense a knock. + digitalWrite(ledPin, LOW); + } else { + digitalWrite(ledPin, HIGH); + } + knockDelay(); + if (programModeActive == true){ // Un-blink the LED. + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + } + } + + now = millis(); + + // Stop listening if there are too many knocks or there is too much time between knocks. + } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks)); + Serial.println("end"); + + //we've got our knock recorded, lets see if it's valid + if (programModeActive == false){ // Only do this if we're not recording a new knock. + if (validateKnock() == true){ + // Lock/unlock door + setLockState(!lockStatus, true); + } else { + Serial.println("fail unlock"); + + // knock is invalid. Blink the LED as a warning to others. + for (i=0; i < 4; i++){ + digitalWrite(ledPin, HIGH); + delay(50); + digitalWrite(ledPin, LOW); + delay(50); + } + } + } else { // If we're in programming mode we still validate the lock because it makes some numbers we need, we just don't do anything with the return. + validateKnock(); } - saveSecretKnock(); // save the result to EEPROM - programModeActive = false; - playbackKnock(maxKnockInterval); - return false; - } - - if (currentKnockCount != secretKnockCount){ // Easiest check first. If the number of knocks is wrong, don't unlock. - return false; - } - - /* Now we compare the relative intervals of our knocks, not the absolute time between them. - (ie: if you do the same pattern slow or fast it should still open the door.) - This makes it less picky, which while making it less secure can also make it - less of a pain to use if you're tempo is a little slow or fast. - */ - int totaltimeDifferences = 0; - int timeDiff = 0; - for (i=0; i < maximumKnocks; i++){ // Normalize the times - knockReadings[i]= map(knockReadings[i], 0, maxKnockInterval, 0, 100); - timeDiff = abs(knockReadings[i] - secretCode[i]); - if (timeDiff > rejectValue){ // Individual value too far out of whack. No access for this knock! - return false; } - totaltimeDifferences += timeDiff; - } - // It can also fail if the whole thing is too inaccurate. - if (totaltimeDifferences / secretKnockCount > averageRejectValue){ - return false; - } - - return true; -} - - -// reads the secret knock from EEPROM. (if any.) -void readSecretKnock(){ - byte reading; - int i; - reading = gw.loadState(1); - if (reading == eepromValid){ // only read EEPROM if the signature byte is correct. - for (int i=0; i < maximumKnocks ;i++){ - secretCode[i] = gw.loadState(i+2); + + + // Unlocks the door. + void setLockState(bool state, bool send){ + if (state) + Serial.println("open lock"); + else + Serial.println("close lock"); + if (send) + gw.send(lockMsg.set(state)); + + digitalWrite(ledPin, state); + digitalWrite(lockPin, state); + gw.saveState(0,state); + lockStatus = state; + delay(500); // This delay is here because releasing the latch can cause a vibration that will be sensed as a knock. } - } -} - - -//saves a new pattern too eeprom -void saveSecretKnock(){ - gw.saveState(1, 0); // clear out the signature. That way we know if we didn't finish the write successfully. - for (int i=0; i < maximumKnocks; i++){ - gw.saveState(i+2, secretCode[i]); - } - gw.saveState(1, eepromValid); // all good. Write the signature so we'll know it's all good. -} - -// Plays back the pattern of the knock in blinks and beeps -void playbackKnock(int maxKnockInterval){ - digitalWrite(ledPin, LOW); - delay(1000); - digitalWrite(ledPin, HIGH); - chirp(200, 1800); - for (int i = 0; i < maximumKnocks ; i++){ - digitalWrite(ledPin, LOW); - // only turn it on if there's a delay - if (secretCode[i] > 0){ - delay(map(secretCode[i], 0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was. Roughly. - digitalWrite(ledPin, HIGH); - chirp(200, 1800); + + // Checks to see if our knock matches the secret. + // Returns true if it's a good knock, false if it's not. + boolean validateKnock(){ + int i = 0; + + int currentKnockCount = 0; + int secretKnockCount = 0; + int maxKnockInterval = 0; // We use this later to normalize the times. + + for (i=0;i 0){ + currentKnockCount++; + } + if (secretCode[i] > 0){ + secretKnockCount++; + } + + if (knockReadings[i] > maxKnockInterval){ // Collect normalization data while we're looping. + maxKnockInterval = knockReadings[i]; } } - digitalWrite(ledPin, LOW); -} - -// Deals with the knock delay thingy. -void knockDelay(){ - int itterations = (knockFadeTime / 20); // Wait for the peak to dissipate before listening to next one. - for (int i=0; i < itterations; i++){ - delay(10); - analogRead(knockSensor); // This is done in an attempt to defuse the analog sensor's capacitor that will give false readings on high impedance sensors. - delay(10); - } -} - -// Plays a non-musical tone on the piezo. -// playTime = milliseconds to play the tone -// delayTime = time in microseconds between ticks. (smaller=higher pitch tone.) -void chirp(int playTime, int delayTime){ - long loopTime = (playTime * 1000L) / delayTime; - pinMode(audioOut, OUTPUT); - for(int i=0; i < loopTime; i++){ - digitalWrite(audioOut, HIGH); - delayMicroseconds(delayTime); - digitalWrite(audioOut, LOW); - } - pinMode(audioOut, INPUT); -} - - - -void incomingMessage(const MyMessage &message) { - // We only expect one type of message from controller. But we better check anyway. - if (message.type==V_LOCK_STATUS) { - // Change relay state - setLockState(message.getBool(), false); - - // Write some debug info - Serial.print("Incoming lock status:"); - Serial.println(message.getBool()); - } -} + + // If we're recording a new knock, save the info and get out of here. + if (programModeActive == true){ + for (i=0; i < maximumKnocks; i++){ // Normalize the time between knocks. (the longest time = 100) + secretCode[i] = map(knockReadings[i], 0, maxKnockInterval, 0, 100); + } + saveSecretKnock(); // save the result to EEPROM + programModeActive = false; + playbackKnock(maxKnockInterval); + return false; + } + + if (currentKnockCount != secretKnockCount){ // Easiest check first. If the number of knocks is wrong, don't unlock. + return false; + } + + /* Now we compare the relative intervals of our knocks, not the absolute time between them. + (ie: if you do the same pattern slow or fast it should still open the door.) + This makes it less picky, which while making it less secure can also make it + less of a pain to use if you're tempo is a little slow or fast. + */ + int totaltimeDifferences = 0; + int timeDiff = 0; + for (i=0; i < maximumKnocks; i++){ // Normalize the times + knockReadings[i]= map(knockReadings[i], 0, maxKnockInterval, 0, 100); + timeDiff = abs(knockReadings[i] - secretCode[i]); + if (timeDiff > rejectValue){ // Individual value too far out of whack. No access for this knock! + return false; + } + totaltimeDifferences += timeDiff; + } + // It can also fail if the whole thing is too inaccurate. + if (totaltimeDifferences / secretKnockCount > averageRejectValue){ + return false; + } + + return true; + } + + + // reads the secret knock from EEPROM. (if any.) + void readSecretKnock(){ + byte reading; + int i; + reading = gw.loadState(1); + if (reading == eepromValid){ // only read EEPROM if the signature byte is correct. + for (int i=0; i < maximumKnocks ;i++){ + secretCode[i] = gw.loadState(i+2); + } + } + } + + + //saves a new pattern too eeprom + void saveSecretKnock(){ + gw.saveState(1, 0); // clear out the signature. That way we know if we didn't finish the write successfully. + for (int i=0; i < maximumKnocks; i++){ + gw.saveState(i+2, secretCode[i]); + } + gw.saveState(1, eepromValid); // all good. Write the signature so we'll know it's all good. + } + + // Plays back the pattern of the knock in blinks and beeps + void playbackKnock(int maxKnockInterval){ + digitalWrite(ledPin, LOW); + delay(1000); + digitalWrite(ledPin, HIGH); + chirp(200, 1800); + for (int i = 0; i < maximumKnocks ; i++){ + digitalWrite(ledPin, LOW); + // only turn it on if there's a delay + if (secretCode[i] > 0){ + delay(map(secretCode[i], 0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was. Roughly. + digitalWrite(ledPin, HIGH); + chirp(200, 1800); + } + } + digitalWrite(ledPin, LOW); + } + + // Deals with the knock delay thingy. + void knockDelay(){ + int itterations = (knockFadeTime / 20); // Wait for the peak to dissipate before listening to next one. + for (int i=0; i < itterations; i++){ + delay(10); + analogRead(knockSensor); // This is done in an attempt to defuse the analog sensor's capacitor that will give false readings on high impedance sensors. + delay(10); + } + } + + // Plays a non-musical tone on the piezo. + // playTime = milliseconds to play the tone + // delayTime = time in microseconds between ticks. (smaller=higher pitch tone.) + void chirp(int playTime, int delayTime){ + long loopTime = (playTime * 1000L) / delayTime; + pinMode(audioOut, OUTPUT); + for(int i=0; i < loopTime; i++){ + digitalWrite(audioOut, HIGH); + delayMicroseconds(delayTime); + digitalWrite(audioOut, LOW); + } + pinMode(audioOut, INPUT); + } + + + + void incomingMessage(const MyMessage &message) { + // We only expect one type of message from controller. But we better check anyway. + if (message.type==V_LOCK_STATUS) { + // Change relay state + setLockState(message.getBool(), false); + + // Write some debug info + Serial.print("Incoming lock status:"); + Serial.println(message.getBool()); + } + } From 8da32be65264c636d4d0b8aeb752a5afec0dcc8a Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sat, 16 Aug 2014 00:43:09 +0200 Subject: [PATCH 291/326] Comment fixed --- .../MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino index 35b3566..c411e90 100644 --- a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino +++ b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino @@ -15,7 +15,7 @@ Pin 0: Program button used for recording a new Knock (connect Pin0 -> button -> GND) Pin 1: Optional: Connect LED here (remember resisor in series) Pin 2: Optional: Piezo element (for beeps). - Pin 1: A sound sensor (digital output) for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg + Pin 5: A sound sensor (digital output) for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg Pin 4: Connects to either 1. Relay which open door or lock or 2. transistor that opens a solenoid lock when HIGH (see adafruit guide for this option). From c910bfdb9e54a9a41e991734b60c757968bd8210 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 13:57:51 +0200 Subject: [PATCH 292/326] Move routing message part to the top of message to ease later refactoring of header/payload (encryption etc). --- libraries/MySensors/MyMessage.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index aa3f56c..c7893bf 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -170,6 +170,9 @@ struct { #endif + uint8_t last; // 8 bit - Id of last node this message passed + uint8_t sender; // 8 bit - Id of sender node (origin) + uint8_t destination; // 8 bit - Id of destination node uint8_t version_length; // 3 bit - Protocol version // 5 bit - Length of payload @@ -177,9 +180,6 @@ struct // 1 bit - Request an ack - Indicator that receiver should send an ack back. // 1 bit - Is ack messsage - Indicator that this is the actual ack message. // 3 bit - Payload data type - uint8_t sender; // 8 bit - Id of sender node - uint8_t last; // 8 bit - Id of last node this message passed - uint8_t destination; // 8 bit - Id of destination node uint8_t type; // 8 bit - Type varies depending on command uint8_t sensor; // 8 bit - Id of sensor that this message concerns. From 0d00e224982365f3a9ff847f194b7c1ef5cd9ea9 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 13:58:21 +0200 Subject: [PATCH 293/326] Fix error and add chirp when correct code entered. --- .../examples/SecretKnockSensor/SecretKnockSensor.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino index c411e90..df822b2 100644 --- a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino +++ b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino @@ -101,7 +101,7 @@ chirp(500, 1500); delay(500); } - while (digitalRead(programButton) == HIGH){ + while (digitalRead(programButton) == LOW){ delay(10); // Hang around until the button is released. } } @@ -172,6 +172,8 @@ if (programModeActive == false){ // Only do this if we're not recording a new knock. if (validateKnock() == true){ // Lock/unlock door + chirp(500, 1500); // And play a tone in case the user can't see the LED. + chirp(500, 1000); setLockState(!lockStatus, true); } else { Serial.println("fail unlock"); From ec53d08e5738e1e629dc85ed85f7ad41283fef5d Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 14:45:17 +0200 Subject: [PATCH 294/326] Fix indentation. --- .../SecretKnockSensor/SecretKnockSensor.ino | 680 +++++++++--------- 1 file changed, 340 insertions(+), 340 deletions(-) diff --git a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino index df822b2..9f5f00f 100644 --- a/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino +++ b/libraries/MySensors/examples/SecretKnockSensor/SecretKnockSensor.ino @@ -1,349 +1,349 @@ - /* Secret Knock Sensor +/* Secret Knock Sensor - See original instructions here (note: The MySensors adopted code might differ in wiring. The instructions below is correct): - https://learn.adafruit.com/secret-knock-activated-drawer-lock/ - Version 13.10.31 Built with Arduino IDE 1.0.5 - - By Steve Hoefer http://grathio.com - Adapted to MySensors by Henrik Ekblad - - Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0 - http://creativecommons.org/licenses/by-nc-sa/3.0/us/ - (In short: Do what you want, as long as you credit me, don't relicense it, and don't sell it or use it in anything you sell without contacting me.) - - ------Wiring------ - Pin 0: Program button used for recording a new Knock (connect Pin0 -> button -> GND) - Pin 1: Optional: Connect LED here (remember resisor in series) - Pin 2: Optional: Piezo element (for beeps). - Pin 5: A sound sensor (digital output) for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg - Pin 4: Connects to either 1. Relay which open door or lock or - 2. transistor that opens a solenoid lock when HIGH (see adafruit guide for this option). - - - Connect radio according as usual(you can skip IRQ pin) - http://www.mysensors.org/build/connect_radio - */ - - #include - #include - - - #define CHILD_ID 99 // Id of the sensor child - - const byte eepromValid = 121; // If the first byte in eeprom is this then the data is valid. - - /*Pin definitions*/ - const int programButton = 0; // (Digital 0) Record A New Knock button. - const int ledPin = 1; // (Digital 1) The LED pin (if any) - const int knockSensor = 5; // (Digital 5) for using the microphone digital output (tune knob to register knock) - const int audioOut = 2; // (Digital 2) for using the peizo as an output device. (Thing that goes beep.) - const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock. - - /*Tuning constants. Changing the values below changes the behavior of the device.*/ - int threshold = 3; // Minimum signal from the piezo to register as a knock. Higher = less sensitive. Typical values 1 - 10 - const int rejectValue = 25; // If an individual knock is off by this percentage of a knock we don't unlock. Typical values 10-30 - const int averageRejectValue = 15; // If the average timing of all the knocks is off by this percent we don't unlock. Typical values 5-20 - const int knockFadeTime = 150; // Milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.) - const int lockOperateTime = 2500; // Milliseconds that we operate the lock solenoid latch before releasing it. - const int maximumKnocks = 20; // Maximum number of knocks to listen for. - const int knockComplete = 1200; // Longest time to wait for a knock before we assume that it's finished. (milliseconds) - - byte secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initial setup: "Shave and a Hair Cut, two bits." - int knockReadings[maximumKnocks]; // When someone knocks this array fills with the delays between knocks. - int knockSensorValue = 0; // Last reading of the knock sensor. - boolean programModeActive = false; // True if we're trying to program a new knock. - - bool lockStatus; - MySensor gw; - MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS); - - - void setup() { - - pinMode(ledPin, OUTPUT); - pinMode(knockSensor, INPUT); - pinMode(lockPin, OUTPUT); - pinMode(programButton, INPUT); - digitalWrite(programButton, HIGH); // Enable internal pull up - - gw.begin(incomingMessage); - - gw.sendSketchInfo("Secret Knock", "1.0"); - gw.present(CHILD_ID, S_LOCK); - - readSecretKnock(); // Load the secret knock (if any) from EEPROM. - - digitalWrite(lockPin, HIGH); // Unlock the door for a bit when we power up. For system check and to allow a way in if the key is forgotten - delay(500); // Wait a short time - - lockStatus = gw.loadState(0); // Read last lock status from eeprom - setLockState(lockStatus, true); // Now set the last known state and send it to controller - - delay(500); // This delay is here because the solenoid lock returning to place can otherwise trigger and inadvertent knock. - } - - void loop() { - gw.process(); // Process incomming messages - // Listen for any knock at all. - knockSensorValue = digitalRead(knockSensor); - if (digitalRead(programButton) == LOW){ // is the program button pressed? - delay(100); // Cheap debounce. - if (digitalRead(programButton) == LOW){ - if (programModeActive == false){ // If we're not in programming mode, turn it on. - programModeActive = true; // Remember we're in programming mode. - digitalWrite(ledPin, HIGH); // Turn on the red light too so the user knows we're programming. - chirp(500, 1500); // And play a tone in case the user can't see the LED. - chirp(500, 1000); - } else { // If we are in programing mode, turn it off. - programModeActive = false; - digitalWrite(ledPin, LOW); - chirp(500, 1000); // Turn off the programming LED and play a sad note. - chirp(500, 1500); - delay(500); - } - while (digitalRead(programButton) == LOW){ - delay(10); // Hang around until the button is released. - } - } - delay(250); // Another cheap debounce. Longer because releasing the button can sometimes be sensed as a knock. + See original instructions here (note: The MySensors adopted code might differ in wiring. The instructions below is correct): + https://learn.adafruit.com/secret-knock-activated-drawer-lock/ + Version 13.10.31 Built with Arduino IDE 1.0.5 + + By Steve Hoefer http://grathio.com + Adapted to MySensors by Henrik Ekblad + + Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0 + http://creativecommons.org/licenses/by-nc-sa/3.0/us/ + (In short: Do what you want, as long as you credit me, don't relicense it, and don't sell it or use it in anything you sell without contacting me.) + + ------Wiring------ + Pin 0: Program button used for recording a new Knock (connect Pin0 -> button -> GND) + Pin 1: Optional: Connect LED here (remember resisor in series) + Pin 2: Optional: Piezo element (for beeps). + Pin 5: A sound sensor (digital output) for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg + Pin 4: Connects to either 1. Relay which open door or lock or + 2. transistor that opens a solenoid lock when HIGH (see adafruit guide for this option). + + + Connect radio according as usual(you can skip IRQ pin) + http://www.mysensors.org/build/connect_radio +*/ + +#include +#include + + +#define CHILD_ID 99 // Id of the sensor child + +const byte eepromValid = 121; // If the first byte in eeprom is this then the data is valid. + +/*Pin definitions*/ +const int programButton = 0; // (Digital 0) Record A New Knock button. +const int ledPin = 1; // (Digital 1) The LED pin (if any) +const int knockSensor = 5; // (Digital 5) for using the microphone digital output (tune knob to register knock) +const int audioOut = 2; // (Digital 2) for using the peizo as an output device. (Thing that goes beep.) +const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock. + +/*Tuning constants. Changing the values below changes the behavior of the device.*/ +int threshold = 3; // Minimum signal from the piezo to register as a knock. Higher = less sensitive. Typical values 1 - 10 +const int rejectValue = 25; // If an individual knock is off by this percentage of a knock we don't unlock. Typical values 10-30 +const int averageRejectValue = 15; // If the average timing of all the knocks is off by this percent we don't unlock. Typical values 5-20 +const int knockFadeTime = 150; // Milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.) +const int lockOperateTime = 2500; // Milliseconds that we operate the lock solenoid latch before releasing it. +const int maximumKnocks = 20; // Maximum number of knocks to listen for. +const int knockComplete = 1200; // Longest time to wait for a knock before we assume that it's finished. (milliseconds) + +byte secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initial setup: "Shave and a Hair Cut, two bits." +int knockReadings[maximumKnocks]; // When someone knocks this array fills with the delays between knocks. +int knockSensorValue = 0; // Last reading of the knock sensor. +boolean programModeActive = false; // True if we're trying to program a new knock. + +bool lockStatus; +MySensor gw; +MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS); + + +void setup() { + + pinMode(ledPin, OUTPUT); + pinMode(knockSensor, INPUT); + pinMode(lockPin, OUTPUT); + pinMode(programButton, INPUT); + digitalWrite(programButton, HIGH); // Enable internal pull up + + gw.begin(incomingMessage); + + gw.sendSketchInfo("Secret Knock", "1.0"); + gw.present(CHILD_ID, S_LOCK); + + readSecretKnock(); // Load the secret knock (if any) from EEPROM. + + digitalWrite(lockPin, HIGH); // Unlock the door for a bit when we power up. For system check and to allow a way in if the key is forgotten + delay(500); // Wait a short time + + lockStatus = gw.loadState(0); // Read last lock status from eeprom + setLockState(lockStatus, true); // Now set the last known state and send it to controller + + delay(500); // This delay is here because the solenoid lock returning to place can otherwise trigger and inadvertent knock. +} + +void loop() { + gw.process(); // Process incomming messages + // Listen for any knock at all. + knockSensorValue = digitalRead(knockSensor); + if (digitalRead(programButton) == LOW){ // is the program button pressed? + delay(100); // Cheap debounce. + if (digitalRead(programButton) == LOW){ + if (programModeActive == false){ // If we're not in programming mode, turn it on. + programModeActive = true; // Remember we're in programming mode. + digitalWrite(ledPin, HIGH); // Turn on the red light too so the user knows we're programming. + chirp(500, 1500); // And play a tone in case the user can't see the LED. + chirp(500, 1000); + } else { // If we are in programing mode, turn it off. + programModeActive = false; + digitalWrite(ledPin, LOW); + chirp(500, 1000); // Turn off the programming LED and play a sad note. + chirp(500, 1500); + delay(500); } - - - if (knockSensorValue == 0) { - if (programModeActive == true){ // Blink the LED when we sense a knock. - digitalWrite(ledPin, LOW); - } else { - digitalWrite(ledPin, HIGH); - } - knockDelay(); - if (programModeActive == true){ // Un-blink the LED. - digitalWrite(ledPin, HIGH); - } else { - digitalWrite(ledPin, LOW); - } - listenToSecretKnock(); // We have our first knock. Go and see what other knocks are in store... - } - - } - - // Records the timing of knocks. - void listenToSecretKnock(){ - int i = 0; - // First reset the listening array. - for (i=0; i < maximumKnocks; i++){ - knockReadings[i] = 0; - } - - int currentKnockNumber = 0; // Position counter for the array. - int startTime = millis(); // Reference for when this knock started. - int now = millis(); - - do { // Listen for the next knock or wait for it to timeout. - knockSensorValue = digitalRead(knockSensor); - - if (knockSensorValue == 0){ // Here's another knock. Save the time between knocks. - Serial.println("knock"); - - now=millis(); - knockReadings[currentKnockNumber] = now - startTime; - currentKnockNumber ++; - startTime = now; - - if (programModeActive==true){ // Blink the LED when we sense a knock. - digitalWrite(ledPin, LOW); - } else { - digitalWrite(ledPin, HIGH); - } - knockDelay(); - if (programModeActive == true){ // Un-blink the LED. - digitalWrite(ledPin, HIGH); - } else { - digitalWrite(ledPin, LOW); - } - } - - now = millis(); - - // Stop listening if there are too many knocks or there is too much time between knocks. - } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks)); - Serial.println("end"); - - //we've got our knock recorded, lets see if it's valid - if (programModeActive == false){ // Only do this if we're not recording a new knock. - if (validateKnock() == true){ - // Lock/unlock door - chirp(500, 1500); // And play a tone in case the user can't see the LED. - chirp(500, 1000); - setLockState(!lockStatus, true); - } else { - Serial.println("fail unlock"); - - // knock is invalid. Blink the LED as a warning to others. - for (i=0; i < 4; i++){ - digitalWrite(ledPin, HIGH); - delay(50); - digitalWrite(ledPin, LOW); - delay(50); - } - } - } else { // If we're in programming mode we still validate the lock because it makes some numbers we need, we just don't do anything with the return. - validateKnock(); - } - } - - - // Unlocks the door. - void setLockState(bool state, bool send){ - if (state) - Serial.println("open lock"); - else - Serial.println("close lock"); - if (send) - gw.send(lockMsg.set(state)); - - digitalWrite(ledPin, state); - digitalWrite(lockPin, state); - gw.saveState(0,state); - lockStatus = state; - delay(500); // This delay is here because releasing the latch can cause a vibration that will be sensed as a knock. - } - - // Checks to see if our knock matches the secret. - // Returns true if it's a good knock, false if it's not. - boolean validateKnock(){ - int i = 0; - - int currentKnockCount = 0; - int secretKnockCount = 0; - int maxKnockInterval = 0; // We use this later to normalize the times. - - for (i=0;i 0){ - currentKnockCount++; - } - if (secretCode[i] > 0){ - secretKnockCount++; - } - - if (knockReadings[i] > maxKnockInterval){ // Collect normalization data while we're looping. - maxKnockInterval = knockReadings[i]; - } - } - - // If we're recording a new knock, save the info and get out of here. - if (programModeActive == true){ - for (i=0; i < maximumKnocks; i++){ // Normalize the time between knocks. (the longest time = 100) - secretCode[i] = map(knockReadings[i], 0, maxKnockInterval, 0, 100); - } - saveSecretKnock(); // save the result to EEPROM - programModeActive = false; - playbackKnock(maxKnockInterval); - return false; - } - - if (currentKnockCount != secretKnockCount){ // Easiest check first. If the number of knocks is wrong, don't unlock. - return false; - } - - /* Now we compare the relative intervals of our knocks, not the absolute time between them. - (ie: if you do the same pattern slow or fast it should still open the door.) - This makes it less picky, which while making it less secure can also make it - less of a pain to use if you're tempo is a little slow or fast. - */ - int totaltimeDifferences = 0; - int timeDiff = 0; - for (i=0; i < maximumKnocks; i++){ // Normalize the times - knockReadings[i]= map(knockReadings[i], 0, maxKnockInterval, 0, 100); - timeDiff = abs(knockReadings[i] - secretCode[i]); - if (timeDiff > rejectValue){ // Individual value too far out of whack. No access for this knock! - return false; - } - totaltimeDifferences += timeDiff; - } - // It can also fail if the whole thing is too inaccurate. - if (totaltimeDifferences / secretKnockCount > averageRejectValue){ - return false; - } - - return true; - } - - - // reads the secret knock from EEPROM. (if any.) - void readSecretKnock(){ - byte reading; - int i; - reading = gw.loadState(1); - if (reading == eepromValid){ // only read EEPROM if the signature byte is correct. - for (int i=0; i < maximumKnocks ;i++){ - secretCode[i] = gw.loadState(i+2); - } - } - } - - - //saves a new pattern too eeprom - void saveSecretKnock(){ - gw.saveState(1, 0); // clear out the signature. That way we know if we didn't finish the write successfully. - for (int i=0; i < maximumKnocks; i++){ - gw.saveState(i+2, secretCode[i]); - } - gw.saveState(1, eepromValid); // all good. Write the signature so we'll know it's all good. - } - - // Plays back the pattern of the knock in blinks and beeps - void playbackKnock(int maxKnockInterval){ - digitalWrite(ledPin, LOW); - delay(1000); - digitalWrite(ledPin, HIGH); - chirp(200, 1800); - for (int i = 0; i < maximumKnocks ; i++){ - digitalWrite(ledPin, LOW); - // only turn it on if there's a delay - if (secretCode[i] > 0){ - delay(map(secretCode[i], 0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was. Roughly. - digitalWrite(ledPin, HIGH); - chirp(200, 1800); - } - } - digitalWrite(ledPin, LOW); - } - - // Deals with the knock delay thingy. - void knockDelay(){ - int itterations = (knockFadeTime / 20); // Wait for the peak to dissipate before listening to next one. - for (int i=0; i < itterations; i++){ - delay(10); - analogRead(knockSensor); // This is done in an attempt to defuse the analog sensor's capacitor that will give false readings on high impedance sensors. - delay(10); + while (digitalRead(programButton) == LOW){ + delay(10); // Hang around until the button is released. } } - - // Plays a non-musical tone on the piezo. - // playTime = milliseconds to play the tone - // delayTime = time in microseconds between ticks. (smaller=higher pitch tone.) - void chirp(int playTime, int delayTime){ - long loopTime = (playTime * 1000L) / delayTime; - pinMode(audioOut, OUTPUT); - for(int i=0; i < loopTime; i++){ - digitalWrite(audioOut, HIGH); - delayMicroseconds(delayTime); - digitalWrite(audioOut, LOW); + delay(250); // Another cheap debounce. Longer because releasing the button can sometimes be sensed as a knock. + } + + + if (knockSensorValue == 0) { + if (programModeActive == true){ // Blink the LED when we sense a knock. + digitalWrite(ledPin, LOW); + } else { + digitalWrite(ledPin, HIGH); + } + knockDelay(); + if (programModeActive == true){ // Un-blink the LED. + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + } + listenToSecretKnock(); // We have our first knock. Go and see what other knocks are in store... + } + +} + +// Records the timing of knocks. +void listenToSecretKnock(){ + int i = 0; + // First reset the listening array. + for (i=0; i < maximumKnocks; i++){ + knockReadings[i] = 0; + } + + int currentKnockNumber = 0; // Position counter for the array. + int startTime = millis(); // Reference for when this knock started. + int now = millis(); + + do { // Listen for the next knock or wait for it to timeout. + knockSensorValue = digitalRead(knockSensor); + + if (knockSensorValue == 0){ // Here's another knock. Save the time between knocks. + Serial.println("knock"); + + now=millis(); + knockReadings[currentKnockNumber] = now - startTime; + currentKnockNumber ++; + startTime = now; + + if (programModeActive==true){ // Blink the LED when we sense a knock. + digitalWrite(ledPin, LOW); + } else { + digitalWrite(ledPin, HIGH); + } + knockDelay(); + if (programModeActive == true){ // Un-blink the LED. + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + } + } + + now = millis(); + + // Stop listening if there are too many knocks or there is too much time between knocks. + } while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks)); + Serial.println("end"); + + //we've got our knock recorded, lets see if it's valid + if (programModeActive == false){ // Only do this if we're not recording a new knock. + if (validateKnock() == true){ + // Lock/unlock door + chirp(500, 1500); // And play a tone in case the user can't see the LED. + chirp(500, 1000); + setLockState(!lockStatus, true); + } else { + Serial.println("fail unlock"); + + // knock is invalid. Blink the LED as a warning to others. + for (i=0; i < 4; i++){ + digitalWrite(ledPin, HIGH); + delay(50); + digitalWrite(ledPin, LOW); + delay(50); } - pinMode(audioOut, INPUT); + } + } else { // If we're in programming mode we still validate the lock because it makes some numbers we need, we just don't do anything with the return. + validateKnock(); + } +} + + +// Unlocks the door. +void setLockState(bool state, bool send){ + if (state) + Serial.println("open lock"); + else + Serial.println("close lock"); + if (send) + gw.send(lockMsg.set(state)); + + digitalWrite(ledPin, state); + digitalWrite(lockPin, state); + gw.saveState(0,state); + lockStatus = state; + delay(500); // This delay is here because releasing the latch can cause a vibration that will be sensed as a knock. +} + +// Checks to see if our knock matches the secret. +// Returns true if it's a good knock, false if it's not. +boolean validateKnock(){ + int i = 0; + + int currentKnockCount = 0; + int secretKnockCount = 0; + int maxKnockInterval = 0; // We use this later to normalize the times. + + for (i=0;i 0){ + currentKnockCount++; } + if (secretCode[i] > 0){ + secretKnockCount++; + } - - - void incomingMessage(const MyMessage &message) { - // We only expect one type of message from controller. But we better check anyway. - if (message.type==V_LOCK_STATUS) { - // Change relay state - setLockState(message.getBool(), false); - - // Write some debug info - Serial.print("Incoming lock status:"); - Serial.println(message.getBool()); - } + if (knockReadings[i] > maxKnockInterval){ // Collect normalization data while we're looping. + maxKnockInterval = knockReadings[i]; + } + } + + // If we're recording a new knock, save the info and get out of here. + if (programModeActive == true){ + for (i=0; i < maximumKnocks; i++){ // Normalize the time between knocks. (the longest time = 100) + secretCode[i] = map(knockReadings[i], 0, maxKnockInterval, 0, 100); + } + saveSecretKnock(); // save the result to EEPROM + programModeActive = false; + playbackKnock(maxKnockInterval); + return false; + } + + if (currentKnockCount != secretKnockCount){ // Easiest check first. If the number of knocks is wrong, don't unlock. + return false; + } + + /* Now we compare the relative intervals of our knocks, not the absolute time between them. + (ie: if you do the same pattern slow or fast it should still open the door.) + This makes it less picky, which while making it less secure can also make it + less of a pain to use if you're tempo is a little slow or fast. + */ + int totaltimeDifferences = 0; + int timeDiff = 0; + for (i=0; i < maximumKnocks; i++){ // Normalize the times + knockReadings[i]= map(knockReadings[i], 0, maxKnockInterval, 0, 100); + timeDiff = abs(knockReadings[i] - secretCode[i]); + if (timeDiff > rejectValue){ // Individual value too far out of whack. No access for this knock! + return false; + } + totaltimeDifferences += timeDiff; + } + // It can also fail if the whole thing is too inaccurate. + if (totaltimeDifferences / secretKnockCount > averageRejectValue){ + return false; + } + + return true; +} + + +// reads the secret knock from EEPROM. (if any.) +void readSecretKnock(){ + byte reading; + int i; + reading = gw.loadState(1); + if (reading == eepromValid){ // only read EEPROM if the signature byte is correct. + for (int i=0; i < maximumKnocks ;i++){ + secretCode[i] = gw.loadState(i+2); } + } +} + + +//saves a new pattern too eeprom +void saveSecretKnock(){ + gw.saveState(1, 0); // clear out the signature. That way we know if we didn't finish the write successfully. + for (int i=0; i < maximumKnocks; i++){ + gw.saveState(i+2, secretCode[i]); + } + gw.saveState(1, eepromValid); // all good. Write the signature so we'll know it's all good. +} + +// Plays back the pattern of the knock in blinks and beeps +void playbackKnock(int maxKnockInterval){ + digitalWrite(ledPin, LOW); + delay(1000); + digitalWrite(ledPin, HIGH); + chirp(200, 1800); + for (int i = 0; i < maximumKnocks ; i++){ + digitalWrite(ledPin, LOW); + // only turn it on if there's a delay + if (secretCode[i] > 0){ + delay(map(secretCode[i], 0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was. Roughly. + digitalWrite(ledPin, HIGH); + chirp(200, 1800); + } + } + digitalWrite(ledPin, LOW); +} + +// Deals with the knock delay thingy. +void knockDelay(){ + int itterations = (knockFadeTime / 20); // Wait for the peak to dissipate before listening to next one. + for (int i=0; i < itterations; i++){ + delay(10); + analogRead(knockSensor); // This is done in an attempt to defuse the analog sensor's capacitor that will give false readings on high impedance sensors. + delay(10); + } +} + +// Plays a non-musical tone on the piezo. +// playTime = milliseconds to play the tone +// delayTime = time in microseconds between ticks. (smaller=higher pitch tone.) +void chirp(int playTime, int delayTime){ + long loopTime = (playTime * 1000L) / delayTime; + pinMode(audioOut, OUTPUT); + for(int i=0; i < loopTime; i++){ + digitalWrite(audioOut, HIGH); + delayMicroseconds(delayTime); + digitalWrite(audioOut, LOW); + } + pinMode(audioOut, INPUT); +} + + + +void incomingMessage(const MyMessage &message) { + // We only expect one type of message from controller. But we better check anyway. + if (message.type==V_LOCK_STATUS) { + // Change relay state + setLockState(message.getBool(), false); + + // Write some debug info + Serial.print("Incoming lock status:"); + Serial.println(message.getBool()); + } +} From 994d715b4e575a8ae2d8778c70915629fa765146 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 14:45:38 +0200 Subject: [PATCH 295/326] UV sensor example added --- .../MySensors/examples/UVSensor/UVSensor.ino | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 libraries/MySensors/examples/UVSensor/UVSensor.ino diff --git a/libraries/MySensors/examples/UVSensor/UVSensor.ino b/libraries/MySensors/examples/UVSensor/UVSensor.ino new file mode 100644 index 0000000..52358ad --- /dev/null +++ b/libraries/MySensors/examples/UVSensor/UVSensor.ino @@ -0,0 +1,64 @@ +/* + Vera Arduino UVM-30A + + connect the sensor as follows : + + + >>> 5V + - >>> GND + out >>> A0 + + Contribution: epierre, bulldoglowell + Cobverted to 1.4 by Henrik Ekblad + + License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) + +*/ + +#include +#include + +#define CHILD_ID_UV 0 +#define UV_SENSOR_ANALOG_PIN 0 +unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) + +MySensor gw; +MyMessage uvMsg(CHILD_ID_UV, V_UV); +int lastUV = -1; +int uvIndexValue [13] = { 50, 227, 318, 408, 503, 606, 696, 795, 881, 976, 1079, 1170, 3000}; +int uvIndex; + +void setup() +{ + gw.begin(); + + // Send the sketch version information to the gateway and Controller + gw.sendSketchInfo("UV Sensor", "1.1"); + + // Register all sensors to gateway (they will be created as child devices) + gw.present(CHILD_ID_UV, S_UV); + +} + +void loop() +{ + uint16_t uv = analogRead(0);// Get UV value + Serial.print("Uv reading: "); + Serial.println(uv); + for (int i = 0; i < 13; i++) + { + if (uv <= uvIndexValue[i]) + { + uvIndex = i; + break; + } + } + Serial.print("Uv index: "); + Serial.println(uvIndex); + + if (uvIndex != lastUV) { + gw.send(uvMsg.set(uvIndex)); + lastUV = uvIndex; + } + + gw.sleep(SLEEP_TIME); +} From 65e61efb750837d382015501a41b2d9d03f4c23d Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 14:49:44 +0200 Subject: [PATCH 296/326] Fix spelling error --- libraries/MySensors/examples/UVSensor/UVSensor.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/examples/UVSensor/UVSensor.ino b/libraries/MySensors/examples/UVSensor/UVSensor.ino index 52358ad..838cdbd 100644 --- a/libraries/MySensors/examples/UVSensor/UVSensor.ino +++ b/libraries/MySensors/examples/UVSensor/UVSensor.ino @@ -8,7 +8,7 @@ out >>> A0 Contribution: epierre, bulldoglowell - Cobverted to 1.4 by Henrik Ekblad + Converted to 1.4 by Henrik Ekblad License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) From 95467c43a740b08085d2b923e9664a9ccaea29ed Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 15:03:09 +0200 Subject: [PATCH 297/326] Add Custom sensor type that can be used in presentation when no other sensor fits. --- libraries/MySensors/MyMessage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index c7893bf..cd2d7b2 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -53,7 +53,7 @@ typedef enum { typedef enum { S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, 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_ARDUINO_REPEATER_NODE, S_LOCK, S_IR, S_WATER, S_AIR_QUALITY, S_CUSTOM } sensor; // Type of data stream (for streamed message) From dcb0babe63085366155388cb04d4bc6962f73af2 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 19:57:58 +0200 Subject: [PATCH 298/326] Dust sensor added. Thanks epierre. --- libraries/MySensors/MyMessage.h | 5 +- .../examples/DustSensor/DustSensor.ino | 80 +++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 libraries/MySensors/examples/DustSensor/DustSensor.ino diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index cd2d7b2..dddfcbe 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -38,7 +38,8 @@ typedef enum { 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_HEATER, V_HEATER_SW, 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_UP, V_DOWN, V_STOP, V_IR_SEND, V_IR_RECEIVE, V_FLOW, V_VOLUME, V_LOCK_STATUS, + V_DUST_LEVEL } data; // Type of internal messages (for internal messages) @@ -53,7 +54,7 @@ typedef enum { typedef enum { S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, 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_ARDUINO_REPEATER_NODE, S_LOCK, S_IR, S_WATER, S_AIR_QUALITY, S_CUSTOM, S_DUST } sensor; // Type of data stream (for streamed message) diff --git a/libraries/MySensors/examples/DustSensor/DustSensor.ino b/libraries/MySensors/examples/DustSensor/DustSensor.ino new file mode 100644 index 0000000..4ebe804 --- /dev/null +++ b/libraries/MySensors/examples/DustSensor/DustSensor.ino @@ -0,0 +1,80 @@ +/* + Arduino Dust Sensort + + connect the sensor as follows : + + VCC >>> 5V + A >>> A0 + GND >>> GND + + Based on: http://www.dfrobot.com/wiki/index.php/Sharp_GP2Y1010AU + Authors: Cyrille Médard de Chardon (serialC), Christophe Trefois (Trefex) + Contribution: epierre + COnverted to 1.4 by Henrik Ekblad + + The dust sensor used (see purchase guide for latest link): + http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=171259125886&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg + +*/ + +#include +#include + +#define CHILD_ID_DUST 0 +#define DUST_SENSOR_ANALOG_PIN 1 + +unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) +//VARIABLES +int val = 0; // variable to store the value coming from the sensor +float valDUST =0.0; +float lastDUST =0.0; +int samplingTime = 280; +int deltaTime = 40; +int sleepTime = 9680; +float voMeasured = 0; +float calcVoltage = 0; +float dustDensity = 0; + +MySensor gw; +MyMessage dustMsg(CHILD_ID_DUST, V_DUST_LEVEL); + +void setup() +{ + gw.begin(); + + // Send the sketch version information to the gateway and Controller + gw.sendSketchInfo("Dust Sensor", "1.1"); + + // Register all sensors to gateway (they will be created as child devices) + gw.present(CHILD_ID_DUST, S_DUST); + +} + +void loop() +{ + uint16_t voMeasured = analogRead(DUST_SENSOR_ANALOG_PIN);// Get DUST value + + // 0 - 5V mapped to 0 - 1023 integer values + // recover voltage + calcVoltage = voMeasured * (5.0 / 1024.0); + + // linear eqaution taken from http://www.howmuchsnow.com/arduino/airquality/ + // Chris Nafis (c) 2012 + dustDensity = (0.17 * calcVoltage - 0.1)*1000; + + Serial.print("Raw Signal Value (0-1023): "); + Serial.print(voMeasured); + + Serial.print(" - Voltage: "); + Serial.print(calcVoltage); + + Serial.print(" - Dust Density: "); + Serial.println(dustDensity); // unit: ug/m3 + + if (ceil(dustDensity) != lastDUST) { + gw.send(dustMsg.set((int)ceil(dustDensity))); + lastDUST = ceil(dustDensity); + } + + gw.sleep(SLEEP_TIME); +} From 1150c4417c7309bdfdb938fbf21a391323217449 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 19:58:29 +0200 Subject: [PATCH 299/326] Removed a few decimals from pressure value. --- libraries/MySensors/examples/PressureSensor/PressureSensor.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino index 94aa7e3..fafe08c 100644 --- a/libraries/MySensors/examples/PressureSensor/PressureSensor.ino +++ b/libraries/MySensors/examples/PressureSensor/PressureSensor.ino @@ -44,7 +44,7 @@ void setup() { } void loop() { - float pressure = bmp.readPressure(); + float pressure = bmp.readPressure()/100; float temperature = bmp.readTemperature(); if (!metric) { From b0917af27e415f4c45b6e1e7e6c610dd393b610d Mon Sep 17 00:00:00 2001 From: Daniel Wiegert Date: Sun, 17 Aug 2014 20:51:52 +0200 Subject: [PATCH 300/326] Changed from Switch/case to if/elseif to save some runtime ram --- libraries/MySensors/MyMessage.cpp | 79 +++++++++++++------------------ libraries/MySensors/MySensor.cpp | 15 ++---- 2 files changed, 38 insertions(+), 56 deletions(-) mode change 100644 => 100755 libraries/MySensors/MyMessage.cpp mode change 100644 => 100755 libraries/MySensors/MySensor.cpp diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp old mode 100644 new mode 100755 index 8c5492a..286b111 --- a/libraries/MySensors/MyMessage.cpp +++ b/libraries/MySensors/MyMessage.cpp @@ -64,28 +64,21 @@ char* MyMessage::getString(char *buffer) const { buffer[miGetLength()] = 0; return buffer; } else if (buffer != NULL) { - switch(payloadType) { - case P_BYTE: - itoa(bValue, buffer, 10); - break; - case P_INT16: - itoa(iValue, buffer, 10); - break; - case P_UINT16: - utoa(uiValue, buffer, 10); - break; - case P_LONG32: - ltoa(lValue, buffer, 10); - break; - case P_ULONG32: - ultoa(ulValue, buffer, 10); - break; - case P_FLOAT32: - dtostrf(fValue,2,fPrecision,buffer); - break; - case P_CUSTOM: - return getStream(buffer); - break; + if (payloadType == P_BYTE) { + itoa(bValue, buffer, 10); + } else if (payloadType == P_INT16) { + itoa(iValue, buffer, 10); + } else if (payloadType == P_UINT16) { + utoa(uiValue, buffer, 10); + } else if (payloadType == P_LONG32) { + ltoa(lValue, buffer, 10); + } else if (payloadType == P_ULONG32) { + + ultoa(ulValue, buffer, 10); + } else if (payloadType == P_FLOAT32) { + dtostrf(fValue,2,fPrecision,buffer); + } else if (payloadType == P_CUSTOM) { + return getStream(buffer); } return buffer; } else { @@ -94,12 +87,11 @@ char* MyMessage::getString(char *buffer) const { } uint8_t MyMessage::getByte() const { - switch(miGetPayloadType()) { - case P_BYTE: + if (miGetPayloadType() == P_BYTE) { return data[0]; - case P_STRING: + } else if (miGetPayloadType() == P_STRING) { return atoi(data); - default: + } else { return 0; } } @@ -109,56 +101,51 @@ bool MyMessage::getBool() const { } float MyMessage::getFloat() const { - switch(miGetPayloadType()) { - case P_FLOAT32: + if (miGetPayloadType() == P_FLOAT32) { return fValue; - case P_STRING: + } else if (miGetPayloadType() == P_STRING) { return atof(data); - default: + } else { return 0; } } long MyMessage::getLong() const { - switch(miGetPayloadType()) { - case P_LONG32: + if (miGetPayloadType() == P_LONG32) { return lValue; - case P_STRING: + } else if (miGetPayloadType() == P_STRING) { return atol(data); - default: + } else { return 0; } } unsigned long MyMessage::getULong() const { - switch(miGetPayloadType()) { - case P_ULONG32: + if (miGetPayloadType() == P_ULONG32) { return ulValue; - case P_STRING: + } else if (miGetPayloadType() == P_STRING) { return atol(data); - default: + } else { return 0; } } int MyMessage::getInt() const { - switch(miGetPayloadType()) { - case P_INT16: + if (miGetPayloadType() == P_INT16) { return iValue; - case P_STRING: + } else if (miGetPayloadType() == P_STRING) { return atoi(data); - default: + } else { return 0; } } unsigned int MyMessage::getUInt() const { - switch(miGetPayloadType()) { - case P_UINT16: + if (miGetPayloadType() == P_UINT16) { return uiValue; - case P_STRING: + } else if (miGetPayloadType() == P_STRING) { return atoi(data); - default: + } else { return 0; } diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp old mode 100644 new mode 100755 index 11fcf18..b43a46a --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -327,12 +327,10 @@ boolean MySensor::process() { } else if (sender == GATEWAY_ADDRESS) { bool isMetric; - switch (type) { - case I_REBOOT: + if (type == I_REBOOT) { wdt_enable(WDTO_15MS); for (;;); - break; - case I_ID_RESPONSE: + } else if (type == I_ID_RESPONSE) { if (nc.nodeId == AUTO) { nc.nodeId = msg.getByte(); // Write id to EEPROM @@ -346,8 +344,7 @@ boolean MySensor::process() { } debug(PSTR("id=%d\n"), nc.nodeId); } - break; - case I_CONFIG: + } else if (type == I_CONFIG) { // Pick up configuration from controller (currently only metric/imperial) // and store it in eeprom if changed isMetric = msg.getByte() == 'M' ; @@ -356,8 +353,7 @@ boolean MySensor::process() { eeprom_write_byte((uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric); //eeprom_write_block((const void*)&cc, (uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); } - break; - case I_CHILDREN: + } else if (type == I_CHILDREN) { if (repeaterMode && msg.getByte() == 'C') { // Clears child relay data for this node debug(PSTR("rd=clear\n")); @@ -366,8 +362,7 @@ boolean MySensor::process() { } sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); } - break; - case I_TIME: + } else if (type == I_TIME) { if (timeCallback != NULL) { // Deliver time to callback timeCallback(msg.getULong()); From 3af3014178d85a31d9a5ea92b677982926f07334 Mon Sep 17 00:00:00 2001 From: Daniel Wiegert Date: Sun, 17 Aug 2014 21:23:18 +0200 Subject: [PATCH 301/326] New comments and minor fixes --- MQTTGateway/MQTTGateway.ino | 181 ++++++++++++++++------------ libraries/MySensors/MyMQTT.cpp | 209 ++++++++++++++++----------------- libraries/MySensors/MyMQTT.h | 12 +- 3 files changed, 216 insertions(+), 186 deletions(-) diff --git a/MQTTGateway/MQTTGateway.ino b/MQTTGateway/MQTTGateway.ino index 9cbc641..467ac44 100755 --- a/MQTTGateway/MQTTGateway.ino +++ b/MQTTGateway/MQTTGateway.ino @@ -1,36 +1,77 @@ -/* MyMQTT Gateway 0.1b +/* MyMQTT Broker Gateway 0.1b Created by Daniel Wiegert - Based on Mysensors Ethernet Gateway by Henrik Ekblad + Based on MySensors Ethernet Gateway by Henrik Ekblad + http://www.mysensors.org + Requires MySensors lib 1.4b - http://www.mysensors.org - * Don't forget to look at the definitions in MyMQTT.h! - * Don't forget to configure Radio pins, IP and MAC-address! + * Change below; TCP_IP, TCP_PORT, TCP_MAC + This will listen on your selected TCP_IP:TCP_PORT below, Please change TCP_MAC your liking also. + *1 -> NOTE: Keep first byte at x2, x6, xA or xE (replace x with any hex value) for using Local Ranges. - * Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/Light - NodeID and SensorID is number (0-255). - Last segment is translation of the sensor type, look inside MyMQTT.cpp for - the definitions. User can change this to their needs. + *2 You can use standard pin set-up as MySensors recommends or if you own a IBOARD you may change + the radio-pins below if you hardware mod your iBoard. see [URL BELOW] for more details. + http://forum.mysensors.org/topic/224/iboard-cheap-single-board-ethernet-arduino-with-radio/5 -Example openhab setup: http://www.openhab.org/ + * Don't forget to look at the definitions in libraries\MySensors\MyMQTT.h! -* openhab.cfg + define TCPDUMP and connect serial interface if you have problems, please write on + http://forum.mysensors.org/ and explain your problem, include serial output. Don't forget to + turn on DEBUG in libraries\MySensors\MyConfig.h also. + + MQTT_FIRST_SENSORID is for 'DHCP' server in MyMQTT. You may limit the ID's with FIRST and LAST definition. + If you want your manually configured below 20 set MQTT_FIRST_SENSORID to 20. + To disable: set MQTT_FIRST_SENSORID to 255. + + MQTT_BROKER_PREFIX is the leading prefix for your nodes. This can be only one char if like. + + MQTT_SEND_SUBSCRIPTION is if you want the MyMQTT to send a empty payload message to your nodes. + This can be useful if you want to send latest state back to the MQTT client. Just check if incoming + message has any length or not. + + * Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/[SensorType] + NodeID and SensorID is uint8 (0-255) number. + Last segment is translation of the sensor type, look inside MyMQTT.cpp for the definitions. + User can change this to their needs. We have also left some space for custom types. + +Special: (sensor 255 reserved for special commands) +You can receive a node sketch name with MyMQTT/20/255/Sketch_name (or version with _version) + +To-do: + Special commands : clear or set EEPROM Values, Send REBOOT and Receive reboot for MyMQTT itself. + Be able to send ACK so client returns the data being sent. + ... Please come with ideas! + + Test in more MQTT clients, So far tested in openhab and MyMQTT for Android (Not my creation) + - http://www.openhab.org/ + - https://play.google.com/store/apps/details?id=at.tripwire.mqtt.client&hl=en + ... Please notify me if you use this broker with other software. + + + * Example Openhab configuration: +openhab.cfg --- mqtt:mysensor.url=tcp://192.168.0.234:1883 mqtt:mysensor.clientId=MQTT --- -* items/test.items +items/test.items --- Group test -Number Temp_test "Temp [%.1f °C]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Temperature:state:default]"} -Number Hum_test "Hum [%.1f %%]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Humidity:state:default]"} -Number test_test "test [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/3/#:state:default]"} -Switch sw1 "sw 1" (test) {<[mysensor:MyMQTT/21/1/Light:command:MAP(1.map)]"} +Number Temp_test "Temp [%.1f °C]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Temperature:state:default]"} +Number Hum_test "Hum [%.1f %%]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Humidity:state:default]"} +Number test_test "test [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/3/#:state:default]"} +Switch sw1 "sw 1" (test) {mqtt="<[mysensor:MyMQTT/21/1/Light:command:MAP(1on0off.map)]"} +Switch sw2 "sw2 recieve example" (test) {mqtt=">[mysensor:MyMQTT/21/2/Light:command:ON:1], + >[mysensor:MyMQTT/21/2/Light:command:OFF:0]"} +Switch sw3 "sw2 send + recieve example" (test) {mqtt=">[mysensor:MyMQTT/21/2/Light:command:ON:1], + >[mysensor:MyMQTT/21/2/Light:command:OFF:0],<[mysensor:MyMQTT/21/2/Light:command:MAP(1on0off.map)]"} +String sketch20 "Node 20 Sketch name [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} +String sketch21 "Node 21 Sketch name [%s]" (test) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} --- (Note; # = Wildcard character) -* sitemap/test.site +sitemap/test.site --- sitemap demo label="Menu" Frame label="Openhab" { @@ -38,96 +79,84 @@ Frame label="Openhab" { } --- -* transform/1.map +transform/1on0off.map --- 1=ON 0=OFF --- -* Features: - - Supports automatic nodeID delegation - - Recieve sketchname and version Example: (openhab item) -String sketch20 "Node 20 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} -String sketch21 "Node 21 Sketch name [%s]" (sketch,name,a) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} -[...] - -* Todo: - - DOCUMENTATION... - - Special commands - Read and set EEPROM Values - Send Reboot, And reboot gateway itself. -... - -Sketch uses 23,666 bytes (77%) of program storage space. Maximum is 30,720 bytes. -Global variables use 766 bytes (37%) of dynamic memory, leaving 1,282 bytes for local variables. Maximum is 2,048 bytes. */ + #include #include #include #include -// Use this for IBOARD modded to use standard MISO/MOSI/SCK More information see; -// http://forum.mysensors.org/topic/224/iboard-cheap-single-board-ethernet-arduino-with-radio/5 -//#define RADIO_CE_PIN 3 // radio chip enable -//#define RADIO_SPI_SS_PIN 8 // radio SPI serial select -//#define RADIO_ERROR_LED_PIN A2 // Error led pin -//#define RADIO_RX_LED_PIN A1 // Receive led pin -//#define RADIO_TX_LED_PIN A0 // the PCB, on board LED +// Use this for IBOARD modded to use standard MISO/MOSI/SCK, see note *1 above! +/* +#define RADIO_CE_PIN 3 // radio chip enable +#define RADIO_SPI_SS_PIN 8 // radio SPI serial select +#define RADIO_ERROR_LED_PIN A2 // Error led pin +#define RADIO_RX_LED_PIN A1 // Receive led pin +#define RADIO_TX_LED_PIN A0 // the PCB, on board LED*/ //Use this for default configured pro mini / nano etc : +///* #define RADIO_CE_PIN 5 // radio chip enable #define RADIO_SPI_SS_PIN 6 // radio SPI serial select #define RADIO_ERROR_LED_PIN 7 // Error led pin #define RADIO_RX_LED_PIN 8 // Receive led pin -#define RADIO_TX_LED_PIN 9 // the PCB, on board LED +#define RADIO_TX_LED_PIN 9 // the PCB, on board LED*/ -#define IP_PORT 1883 // MQTT Listening port -IPAddress myIp ( 192, 168, 0, 234 ); // Configure your static ip-address here -byte mac[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - Change this! -// * NOTE: Keep first byte at x2, x6, xA or xE (replace x with any hex value) for using Locally Administered Address Ranges. +#define TCP_PORT 1883 // Set your MQTT Broker Listening port. +IPAddress TCP_IP ( 192, 168, 0, 234 ); // Configure your static ip-address here +byte TCP_MAC[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - You should change this! see note *2 above! -EthernetServer server = EthernetServer(IP_PORT); +EthernetServer server = EthernetServer(TCP_PORT); MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN); -// Uncomment this constructor if you have leds and include button attached to your gateway -// MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); + +// -- Uncomment this constructor if you have leds and include button attached to your gateway +//MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); void processEthernetMessages() { - char inputString[MQTT_MAX_PACKET_SIZE] = ""; - int inputSize = 0; - EthernetClient client = server.available(); - if (client) { - while (client.available()) { - char inChar = client.read(); - inputString[inputSize] = inChar; - inputSize++; - } - gw.processMQTTMessage(inputString, inputSize); - } + char inputString[MQTT_MAX_PACKET_SIZE] = ""; + int inputSize = 0; + EthernetClient client = server.available(); + if (client) { + while (client.available()) { + char inChar = client.read(); + inputString[inputSize] = inChar; + inputSize++; + } + gw.processMQTTMessage(inputString, inputSize); + } } void writeEthernet(char *writeBuffer, int *writeSize) { - server.write(writeBuffer, *writeSize); // Should this really be *writeSize? + server.write(writeBuffer, *writeSize); // Todo: Should this really be *writeSize? } void ledTimersInterrupt() { - gw.ledTimersInterrupt(); + gw.ledTimersInterrupt(); } int main(void) { - init(); - Ethernet.begin(mac, myIp); - delay(1000); // Wait for Ethernet to get configured. - gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet); - server.begin(); - if (gw.isLedMode()) { - // Add led timer interrupt - MsTimer2::set(300, ledTimersInterrupt); - MsTimer2::start(); - } - while (1) { - processEthernetMessages(); - gw.processRadioMessage(); - } + init(); + Ethernet.begin(TCP_MAC, TCP_IP); + delay(1000); // Wait for Ethernet to get configured. + gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet); + server.begin(); +// Add led timer interrupt + if (gw.isLedMode()) { + MsTimer2::set(200, ledTimersInterrupt); + MsTimer2::start(); + } + while (1) { + processEthernetMessages(); + gw.processRadioMessage(); + } } + + diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp index 7d8c9a3..2e1d49e 100755 --- a/libraries/MySensors/MyMQTT.cpp +++ b/libraries/MySensors/MyMQTT.cpp @@ -12,44 +12,45 @@ version 2 as published by the Free Software Foundation. #include "MyMQTT.h" char broker[] PROGMEM = MQTT_BROKER_PREFIX; -char S_0[] PROGMEM = "Temperature"; -char S_1[] PROGMEM = "Humidity"; -char S_2[] PROGMEM = "Light"; -char S_3[] PROGMEM = "Dimmer"; -char S_4[] PROGMEM = "Pressure"; -char S_5[] PROGMEM = "Forecast"; -char S_6[] PROGMEM = "Rain"; -char S_7[] PROGMEM = "Rainrate"; -char S_8[] PROGMEM = "Wind"; -char S_9[] PROGMEM = "Gust"; -char S_10[] PROGMEM = "Direction"; -char S_11[] PROGMEM = "UV"; -char S_12[] PROGMEM = "Weight"; -char S_13[] PROGMEM = "Distance"; -char S_14[] PROGMEM = "Impedance"; -char S_15[] PROGMEM = "Armed"; -char S_16[] PROGMEM = "Tripped"; -char S_17[] PROGMEM = "Watt"; -char S_18[] PROGMEM = "KWH"; -char S_19[] PROGMEM = "Scene_ON"; -char S_20[] PROGMEM = "Scene_OFF"; -char S_21[] PROGMEM = "Heater"; -char S_22[] PROGMEM = "Heater_SW"; -char S_23[] PROGMEM = "LightLVL"; -char S_24[] PROGMEM = "Var1"; -char S_25[] PROGMEM = "Var2"; -char S_26[] PROGMEM = "Var3"; -char S_27[] PROGMEM = "Var4"; -char S_28[] PROGMEM = "Var5,"; -char S_29[] PROGMEM = "Up"; -char S_30[] PROGMEM = "Down"; -char S_31[] PROGMEM = "Stop"; -char S_32[] PROGMEM = "IR_Send"; -char S_33[] PROGMEM = "IR_Receive"; -char S_34[] PROGMEM = "Flow"; -char S_35[] PROGMEM = "Volume"; -char S_36[] PROGMEM = "Lock_Status"; -char S_37[] PROGMEM = "Volt_Batt"; // Does not exist in mysensors lib! + +char S_0[] PROGMEM = "Temperature"; //V_TEMP +char S_1[] PROGMEM = "Humidity"; //V_HUM +char S_2[] PROGMEM = "Light"; //V_LIGHT +char S_3[] PROGMEM = "Dimmer"; //V_DIMMER +char S_4[] PROGMEM = "Pressure"; //V_PRESSURE +char S_5[] PROGMEM = "Forecast"; //V_FORECAST +char S_6[] PROGMEM = "Rain"; //V_RAIN +char S_7[] PROGMEM = "Rainrate"; //V_RAINRATE +char S_8[] PROGMEM = "Wind"; //V_WIND +char S_9[] PROGMEM = "Gust"; //V_GUST +char S_10[] PROGMEM = "Direction"; //V_DIRECTION +char S_11[] PROGMEM = "UV"; //V_UV +char S_12[] PROGMEM = "Weight"; //V_WEIGHT +char S_13[] PROGMEM = "Distance"; //V_DISTANCE +char S_14[] PROGMEM = "Impedance"; //V_IMPEDANCE +char S_15[] PROGMEM = "Armed"; //V_ARMED +char S_16[] PROGMEM = "Tripped"; //V_TRIPPED +char S_17[] PROGMEM = "Watt"; //V_WATT +char S_18[] PROGMEM = "KWH"; //V_KWH +char S_19[] PROGMEM = "Scene_ON"; //V_SCENE_ON +char S_20[] PROGMEM = "Scene_OFF"; //V_SCENE_OFF +char S_21[] PROGMEM = "Heater"; //V_HEATER +char S_22[] PROGMEM = "Heater_SW"; //V_HEATER_SW +char S_23[] PROGMEM = "LightLevel"; //V_LIGHT_LEVEL +char S_24[] PROGMEM = "Var1"; //V_VAR1 +char S_25[] PROGMEM = "Var2"; //V_VAR2 +char S_26[] PROGMEM = "Var3"; //V_VAR3 +char S_27[] PROGMEM = "Var4"; //V_VAR4 +char S_28[] PROGMEM = "Var5,"; //V_VAR5 +char S_29[] PROGMEM = "Up"; //V_UP +char S_30[] PROGMEM = "Down"; //V_DOWN +char S_31[] PROGMEM = "Stop"; //V_STOP +char S_32[] PROGMEM = "IR_Send"; //V_IR_SEND +char S_33[] PROGMEM = "IR_Receive"; //V_IR_RECEIVE +char S_34[] PROGMEM = "Flow"; //V_FLOW +char S_35[] PROGMEM = "Volume"; //V_VOLUME +char S_36[] PROGMEM = "Lock_Status";//V_LOCK_STATUS +char S_37[] PROGMEM = "DustLevel"; //V_DUST_LEVEL char S_38[] PROGMEM = ""; char S_39[] PROGMEM = ""; char S_40[] PROGMEM = ""; @@ -57,23 +58,24 @@ char S_41[] PROGMEM = ""; char S_42[] PROGMEM = ""; char S_43[] PROGMEM = ""; char S_44[] PROGMEM = ""; -char S_45[] PROGMEM = ""; +char S_45[] PROGMEM = "Volt_Batt"; // Does not exist in MyMessage.h! char S_46[] PROGMEM = ""; char S_47[] PROGMEM = ""; char S_48[] PROGMEM = ""; -char S_49[] PROGMEM = "Sketch_name"; // Does not exist in mysensors lib! -char S_50[] PROGMEM = "Sketch_version"; // Does not exist in mysensors lib! +char S_49[] PROGMEM = "Sketch_name"; // Does not exist in MyMessage.h! +char S_50[] PROGMEM = "Sketch_version"; // Does not exist in MyMessage.h! -PROGMEM const char *sType[] = -{ S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, +PROGMEM const char *sType[] = { + S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, S_11, S_12, S_13, S_14, S_15, S_16, S_17, S_18, S_19, S_20, - S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, + S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, S_31, S_32, S_33, S_34, S_35, S_36, S_37, S_38, S_39, S_40, - S_41, S_42, S_43, S_44, S_45, S_46, S_47, S_48, S_49, S_50}; + S_41, S_42, S_43, S_44, S_45, S_46, S_47, S_48, S_49, S_50 +}; -MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin, uint8_t _rx, uint8_t _tx, uint8_t _er) : +MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin, uint8_t _rx, uint8_t _tx, uint8_t _er) : MySensor(_cepin, _cspin) { if (_rx != NULL) { pinRx = _rx; @@ -83,7 +85,7 @@ MySensor(_cepin, _cspin) { if (_tx != NULL) { pinTx = _tx; pinMode(pinTx, OUTPUT); - ledMode = true; + ledMode = true; } if (_er != NULL) { pinEr = _er; @@ -118,7 +120,7 @@ void MyMQTT::processRadioMessage() { // A new message was received from one of the sensors MyMessage message = getLastMessage(); // Pass along the message from sensors to serial line - rxBlink(3); + rxBlink(1); SendMQTT(message); } @@ -130,7 +132,7 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { buffer[0]= 0; buffsize = 0; #ifdef TCPDUMP - Serial.print("<<"); + Serial.print("<<"); char buf[4]; for (int a=0; a 0) { #ifdef TCPDUMP - Serial.print(">>"); + Serial.print(">>"); char buf[4]; - for (int a=0; a> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { + if ((byte)inputString[0] >> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { buffer[0]= 0; buffsize = 0; if ((byte)inputString[0] >> 4 == MQTTSUBSCRIBE) { @@ -179,38 +181,32 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { strncat(buffer,inputString+4,inputString[3]); } msg.sender = GATEWAY_ADDRESS; - for (str = strtok_r(buffer, "/", &p); // split using semicolon - str && i < 4; str = strtok_r(NULL, "/", &p) //get subsequent tokens (?) - ) { - switch (i) { - case 0: // Radioid (destination) + for (str = strtok_r(buffer,"/",&p) ; str && i<4 ; str = strtok_r(NULL,"/",&p)) { //split using semicolon + // TODO: Check if we should send ack or not. + if (i == 0) { if (strcmp_P(str,broker)!=0) { return; //Message not for us or malformatted! } - break; - case 1: + } else if (i==1) { msg.destination = atoi(str); - break; - case 2: + } else if (i==2) { msg.sensor = atoi(str); - break; - case 3: + } else if (i==3) { char match=0; for (int j=0; strcpy_P(convBuf, (char*)pgm_read_word(&(sType[j]))) ; j++) { - if (strcmp(str,convBuf)==0) { - match=j; + if (strcmp(str,convBuf)==0) { + match=j; break; } - } + } msg.type = match; - break; } i++; } if ((char)inputString[1] > (char)(inputString[3]+2) && !((byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { strcpy(convBuf,inputString+(inputString[3]+4)); msg.set(convBuf); - } else { + } else { msg.set(""); } msg.sender = GATEWAY_ADDRESS; @@ -225,13 +221,13 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { void MyMQTT::SendMQTT(MyMessage &msg) { -//serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); + //serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); buffsize = 0; if (!MQTTClientConnected) return; if (msg.isAck()) { -Serial.println("msg is ack!"); + Serial.println("msg is ack!"); if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { - // TODO: sending ACK request on id_response fucks node up. doesn't work. + // TODO: sending ACK request on id_response fucks node up. doesn't work. // The idea was to confirm id and save to EEPROM_LATEST_NODE_ADDRESS. } } else { @@ -243,17 +239,17 @@ Serial.println("msg is ack!"); saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); } if (mGetCommand(msg)==C_INTERNAL && msg.type==I_CONFIG) { // CONFIG - // As for now there is only one 'config' request. + // As for now there is only one 'config' request. // We force SI! Resistance is futile! // - // Todo : Support for more config types, Maybe just read from own EEPROM? - // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to + // Todo : Support for more config types, Maybe just read from own EEPROM? + // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to // EEPROM_CONTROLLER_CONFIG_ADDRESS & EEPROM_LOCAL_CONFIG_ADDRESS msg.destination = msg.sender; //NodeID msg.sender = GATEWAY_ADDRESS; msg.sensor = 255; //SensorID msg.type = I_CONFIG; //msgType - mSetCommand(msg,C_INTERNAL); //Subtype + mSetCommand(msg,C_INTERNAL);//Subtype mSetRequestAck(msg,false); mSetAck(msg,false); msg.set("M"); @@ -280,7 +276,7 @@ Serial.println("msg is ack!"); // if (sendRoute(msg)) saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); // If send OK save to eeprom. DOES NOT ALWAYS RETURN 'OK'!? } else if (mGetCommand(msg)!=0) { if (mGetCommand(msg)==3) msg.type=msg.type+38; - buffer[buffsize++] = MQTTPUBLISH << 4; // 0: + buffer[buffsize++] = MQTTPUBLISH << 4; // 0: buffer[buffsize++] = 0x09; // 1: Remaining length with no payload, we'll set this later to correct value, buffsize -2 buffer[buffsize++] = 0x00; // 2: Length MSB (Remaing length can never exceed ff,so MSB must be 0!) buffer[buffsize++] = 0x08; // 3: Length LSB (ADDR), We'll set this later @@ -301,11 +297,11 @@ Serial.println("msg is ack!"); #ifdef TCPDUMP Serial.print(">>"); char buf[4]; - for (int a=0; a Date: Sun, 17 Aug 2014 21:39:18 +0200 Subject: [PATCH 302/326] Serial protocol changed to allowing controller to know if an incoming message is a ack. Outgoing and incoming serial messages now also have the same parameter count. radioId;childId;messageType;ack;subType;payload\n Where ack parameter means the following: outgoing: 0 = unacknoledged message, 1 = request ack from destination node Incoming: 0= normal message, 1=This is an ack message --- libraries/MySensors/MyGateway.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index 114a8da..b39e0a8 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -74,7 +74,7 @@ void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e da RF24::startListening(); // Send startup log message on serial - serial(PSTR("0;0;%d;%d;Arduino startup complete.\n"), C_INTERNAL, I_LOG_MESSAGE); + serial(PSTR("0;0;%d;0;%d;Arduino startup complete.\n"), C_INTERNAL, I_LOG_MESSAGE); } @@ -92,7 +92,7 @@ void MyGateway::checkButtonTriggeredInclusion() { if (buttonTriggeredInclusion) { // Ok, someone pressed the inclusion button on the gateway // start inclusion mode for 1 munute. - serial(PSTR("0;0;%d;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE); + serial(PSTR("0;0;%d;0;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE); buttonTriggeredInclusion = false; setInclusionMode(true); } @@ -171,7 +171,7 @@ void MyGateway::parseAndSend(char *commandBuffer) { // Handle messages directed to gateway if (type == I_VERSION) { // Request for version - serial(PSTR("0;0;%d;%d;%s\n"),C_INTERNAL, I_VERSION, LIBRARY_VERSION); + serial(PSTR("0;0;%d;0;%d;%s\n"),C_INTERNAL, I_VERSION, LIBRARY_VERSION); } else if (type == I_INCLUSION_MODE) { // Request to change inclusion mode setInclusionMode(atoi(value) == 1); @@ -202,7 +202,7 @@ void MyGateway::setInclusionMode(boolean newMode) { if (newMode != inclusionMode) inclusionMode = newMode; // Send back mode change on serial line to ack command - serial(PSTR("0;0;%d;%d;%d\n"), C_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0); + serial(PSTR("0;0;%d;0;%d;%d\n"), C_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0); if (inclusionMode) { inclusionStartTime = millis(); @@ -239,7 +239,7 @@ void MyGateway::serial(const char *fmt, ... ) { } void MyGateway::serial(MyMessage &msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); + serial(PSTR("%d;%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), mGetAck(msg), msg.type, msg.getString(convBuf)); } From 2ff7c74e990ea16b337f9828c15b1908d0fb2990 Mon Sep 17 00:00:00 2001 From: Daniel Wiegert Date: Sun, 17 Aug 2014 22:04:42 +0200 Subject: [PATCH 303/326] moved comment and minor ops --- MQTTGateway/MQTTGateway.ino | 50 +++++++++++++++++----------------- libraries/MySensors/MyMQTT.cpp | 42 ++++++++++++++-------------- libraries/MySensors/MyMQTT.h | 9 ++---- 3 files changed, 49 insertions(+), 52 deletions(-) diff --git a/MQTTGateway/MQTTGateway.ino b/MQTTGateway/MQTTGateway.ino index 467ac44..54da02d 100755 --- a/MQTTGateway/MQTTGateway.ino +++ b/MQTTGateway/MQTTGateway.ino @@ -1,20 +1,20 @@ /* MyMQTT Broker Gateway 0.1b - Created by Daniel Wiegert - Based on MySensors Ethernet Gateway by Henrik Ekblad - http://www.mysensors.org +Created by Daniel Wiegert +Based on MySensors Ethernet Gateway by Henrik Ekblad +http://www.mysensors.org - Requires MySensors lib 1.4b +Requires MySensors lib 1.4b - * Change below; TCP_IP, TCP_PORT, TCP_MAC - This will listen on your selected TCP_IP:TCP_PORT below, Please change TCP_MAC your liking also. - *1 -> NOTE: Keep first byte at x2, x6, xA or xE (replace x with any hex value) for using Local Ranges. +* Change below; TCP_IP, TCP_PORT, TCP_MAC +This will listen on your selected TCP_IP:TCP_PORT below, Please change TCP_MAC your liking also. +*1 -> NOTE: Keep first byte at x2, x6, xA or xE (replace x with any hex value) for using Local Ranges. - *2 You can use standard pin set-up as MySensors recommends or if you own a IBOARD you may change - the radio-pins below if you hardware mod your iBoard. see [URL BELOW] for more details. - http://forum.mysensors.org/topic/224/iboard-cheap-single-board-ethernet-arduino-with-radio/5 +*2 You can use standard pin set-up as MySensors recommends or if you own a IBOARD you may change + the radio-pins below if you hardware mod your iBoard. see [URL BELOW] for more details. + http://forum.mysensors.org/topic/224/iboard-cheap-single-board-ethernet-arduino-with-radio/5 - * Don't forget to look at the definitions in libraries\MySensors\MyMQTT.h! +* Don't forget to look at the definitions in libraries\MySensors\MyMQTT.h! define TCPDUMP and connect serial interface if you have problems, please write on http://forum.mysensors.org/ and explain your problem, include serial output. Don't forget to @@ -29,8 +29,9 @@ MQTT_SEND_SUBSCRIPTION is if you want the MyMQTT to send a empty payload message to your nodes. This can be useful if you want to send latest state back to the MQTT client. Just check if incoming message has any length or not. + Example: if (msg.type==V_LIGHT && strlen(msg.getString())>0) otherwise the code might do strange things. - * Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/[SensorType] +* Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/[SensorType] NodeID and SensorID is uint8 (0-255) number. Last segment is translation of the sensor type, look inside MyMQTT.cpp for the definitions. User can change this to their needs. We have also left some space for custom types. @@ -39,18 +40,18 @@ Special: (sensor 255 reserved for special commands) You can receive a node sketch name with MyMQTT/20/255/Sketch_name (or version with _version) To-do: - Special commands : clear or set EEPROM Values, Send REBOOT and Receive reboot for MyMQTT itself. - Be able to send ACK so client returns the data being sent. - ... Please come with ideas! - - Test in more MQTT clients, So far tested in openhab and MyMQTT for Android (Not my creation) - - http://www.openhab.org/ - - https://play.google.com/store/apps/details?id=at.tripwire.mqtt.client&hl=en - ... Please notify me if you use this broker with other software. +Special commands : clear or set EEPROM Values, Send REBOOT and Receive reboot for MyMQTT itself. +Be able to send ACK so client returns the data being sent. +... Please come with ideas! + +Test in more MQTT clients, So far tested in openhab and MyMQTT for Android (Not my creation) +- http://www.openhab.org/ +- https://play.google.com/store/apps/details?id=at.tripwire.mqtt.client&hl=en +... Please notify me if you use this broker with other software. - * Example Openhab configuration: -openhab.cfg +* Example Openhab configuration: +openhab.cfg --- mqtt:mysensor.url=tcp://192.168.0.234:1883 mqtt:mysensor.clientId=MQTT @@ -75,7 +76,7 @@ sitemap/test.site --- sitemap demo label="Menu" Frame label="Openhab" { - Group item=test label="Test group" + Group item=test label="Test group" } --- @@ -148,7 +149,7 @@ int main(void) { delay(1000); // Wait for Ethernet to get configured. gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet); server.begin(); -// Add led timer interrupt + // Add led timer interrupt if (gw.isLedMode()) { MsTimer2::set(200, ledTimersInterrupt); MsTimer2::start(); @@ -159,4 +160,3 @@ int main(void) { } } - diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp index 2e1d49e..d2a0352 100755 --- a/libraries/MySensors/MyMQTT.cpp +++ b/libraries/MySensors/MyMQTT.cpp @@ -67,15 +67,15 @@ char S_50[] PROGMEM = "Sketch_version"; // Does not exist in MyMessage.h! PROGMEM const char *sType[] = { - S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, + S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, S_11, S_12, S_13, S_14, S_15, S_16, S_17, S_18, S_19, S_20, - S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, + S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, S_31, S_32, S_33, S_34, S_35, S_36, S_37, S_38, S_39, S_40, S_41, S_42, S_43, S_44, S_45, S_46, S_47, S_48, S_49, S_50 }; -MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin, uint8_t _rx, uint8_t _tx, uint8_t _er) : +MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin, uint8_t _rx, uint8_t _tx, uint8_t _er) : MySensor(_cepin, _cspin) { if (_rx != NULL) { pinRx = _rx; @@ -85,7 +85,7 @@ MySensor(_cepin, _cspin) { if (_tx != NULL) { pinTx = _tx; pinMode(pinTx, OUTPUT); - ledMode = true; + ledMode = true; } if (_er != NULL) { pinEr = _er; @@ -132,7 +132,7 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { buffer[0]= 0; buffsize = 0; #ifdef TCPDUMP - Serial.print("<<"); + Serial.print("<<"); char buf[4]; for (int a=0; a 0) { #ifdef TCPDUMP - Serial.print(">>"); + Serial.print(">>"); char buf[4]; - for (int a=0; a> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { + if ((byte)inputString[0] >> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { buffer[0]= 0; buffsize = 0; if ((byte)inputString[0] >> 4 == MQTTSUBSCRIBE) { @@ -194,11 +194,11 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { } else if (i==3) { char match=0; for (int j=0; strcpy_P(convBuf, (char*)pgm_read_word(&(sType[j]))) ; j++) { - if (strcmp(str,convBuf)==0) { - match=j; + if (strcmp(str,convBuf)==0) { + match=j; break; } - } + } msg.type = match; } i++; @@ -206,7 +206,7 @@ void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { if ((char)inputString[1] > (char)(inputString[3]+2) && !((byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { strcpy(convBuf,inputString+(inputString[3]+4)); msg.set(convBuf); - } else { + } else { msg.set(""); } msg.sender = GATEWAY_ADDRESS; @@ -227,7 +227,7 @@ void MyMQTT::SendMQTT(MyMessage &msg) { if (msg.isAck()) { Serial.println("msg is ack!"); if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { - // TODO: sending ACK request on id_response fucks node up. doesn't work. + // TODO: sending ACK request on id_response fucks node up. doesn't work. // The idea was to confirm id and save to EEPROM_LATEST_NODE_ADDRESS. } } else { @@ -239,11 +239,11 @@ void MyMQTT::SendMQTT(MyMessage &msg) { saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); } if (mGetCommand(msg)==C_INTERNAL && msg.type==I_CONFIG) { // CONFIG - // As for now there is only one 'config' request. + // As for now there is only one 'config' request. // We force SI! Resistance is futile! // - // Todo : Support for more config types, Maybe just read from own EEPROM? - // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to + // Todo : Support for more config types, Maybe just read from own EEPROM? + // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to // EEPROM_CONTROLLER_CONFIG_ADDRESS & EEPROM_LOCAL_CONFIG_ADDRESS msg.destination = msg.sender; //NodeID msg.sender = GATEWAY_ADDRESS; @@ -273,10 +273,10 @@ void MyMQTT::SendMQTT(MyMessage &msg) { mSetVersion(msg, PROTOCOL_VERSION); txBlink(1); if (!sendRoute(msg)) errBlink(1); -// if (sendRoute(msg)) saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); // If send OK save to eeprom. DOES NOT ALWAYS RETURN 'OK'!? + // if (sendRoute(msg)) saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); // If send OK save to eeprom. DOES NOT ALWAYS RETURN 'OK'!? } else if (mGetCommand(msg)!=0) { if (mGetCommand(msg)==3) msg.type=msg.type+38; - buffer[buffsize++] = MQTTPUBLISH << 4; // 0: + buffer[buffsize++] = MQTTPUBLISH << 4; // 0: buffer[buffsize++] = 0x09; // 1: Remaining length with no payload, we'll set this later to correct value, buffsize -2 buffer[buffsize++] = 0x00; // 2: Length MSB (Remaing length can never exceed ff,so MSB must be 0!) buffer[buffsize++] = 0x08; // 3: Length LSB (ADDR), We'll set this later @@ -297,11 +297,11 @@ void MyMQTT::SendMQTT(MyMessage &msg) { #ifdef TCPDUMP Serial.print(">>"); char buf[4]; - for (int a=0; a Date: Sun, 17 Aug 2014 22:27:08 +0200 Subject: [PATCH 304/326] Add emon lib for a later non invasive current sensor. --- libraries/EmonLib/EmonLib.cpp | 259 ++++++++++++++++++ libraries/EmonLib/EmonLib.h | 89 ++++++ libraries/EmonLib/Readme.txt | 25 ++ .../examples/current_only/current_only.ino | 20 ++ .../voltage_and_current.ino | 24 ++ 5 files changed, 417 insertions(+) create mode 100644 libraries/EmonLib/EmonLib.cpp create mode 100644 libraries/EmonLib/EmonLib.h create mode 100644 libraries/EmonLib/Readme.txt create mode 100644 libraries/EmonLib/examples/current_only/current_only.ino create mode 100644 libraries/EmonLib/examples/voltage_and_current/voltage_and_current.ino diff --git a/libraries/EmonLib/EmonLib.cpp b/libraries/EmonLib/EmonLib.cpp new file mode 100644 index 0000000..1a1fd29 --- /dev/null +++ b/libraries/EmonLib/EmonLib.cpp @@ -0,0 +1,259 @@ +/* + Emon.cpp - Library for openenergymonitor + Created by Trystan Lea, April 27 2010 + GNU GPL + modified to use up to 12 bits ADC resolution (ex. Arduino Due) + by boredman@boredomprojects.net 26.12.2013 +*/ + +//#include "WProgram.h" un-comment for use on older versions of Arduino IDE +#include "EmonLib.h" + +#if defined(ARDUINO) && ARDUINO >= 100 + +#include "Arduino.h" + +#else + +#include "WProgram.h" + +#endif + +//-------------------------------------------------------------------------------------- +// Sets the pins to be used for voltage and current sensors +//-------------------------------------------------------------------------------------- +void EnergyMonitor::voltage(int _inPinV, double _VCAL, double _PHASECAL) +{ + inPinV = _inPinV; + VCAL = _VCAL; + PHASECAL = _PHASECAL; +} + +void EnergyMonitor::current(int _inPinI, double _ICAL) +{ + inPinI = _inPinI; + ICAL = _ICAL; +} + +//-------------------------------------------------------------------------------------- +// Sets the pins to be used for voltage and current sensors based on emontx pin map +//-------------------------------------------------------------------------------------- +void EnergyMonitor::voltageTX(double _VCAL, double _PHASECAL) +{ + inPinV = 2; + VCAL = _VCAL; + PHASECAL = _PHASECAL; +} + +void EnergyMonitor::currentTX(int _channel, double _ICAL) +{ + if (_channel == 1) inPinI = 3; + if (_channel == 2) inPinI = 0; + if (_channel == 3) inPinI = 1; + ICAL = _ICAL; +} + +//-------------------------------------------------------------------------------------- +// emon_calc procedure +// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kwh increment +// From a sample window of the mains AC voltage and current. +// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure. +//-------------------------------------------------------------------------------------- +void EnergyMonitor::calcVI(int crossings, int timeout) +{ + #if defined emonTxV3 + int SUPPLYVOLTAGE=3300; + #else + int SUPPLYVOLTAGE = readVcc(); + #endif + + int crossCount = 0; //Used to measure number of times threshold is crossed. + int numberOfSamples = 0; //This is now incremented + + //------------------------------------------------------------------------------------------------------------------------- + // 1) Waits for the waveform to be close to 'zero' (500 adc) part in sin curve. + //------------------------------------------------------------------------------------------------------------------------- + boolean st=false; //an indicator to exit the while loop + + unsigned long start = millis(); //millis()-start makes sure it doesnt get stuck in the loop if there is an error. + + while(st==false) //the while loop... + { + startV = analogRead(inPinV); //using the voltage waveform + if ((startV < (ADC_COUNTS/2+50)) && (startV > (ADC_COUNTS/2-50))) st=true; //check its within range + if ((millis()-start)>timeout) st = true; + } + + //------------------------------------------------------------------------------------------------------------------------- + // 2) Main measurment loop + //------------------------------------------------------------------------------------------------------------------------- + start = millis(); + + while ((crossCount < crossings) && ((millis()-start) startV) checkVCross = true; + else checkVCross = false; + if (numberOfSamples==1) lastVCross = checkVCross; + + if (lastVCross != checkVCross) crossCount++; + } + + //------------------------------------------------------------------------------------------------------------------------- + // 3) Post loop calculations + //------------------------------------------------------------------------------------------------------------------------- + //Calculation of the root of the mean of the voltage and current squared (rms) + //Calibration coeficients applied. + + double V_RATIO = VCAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS)); + Vrms = V_RATIO * sqrt(sumV / numberOfSamples); + + double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS)); + Irms = I_RATIO * sqrt(sumI / numberOfSamples); + + //Calculation power values + realPower = V_RATIO * I_RATIO * sumP / numberOfSamples; + apparentPower = Vrms * Irms; + powerFactor=realPower / apparentPower; + + //Reset accumulators + sumV = 0; + sumI = 0; + sumP = 0; +//-------------------------------------------------------------------------------------- +} + +//-------------------------------------------------------------------------------------- +double EnergyMonitor::calcIrms(int NUMBER_OF_SAMPLES) +{ + + #if defined emonTxV3 + int SUPPLYVOLTAGE=3300; + #else + int SUPPLYVOLTAGE = readVcc(); + #endif + + + for (int n = 0; n < NUMBER_OF_SAMPLES; n++) + { + lastSampleI = sampleI; + sampleI = analogRead(inPinI); + lastFilteredI = filteredI; + filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI); + + // Root-mean-square method current + // 1) square current values + sqI = filteredI * filteredI; + // 2) sum + sumI += sqI; + } + + double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS)); + Irms = I_RATIO * sqrt(sumI / NUMBER_OF_SAMPLES); + + //Reset accumulators + sumI = 0; +//-------------------------------------------------------------------------------------- + + return Irms; +} + +void EnergyMonitor::serialprint() +{ + Serial.print(realPower); + Serial.print(' '); + Serial.print(apparentPower); + Serial.print(' '); + Serial.print(Vrms); + Serial.print(' '); + Serial.print(Irms); + Serial.print(' '); + Serial.print(powerFactor); + Serial.println(' '); + delay(100); +} + +//thanks to http://hacking.majenko.co.uk/making-accurate-adc-readings-on-arduino +//and Jérôme who alerted us to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/ + +long EnergyMonitor::readVcc() { + long result; + + //not used on emonTx V3 - as Vcc is always 3.3V - eliminates bandgap error and need for calibration http://harizanov.com/2013/09/thoughts-on-avr-adc-accuracy/ + + #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined (__AVR_ATmega328P__) + ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); + #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB1286__) + ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); + ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560 http://openenergymonitor.org/emon/node/2253#comment-11432 + #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) + ADMUX = _BV(MUX5) | _BV(MUX0); + #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + ADMUX = _BV(MUX3) | _BV(MUX2); + + #endif + + + #if defined(__AVR__) + delay(2); // Wait for Vref to settle + ADCSRA |= _BV(ADSC); // Convert + while (bit_is_set(ADCSRA,ADSC)); + result = ADCL; + result |= ADCH<<8; + result = 1126400L / result; //1100mV*1024 ADC steps http://openenergymonitor.org/emon/node/1186 + return result; + #elif defined(__arm__) + return (3300); //Arduino Due + #else + return (3300); //Guess that other un-supported architectures will be running a 3.3V! + #endif +} + diff --git a/libraries/EmonLib/EmonLib.h b/libraries/EmonLib/EmonLib.h new file mode 100644 index 0000000..41cafcb --- /dev/null +++ b/libraries/EmonLib/EmonLib.h @@ -0,0 +1,89 @@ +/* + Emon.h - Library for openenergymonitor + Created by Trystan Lea, April 27 2010 + GNU GPL + modified to use up to 12 bits ADC resolution (ex. Arduino Due) + by boredman@boredomprojects.net 26.12.2013 +*/ + +#ifndef EmonLib_h +#define EmonLib_h + +#if defined(ARDUINO) && ARDUINO >= 100 + +#include "Arduino.h" + +#else + +#include "WProgram.h" + +#endif + +// to enable 12-bit ADC resolution on Arduino Due, +// include the following line in main sketch inside setup() function: +// analogReadResolution(ADC_BITS); +// otherwise will default to 10 bits, as in regular Arduino-based boards. +#if defined(__arm__) +#define ADC_BITS 12 +#else +#define ADC_BITS 10 +#endif + +#define ADC_COUNTS (1< Date: Sun, 17 Aug 2014 23:56:53 +0200 Subject: [PATCH 305/326] Added a new RFID Lock Sensor example. --- .../RFIDLockSensor/RFIDLockSensor.ino | 161 ++++++++++++++++++ .../{iso14443a_uid.pde => iso14443a_uid.ino} | 0 ...tndef.pde => mifareclassic_formatndef.ino} | 0 ..._memdump.pde => mifareclassic_memdump.ino} | 0 ...ic.pde => mifareclassic_ndeftoclassic.ino} | 0 ...endef.pde => mifareclassic_updatendef.ino} | 0 .../{readMifare.pde => readMifare.ino} | 0 7 files changed, 161 insertions(+) create mode 100644 libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino rename libraries/PN532/examples/iso14443a_uid/{iso14443a_uid.pde => iso14443a_uid.ino} (100%) rename libraries/PN532/examples/mifareclassic_formatndef/{mifareclassic_formatndef.pde => mifareclassic_formatndef.ino} (100%) rename libraries/PN532/examples/mifareclassic_memdump/{mifareclassic_memdump.pde => mifareclassic_memdump.ino} (100%) rename libraries/PN532/examples/mifareclassic_ndeftoclassic/{mifareclassic_ndeftoclassic.pde => mifareclassic_ndeftoclassic.ino} (100%) rename libraries/PN532/examples/mifareclassic_updatendef/{mifareclassic_updatendef.pde => mifareclassic_updatendef.ino} (100%) rename libraries/PN532/examples/readMifare/{readMifare.pde => readMifare.ino} (100%) diff --git a/libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino b/libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino new file mode 100644 index 0000000..96f7e25 --- /dev/null +++ b/libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino @@ -0,0 +1,161 @@ +/* + RFID Lock sensor/actuator + + Henrik Ekblad + + Use RFID tag to lock/unlock a door or trigger a scene on your controller. + This example sketch allows you to add an optional relay or solenoid + which can be activated/opened by RFID or controller. + + Use the I2C wiring option for your RFID module and connect to the following Arduino pins. + + RFID Arduino + ----- ------- + GND -> GND + VCC -> +5V + SCL -> A5 + SDA -> A4 + + Use normal wiring for NRF24L01 radio + + Attach a optional relay or solonoid lock to pin 4 + +*/ + +#include +#include +#include +#include +#include + + +// Add your valid rfid keys here. To find you your key just run sketch; hold your new RFID tag in fron ot the reader; +// and copy the key from serial output of this sketch. +const uint8_t maxKeyLength = 7; +uint8_t validKeys[][maxKeyLength] = { + { 0xB3, 0xC6, 0xD9, 0x80, 0x00, 0x00, 0x00 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }}; +int keyCount = sizeof validKeys / maxKeyLength; + + +#define CHILD_ID 99 // Id of the sensor child + +/*Pin definitions*/ +const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock. + +bool lockStatus; +MySensor gw; +MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS); +PN532_I2C pn532i2c(Wire); +PN532 nfc(pn532i2c); + +void setup() { + + pinMode(lockPin, OUTPUT); + + nfc.begin(); + uint32_t versiondata = nfc.getFirmwareVersion(); + if (! versiondata) { + Serial.print("Couldn't find PN53x board"); + while (1); // halt + } + Serial.print("Found NFC chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); + Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); + Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); + // Set the max number of retry attempts to read from a card + // This prevents us from waiting forever for a card, which is + // the default behaviour of the PN532. + nfc.setPassiveActivationRetries(0x3); + + // configure board to read RFID tags + nfc.SAMConfig(); + + // Init mysensors library + gw.begin(incomingMessage); + + gw.sendSketchInfo("RFID Lock", "1.0"); + gw.present(CHILD_ID, S_LOCK); + + lockStatus = gw.loadState(0); // Read last lock status from eeprom + setLockState(lockStatus, true); // Now set the last known state and send it to controller +} + +void loop() { + boolean success; + uint8_t key[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID + uint8_t currentKeyLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) + + gw.process(); // Process incomming messages + + // Wait for an ISO14443A type cards (Mifare, etc.). When one is found + // 'uid' will be populated with the UID, and uidLength will indicate + // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) + success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &key[0], ¤tKeyLength); + + if (success) { + Serial.print("Found tag id: "); + for (uint8_t i=0; i < currentKeyLength; i++) + { + if (i>0) Serial.print(","); + Serial.print("0x");Serial.print(key[i], HEX); + } + for (uint8_t i=currentKeyLength; i < maxKeyLength; i++) + { + Serial.print(",0x00"); + } + + + Serial.println(""); + + boolean valid = false; + // Compare this key to the valid once registered here in sketch + for (int i=0;i Date: Mon, 18 Aug 2014 15:46:47 +0200 Subject: [PATCH 306/326] Add scene controller device --- libraries/MySensors/MyMessage.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index dddfcbe..729dacd 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -54,7 +54,8 @@ typedef enum { typedef enum { S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, 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_ARDUINO_REPEATER_NODE, S_LOCK, S_IR, S_WATER, S_AIR_QUALITY, S_CUSTOM, S_DUST, + S_SCENE_CONTROLLER } sensor; // Type of data stream (for streamed message) From 4c1ce4d45a9c36018525277ea4e27c1cd723ee04 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 18 Aug 2014 19:45:51 +0200 Subject: [PATCH 307/326] Fix:Debug messages now uses the new serial formal. --- libraries/MySensors/MySensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index b43a46a..55b15ee 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -517,7 +517,7 @@ void MySensor::debugPrint(const char *fmt, ... ) { char fmtBuffer[300]; if (isGateway) { // prepend debug message to be handled correctly by gw (C_INTERNAL, I_LOG_MESSAGE) - snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;%d;"), C_INTERNAL, I_LOG_MESSAGE); + snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE); Serial.print(fmtBuffer); } va_list args; From 9f3ec7f4f42a78d9d4b347b166f27da97bb4f77d Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 18 Aug 2014 21:28:48 +0200 Subject: [PATCH 308/326] clean up among libraries --- libraries/PN532_HSU/PN532_HSU.cpp | 190 -------------- libraries/PN532_HSU/PN532_HSU.h | 29 --- libraries/PN532_SPI/PN532_SPI.cpp | 209 --------------- libraries/PN532_SPI/PN532_SPI.h | 36 --- libraries/Sleep_n0m1/README | 73 ------ libraries/Sleep_n0m1/Sleep_n0m1.cpp | 243 ------------------ libraries/Sleep_n0m1/Sleep_n0m1.h | 109 -------- .../Sleep_n0m1_Interrupt.ino | 28 -- .../Sleep_n0m1_abortCycle.ino | 44 ---- .../Sleep_n0m1_countDown.ino | 39 --- .../Sleep_n0m1_simple/Sleep_n0m1_simple.ino | 31 --- 11 files changed, 1031 deletions(-) delete mode 100644 libraries/PN532_HSU/PN532_HSU.cpp delete mode 100644 libraries/PN532_HSU/PN532_HSU.h delete mode 100644 libraries/PN532_SPI/PN532_SPI.cpp delete mode 100644 libraries/PN532_SPI/PN532_SPI.h delete mode 100644 libraries/Sleep_n0m1/README delete mode 100644 libraries/Sleep_n0m1/Sleep_n0m1.cpp delete mode 100644 libraries/Sleep_n0m1/Sleep_n0m1.h delete mode 100644 libraries/Sleep_n0m1/examples/Sleep_n0m1_Interrupt/Sleep_n0m1_Interrupt.ino delete mode 100644 libraries/Sleep_n0m1/examples/Sleep_n0m1_abortCycle/Sleep_n0m1_abortCycle.ino delete mode 100644 libraries/Sleep_n0m1/examples/Sleep_n0m1_countDown/Sleep_n0m1_countDown.ino delete mode 100644 libraries/Sleep_n0m1/examples/Sleep_n0m1_simple/Sleep_n0m1_simple.ino diff --git a/libraries/PN532_HSU/PN532_HSU.cpp b/libraries/PN532_HSU/PN532_HSU.cpp deleted file mode 100644 index d0e67a2..0000000 --- a/libraries/PN532_HSU/PN532_HSU.cpp +++ /dev/null @@ -1,190 +0,0 @@ - -#include "PN532_HSU.h" -#include "PN532_debug.h" - - -PN532_HSU::PN532_HSU(HardwareSerial &serial) -{ - _serial = &serial; - command = 0; -} - -void PN532_HSU::begin() -{ - _serial->begin((long)115200); -} - -void PN532_HSU::wakeup() -{ - _serial->write((uint8_t)0x55); - _serial->write((uint8_t)0x55); - _serial->write((uint8_t)0x00); - _serial->write((uint8_t)0x00); - _serial->write((uint8_t)0x00); - - /** dump serial buffer */ - if(_serial->available()){ - DMSG("Dump serial buffer: "); - } - while(_serial->available()){ - uint8_t ret = _serial->read(); - DMSG_HEX(ret); - } - -} - -int8_t PN532_HSU::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) -{ - - /** dump serial buffer */ - if(_serial->available()){ - DMSG("Dump serial buffer: "); - } - while(_serial->available()){ - uint8_t ret = _serial->read(); - DMSG_HEX(ret); - } - - command = header[0]; - - _serial->write((uint8_t)PN532_PREAMBLE); - _serial->write((uint8_t)PN532_STARTCODE1); - _serial->write((uint8_t)PN532_STARTCODE2); - - uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA - _serial->write(length); - _serial->write(~length + 1); // checksum of length - - _serial->write((uint8_t)PN532_HOSTTOPN532); - uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA - - _serial->write(header, hlen); - for (uint8_t i = 0; i < hlen; i++) { - sum += header[i]; - } - - _serial->write(body, blen); - for (uint8_t i = 0; i < blen; i++) { - sum += body[i]; - } - - uint8_t checksum = ~sum + 1; // checksum of TFI + DATA - _serial->write(checksum); - _serial->write((uint8_t)PN532_POSTAMBLE); - - return readAckFrame(); -} - -int16_t PN532_HSU::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) -{ - uint8_t tmp[3]; - - DMSG("Read response\n"); - - /** Frame Preamble and Start Code */ - if(receive(tmp, 3, timeout)<=0){ - return PN532_TIMEOUT; - } - if(0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]){ - DMSG("Preamble error"); - return PN532_INVALID_FRAME; - } - - /** receive length and check */ - uint8_t length[2]; - if(receive(length, 2, timeout) <= 0){ - return PN532_TIMEOUT; - } - if( 0 != (uint8_t)(length[0] + length[1]) ){ - DMSG("Length error"); - return PN532_INVALID_FRAME; - } - length[0] -= 2; - if( length[0] > len){ - return PN532_NO_SPACE; - } - - /** receive command byte */ - uint8_t cmd = command + 1; // response command - if(receive(tmp, 2, timeout) <= 0){ - return PN532_TIMEOUT; - } - if( PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]){ - DMSG("Command error"); - return PN532_INVALID_FRAME; - } - - if(receive(buf, length[0], timeout) != length[0]){ - return PN532_TIMEOUT; - } - uint8_t sum = PN532_PN532TOHOST + cmd; - for(uint8_t i=0; i return value buffer. - len --> length expect to receive. - timeout --> time of reveiving - @retval number of received bytes, 0 means no data received. -*/ -int8_t PN532_HSU::receive(uint8_t *buf, int len, uint16_t timeout) -{ - int read_bytes = 0; - int ret; - unsigned long start_millis; - - while (read_bytes < len) { - start_millis = millis(); - do { - ret = _serial->read(); - if (ret >= 0) { - break; - } - } while( (millis()- start_millis ) < timeout); - - if (ret < 0) { - if(read_bytes){ - return read_bytes; - }else{ - return PN532_TIMEOUT; - } - } - buf[read_bytes] = (uint8_t)ret; - DMSG_HEX(ret); - read_bytes++; - } - return read_bytes; -} \ No newline at end of file diff --git a/libraries/PN532_HSU/PN532_HSU.h b/libraries/PN532_HSU/PN532_HSU.h deleted file mode 100644 index 54dddd3..0000000 --- a/libraries/PN532_HSU/PN532_HSU.h +++ /dev/null @@ -1,29 +0,0 @@ - -#ifndef __PN532_HSU_H__ -#define __PN532_HSU_H__ - -#include "PN532Interface.h" - -#define PN532_HSU_DEBUG - -#define PN532_HSU_READ_TIMEOUT (1000) - -class PN532_HSU : public PN532Interface { -public: - PN532_HSU(HardwareSerial &serial); - - void begin(); - void wakeup(); - virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); - int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); - -private: - HardwareSerial* _serial; - uint8_t command; - - int8_t readAckFrame(); - - int8_t receive(uint8_t *buf, int len, uint16_t timeout=PN532_HSU_READ_TIMEOUT); -}; - -#endif diff --git a/libraries/PN532_SPI/PN532_SPI.cpp b/libraries/PN532_SPI/PN532_SPI.cpp deleted file mode 100644 index 39adbf9..0000000 --- a/libraries/PN532_SPI/PN532_SPI.cpp +++ /dev/null @@ -1,209 +0,0 @@ - -#include "PN532_SPI.h" -#include "PN532_debug.h" - -#define STATUS_READ 2 -#define DATA_WRITE 1 -#define DATA_READ 3 - -PN532_SPI::PN532_SPI(SPIClass &spi, uint8_t ss) -{ - command = 0; - _spi = &spi; - _ss = ss; -} - -void PN532_SPI::begin() -{ - pinMode(_ss, OUTPUT); - - _spi->begin(); - _spi->setDataMode(SPI_MODE0); // PN532 only supports mode0 - _spi->setBitOrder(LSBFIRST); -#ifndef __SAM3X8E__ - _spi->setClockDivider(SPI_CLOCK_DIV8); // set clock 2MHz(max: 5MHz) -#else - /** DUE spi library does not support SPI_CLOCK_DIV8 macro */ - _spi->setClockDivider(42); // set clock 2MHz(max: 5MHz) -#endif - -} - -void PN532_SPI::wakeup() -{ - digitalWrite(_ss, LOW); - delay(2); - digitalWrite(_ss, HIGH); -} - - - -int8_t PN532_SPI::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) -{ - command = header[0]; - writeFrame(header, hlen, body, blen); - - uint8_t timeout = PN532_ACK_WAIT_TIME; - while (!isReady()) { - delay(1); - timeout--; - if (0 == timeout) { - DMSG("Time out when waiting for ACK\n"); - return -2; - } - } - if (readAckFrame()) { - DMSG("Invalid ACK\n"); - return PN532_INVALID_ACK; - } - return 0; -} - -int16_t PN532_SPI::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) -{ - uint16_t time = 0; - while (!isReady()) { - delay(1); - time++; - if (timeout > 0 && time > timeout) { - return PN532_TIMEOUT; - } - } - - digitalWrite(_ss, LOW); - delay(1); - - int16_t result; - do { - write(DATA_READ); - - if (0x00 != read() || // PREAMBLE - 0x00 != read() || // STARTCODE1 - 0xFF != read() // STARTCODE2 - ) { - - result = PN532_INVALID_FRAME; - break; - } - - uint8_t length = read(); - if (0 != (uint8_t)(length + read())) { // checksum of length - result = PN532_INVALID_FRAME; - break; - } - - uint8_t cmd = command + 1; // response command - if (PN532_PN532TOHOST != read() || (cmd) != read()) { - result = PN532_INVALID_FRAME; - break; - } - - DMSG("read: "); - DMSG_HEX(cmd); - - length -= 2; - if (length > len) { - for (uint8_t i = 0; i < length; i++) { - DMSG_HEX(read()); // dump message - } - DMSG("\nNot enough space\n"); - read(); - read(); - result = PN532_NO_SPACE; // not enough space - break; - } - - uint8_t sum = PN532_PN532TOHOST + cmd; - for (uint8_t i = 0; i < length; i++) { - buf[i] = read(); - sum += buf[i]; - - DMSG_HEX(buf[i]); - } - DMSG('\n'); - - uint8_t checksum = read(); - if (0 != (uint8_t)(sum + checksum)) { - DMSG("checksum is not ok\n"); - result = PN532_INVALID_FRAME; - break; - } - read(); // POSTAMBLE - - result = length; - } while (0); - - digitalWrite(_ss, HIGH); - - return result; -} - -boolean PN532_SPI::isReady() -{ - digitalWrite(_ss, LOW); - - write(STATUS_READ); - uint8_t status = read() & 1; - digitalWrite(_ss, HIGH); - return status; -} - -void PN532_SPI::writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) -{ - digitalWrite(_ss, LOW); - delay(2); // wake up PN532 - - write(DATA_WRITE); - write(PN532_PREAMBLE); - write(PN532_STARTCODE1); - write(PN532_STARTCODE2); - - uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA - write(length); - write(~length + 1); // checksum of length - - write(PN532_HOSTTOPN532); - uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA - - DMSG("write: "); - - for (uint8_t i = 0; i < hlen; i++) { - write(header[i]); - sum += header[i]; - - DMSG_HEX(header[i]); - } - for (uint8_t i = 0; i < blen; i++) { - write(body[i]); - sum += body[i]; - - DMSG_HEX(body[i]); - } - - uint8_t checksum = ~sum + 1; // checksum of TFI + DATA - write(checksum); - write(PN532_POSTAMBLE); - - digitalWrite(_ss, HIGH); - - DMSG('\n'); -} - -int8_t PN532_SPI::readAckFrame() -{ - const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; - - uint8_t ackBuf[sizeof(PN532_ACK)]; - - digitalWrite(_ss, LOW); - delay(1); - write(DATA_READ); - - for (uint8_t i = 0; i < sizeof(PN532_ACK); i++) { - ackBuf[i] = read(); - } - - digitalWrite(_ss, HIGH); - - return memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK)); -} diff --git a/libraries/PN532_SPI/PN532_SPI.h b/libraries/PN532_SPI/PN532_SPI.h deleted file mode 100644 index 53ff697..0000000 --- a/libraries/PN532_SPI/PN532_SPI.h +++ /dev/null @@ -1,36 +0,0 @@ - -#ifndef __PN532_SPI_H__ -#define __PN532_SPI_H__ - -#include -#include "PN532Interface.h" - -class PN532_SPI : public PN532Interface { -public: - PN532_SPI(SPIClass &spi, uint8_t ss); - - void begin(); - void wakeup(); - int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); - - int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); - -private: - SPIClass* _spi; - uint8_t _ss; - uint8_t command; - - boolean isReady(); - void writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); - int8_t readAckFrame(); - - inline void write(uint8_t data) { - _spi->transfer(data); - }; - - inline uint8_t read() { - return _spi->transfer(0); - }; -}; - -#endif diff --git a/libraries/Sleep_n0m1/README b/libraries/Sleep_n0m1/README deleted file mode 100644 index 62091c7..0000000 --- a/libraries/Sleep_n0m1/README +++ /dev/null @@ -1,73 +0,0 @@ -Name : Sleep_n0m1 Library -Author : Noah Shibley, Michael Grant, NoMi Design Ltd. http://n0m1.com -Date : July 10th 2011 -Version : 0.1 -Notes : Arduino Library to place the arduino into sleep mode for - a specific length of time, or a specific number of sleep cycles - Some of this code comes from "Cloudy" on the arduino forum - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1292898715 - - Refer to the examples for how to use this library. - -Dependencies: - none - - - -List of Functions: - -Function: idleMode -Description: sets the Arduino into idle Mode sleep, -the least power saving, The idle mode stops the MCU -but leaves peripherals and timers running. ------------------------------------------------------- -Function: adcMode -Description: sets the Arduino into adc Mode sleep, -This mode makes the MCU enter ADC Noise Reduction mode, -stopping the CPU but allowing the ADC, the external interrupts, -the 2-wire Serial Interface address watch, Timer/Counter2 -and the Watchdog to continue operating ------------------------------------------------------- -Function: pwrSaveMode -Description: sets the Arduino into power Save Mode sleep, -The timer crystal will continue to operate in this mode, -Timer2 still active. ------------------------------------------------------- -Function: extStandbyMode -Description: sets the Arduino into extStandby Mode sleep, -This mode is identical to Power-save with the exception -that the Oscillator is kept running for fast wake up ------------------------------------------------------- -Function: standbyMode -Description: sets the Arduino into standby Mode sleep, -This mode is identical to Power-down with the exception -that the Oscillator is kept running for fast wake up ------------------------------------------------------- -Function: pwrDownMode -Description: sets the Arduino into power Down Mode sleep, -The most power saving, all systems are powered down -except the watch dog timer and external reset ------------------------------------------------------- -Function: sleepDelay -Description: Works like the Arduino delay function, sets the -Arduino into sleep mode for a specified time. -Parameters: (unsigned long) time in ms of the sleep cycle ------------------------------------------------------- -Function: sleepDelay -Description: Works like the Arduino delay function, sets the -Arduino into sleep mode for a specified time. -Parameters: (unsigned long) time in ms of the sleep cycle - (boolean) prevents the Arduino from entering sleep ------------------------------------------------------- -Function: setCalibrationInterval -Description: the WDT needs to be calibrated against timer 0 -periodically to keep the sleep time accurate. Default calibration -occurs every 100 wake/sleep cycles. recalibrate too often will -waste power and too rarely will make the sleep time inaccurate. -Parameters: (int) set the # of wake/sleep cycles between calibrations ------------------------------------------------------- -Function: sleepInterrupt -Description: set the Arduino into sleep mode until an interrupt is -triggered. The interrupts are passed in as parameters -Parameters: (int) interrupt value, 0, 1, etc, see attachinterrupt() - (int) mode of trigger, LOW,RISING,FALLING,CHANGE \ No newline at end of file diff --git a/libraries/Sleep_n0m1/Sleep_n0m1.cpp b/libraries/Sleep_n0m1/Sleep_n0m1.cpp deleted file mode 100644 index cb5e126..0000000 --- a/libraries/Sleep_n0m1/Sleep_n0m1.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/************************************************************************************ - * - * Name : Sleep_n0m1.cpp - * Author : Noah Shibley / NoMi Design - * Date : July 10th 2011 - * Version : 0.1 - * Notes : Some of this code comes from "Cloudy" on the arduino forum - * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1292898715 - * - * Copyright (c) 2012 NoMi Design (http://n0m1.com) All right reserved. - * - * This file is part of Triggertrap. See Triggertrap.com for more information. - * - * Triggertrap 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. - * - * Triggertrap 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 Triggertrap. If not, see . - * - ***********************************************************************************/ - -#include "Sleep_n0m1.h" - -Sleep* Sleep::pSleep = 0; - -Sleep::Sleep() -{ - pSleep = this; //the ptr points to this object - timeSleep = 0; // total time due to sleep - calibv = 1.0; // ratio of real clock with WDT clock - byte isrcalled = 0; // WDT vector flag - sleepCycleCount = 0; - sleepCycleInterval = 100; - -} - -/******************************************************************** -* -* setSleepMode -* -********************************************************************/ -void Sleep::setSleepMode(int mode) -{ - sleepMode_ = mode; -} - - -/******************************************************************** -* -* calibrateTime -* -********************************************************************/ -void Sleep::calibrateTime(unsigned long sleepTime, boolean &abortCycle) { - // timer0 continues to run in idle sleep mode - set_sleep_mode(SLEEP_MODE_IDLE); - long tt1=millis(); - sleepWDT(sleepTime,abortCycle); - long tt2=millis(); - - calibv = (float) sleepTime/(tt2-tt1); - - //Serial.println(calibv); -} - -/******************************************************************** -* -* WDTMillis -* -********************************************************************/ -unsigned long Sleep::WDTMillis() { - return millis()+timeSleep; -} - -/******************************************************************** -* -* sleepNow -* -********************************************************************/ -void Sleep::sleepInterrupt(int interrupt,int mode) { - - if(mode == FALLING || mode == LOW) - { - int pin = interrupt + 2; //will fail on the mega - pinMode (pin, INPUT); - digitalWrite (pin, HIGH); - } - - set_sleep_mode(sleepMode_); - sleep_enable(); - attachInterrupt(interrupt,sleepHandler,mode); - sei(); //make sure interrupts are on! - sleep_mode(); - //----------------------------- ZZZZZZ sleeping here---------------------- - sleep_disable(); //disable sleep, awake now - detachInterrupt(interrupt); -} - - -/******************************************************************** -* -* sleepDelay -* -********************************************************************/ -void Sleep::sleepDelay(unsigned long sleepTime){ - - boolean abortCycle = false; - - sleepDelay(sleepTime,abortCycle); -} - -/******************************************************************** -* -* sleepDelay -* -********************************************************************/ -void Sleep::sleepDelay(unsigned long sleepTime, boolean &abortCycle) { - ADCSRA &= ~(1< 0) { - //work out next prescale unit to use - while ((0x10< remainTime && WDTps > 0) { - WDTps--; - } - // send prescaler mask to WDT_On - WDT_On((WDTps & 0x08 ? (1<WDT_Off(); - Sleep::pSleep->isrcalled=1; -} diff --git a/libraries/Sleep_n0m1/Sleep_n0m1.h b/libraries/Sleep_n0m1/Sleep_n0m1.h deleted file mode 100644 index 57ebda7..0000000 --- a/libraries/Sleep_n0m1/Sleep_n0m1.h +++ /dev/null @@ -1,109 +0,0 @@ -/************************************************************************************ - * - * Name : Sleep_n0m1.h - * Author : Noah Shibley / NoMi Design - * Date : July 10th 2011 - * Version : 0.1 - * Notes : Some of this code comes from "Cloudy" on the arduino forum - * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1292898715 - * - * Copyright (c) 2012 NoMi Design (http://n0m1.com) All right reserved. - * - * This file is part of Triggertrap. See Triggertrap.com for more information. - * - * Triggertrap 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. - * - * Triggertrap 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 Triggertrap. If not, see . - * - ***********************************************************************************/ - -#ifndef SLEEP_H -#define SLEEP_H - - - -#include -#include -#if defined(ARDUINO) && ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -extern "C" void WDT_vect(void) __attribute__ ((signal)); -extern "C" void sleepHandler(void) __attribute__ ((signal)); - -class Sleep { - -public: - - friend void WDT_vect(void); - friend void sleepHandler(void); - -Sleep(); - - /* modes of sleep - SLEEP_MODE_IDLE - SLEEP_MODE_ADC - SLEEP_MODE_PWR_SAVE - SLEEP_MODE_EXT_STANDBY - SLEEP_MODE_STANDBY - SLEEP_MODE_PWR_DOWN - */ - - void idleMode() { setSleepMode(SLEEP_MODE_IDLE);} - void adcMode() {setSleepMode(SLEEP_MODE_ADC);} - void pwrSaveMode() {setSleepMode(SLEEP_MODE_PWR_SAVE);} - void extStandbyMode(){setSleepMode(SLEEP_MODE_EXT_STANDBY);} - void standbyMode(){setSleepMode(SLEEP_MODE_STANDBY);} - void pwrDownMode(){setSleepMode(SLEEP_MODE_PWR_DOWN);} - - //WatchDog Sleep Functions: Sleep for a specfic length of time - void sleepDelay(unsigned long sleepTime); - void sleepDelay(unsigned long sleepTime,boolean &abortCycle); - void setCalibrationInterval(int interval){ sleepCycleInterval = interval; } - - //Interrupt Sleep Function: Sleep till something interrupts sleep - void sleepInterrupt(int interrupt,int mode); - - - - - - -private: - - int sleepMode_; - unsigned long timeSleep; - float calibv; - volatile byte isrcalled; - static Sleep* pSleep; //static ptr to Sleep class for the ISR - int sleepCycleCount; - int sleepCycleInterval; - - void setSleepMode(int mode); - void WDT_Off(); - void WDT_On(byte psMask); - int sleepWDT(unsigned long remainTime,boolean &abortCycle); - void calibrateTime(unsigned long sleepTime,boolean &abortCycle); //calibrate the time keeping difference between WDT and Timer0 - unsigned long WDTMillis(); // Estimated millis is real clock + calibrated sleep time - -}; - -#endif - - - - - - - diff --git a/libraries/Sleep_n0m1/examples/Sleep_n0m1_Interrupt/Sleep_n0m1_Interrupt.ino b/libraries/Sleep_n0m1/examples/Sleep_n0m1_Interrupt/Sleep_n0m1_Interrupt.ino deleted file mode 100644 index b8fe2af..0000000 --- a/libraries/Sleep_n0m1/examples/Sleep_n0m1_Interrupt/Sleep_n0m1_Interrupt.ino +++ /dev/null @@ -1,28 +0,0 @@ -#include - -Sleep sleep; - -void setup() -{ - - Serial.begin(9600); - - -} - -void loop() -{ - - delay(100); ////delays are just for serial print, without serial they can be removed - Serial.println("execute your code here"); - - Serial.print("Sleeping Till Interrupt"); - - delay(100); //delay to allow serial to fully print before sleep - - sleep.pwrDownMode(); //set sleep mode - sleep.sleepInterrupt(0,FALLING); //sleep for: sleepTime - - - -} diff --git a/libraries/Sleep_n0m1/examples/Sleep_n0m1_abortCycle/Sleep_n0m1_abortCycle.ino b/libraries/Sleep_n0m1/examples/Sleep_n0m1_abortCycle/Sleep_n0m1_abortCycle.ino deleted file mode 100644 index 8a5390f..0000000 --- a/libraries/Sleep_n0m1/examples/Sleep_n0m1_abortCycle/Sleep_n0m1_abortCycle.ino +++ /dev/null @@ -1,44 +0,0 @@ -#include - -Sleep sleep; -boolean abortSleep; //cancel sleep cycle -int sleepCycleCount; //the number of times awake then asleep -unsigned long sleepTime; //how long you want the arduino to sleep - - - -void setup() -{ - - Serial.begin(9600); - abortSleep = false; //can be used to cancel the sleep cycle - sleepTime = 5000; //set sleep time in ms, max sleep time is 49.7 days - -} - -void loop() -{ - if(abortSleep) - { - Serial.println("sleep no more!"); - } - else - { - delay(100); - Serial.print("sleeping for "); - Serial.println(sleepTime); - delay(100); //delay to allow serial to fully print before sleep - } - - sleepCycleCount++; - if(sleepCycleCount > 5) - { - abortSleep = true; - } - - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(sleepTime,abortSleep); //sleep for: sleepTime - - - -} diff --git a/libraries/Sleep_n0m1/examples/Sleep_n0m1_countDown/Sleep_n0m1_countDown.ino b/libraries/Sleep_n0m1/examples/Sleep_n0m1_countDown/Sleep_n0m1_countDown.ino deleted file mode 100644 index a2ca62c..0000000 --- a/libraries/Sleep_n0m1/examples/Sleep_n0m1_countDown/Sleep_n0m1_countDown.ino +++ /dev/null @@ -1,39 +0,0 @@ -#include - -int count = 0; -Sleep sleep; -unsigned long sleepTime; //how long you want the arduino to sleep - - - -void setup() -{ - - Serial.begin(9600); - sleepTime = 50000; //set sleep time in ms, max sleep time is 49.7 days - -} - -void loop() -{ - delay(100); //delay to allow serial output to be ready after wake up - Serial.print("Awake for "); - Serial.print(count); - Serial.println("sec"); - - count++; - delay(1000); // waits for a second - - if(count >= 3) - { - count = 0; - Serial.print("sleeping "); - Serial.println(sleepTime); - delay(100); //delay to allow serial to fully print before sleep - - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(sleepTime); //sleep for: sleepTime - } - - -} diff --git a/libraries/Sleep_n0m1/examples/Sleep_n0m1_simple/Sleep_n0m1_simple.ino b/libraries/Sleep_n0m1/examples/Sleep_n0m1_simple/Sleep_n0m1_simple.ino deleted file mode 100644 index 09a04ce..0000000 --- a/libraries/Sleep_n0m1/examples/Sleep_n0m1_simple/Sleep_n0m1_simple.ino +++ /dev/null @@ -1,31 +0,0 @@ -#include - -Sleep sleep; -unsigned long sleepTime; //how long you want the arduino to sleep - - - -void setup() -{ - - Serial.begin(9600); - sleepTime = 50000; //set sleep time in ms, max sleep time is 49.7 days - -} - -void loop() -{ - - delay(100); ////delays are just for serial print, without serial they can be removed - Serial.println("execute your code here"); - - Serial.print("sleeping for "); - Serial.println(sleepTime); - delay(100); //delay to allow serial to fully print before sleep - - sleep.pwrDownMode(); //set sleep mode - sleep.sleepDelay(sleepTime); //sleep for: sleepTime - - - -} From 8a6b092db87d4f2ad95e36d81117c36f7d6aaa05 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 18 Aug 2014 21:35:50 +0200 Subject: [PATCH 309/326] We only need one constructor for the gateway sketch. --- SerialGateway/SerialGateway.ino | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/SerialGateway/SerialGateway.ino b/SerialGateway/SerialGateway.ino index 7eca35f..a78a2de 100644 --- a/SerialGateway/SerialGateway.ino +++ b/SerialGateway/SerialGateway.ino @@ -15,14 +15,14 @@ * - Inclusion button should be connected between digital pin 3 and GND * - RX/TX/ERR leds need to be connected between +5V (anode) and digital ping 6/5/4 with resistor 270-330R in a series * - * LED purposes: + * LEDs (OPTIONAL): * - RX (green) - blink fast on radio message recieved. In inclusion mode will blink fast only on presentation recieved * - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly * - ERR (red) - fast blink on error during transmission error or recieve crc error */ #include -#include +#include #include #include #include @@ -31,13 +31,8 @@ #define INCLUSION_MODE_PIN 3 // Digital pin used for inclusion mode button -// No blink or button functionality. Use the vanilla constructor. -//Gateway gw; - -// To start gateway with include button and led blinking functionality enabled use this instead! MyGateway gw(9, 10, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4); - char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface int inputPos = 0; boolean commandComplete = false; // whether the string is complete From a3a401c1c7f34c601784f5353972b7585a57d215 Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Wed, 20 Aug 2014 09:44:18 +0200 Subject: [PATCH 310/326] OTA Bootloader FIND_PARENT and include binary - move from PING to FIND_PARENT - include compiled hex of bootloader --- Bootloader/.gitignore | 1 - Bootloader/MyOtaBootloader.c | 6 +- Bootloader/MyOtaBootloader.hex | 214 +++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 Bootloader/MyOtaBootloader.hex diff --git a/Bootloader/.gitignore b/Bootloader/.gitignore index bdb41b7..71c9880 100644 --- a/Bootloader/.gitignore +++ b/Bootloader/.gitignore @@ -1,3 +1,2 @@ *.o -*.hex *.elf \ No newline at end of file diff --git a/Bootloader/MyOtaBootloader.c b/Bootloader/MyOtaBootloader.c index a601075..cd58712 100644 --- a/Bootloader/MyOtaBootloader.c +++ b/Bootloader/MyOtaBootloader.c @@ -68,7 +68,7 @@ static uint8_t sendAndWait(uint8_t reqType, uint8_t resType) { continue; if (rmsg.destination == nc.nodeId) { if (mGetCommand(rmsg) == C_INTERNAL) { - if (rmsg.type == I_PING_ACK) { + if (rmsg.type == I_FIND_PARENT_RESPONSE) { if (rmsg.data[0] < nc.distance - 1) { nc.distance = rmsg.data[0] + 1; nc.parentNodeId = rmsg.sender; @@ -129,8 +129,8 @@ int main () { nc.distance = 255; mSetCommand(msg, C_INTERNAL); msg.destination = BROADCAST_ADDRESS; - if (!sendAndWait(I_PING, I_PING_ACK)) -// starting existing firmware if no ping response (if firmware is valid) + if (!sendAndWait(I_FIND_PARENT, I_FIND_PARENT_RESPONSE)) +// starting existing firmware if no parent responds (if firmware is valid) // reboot(); startup(); diff --git a/Bootloader/MyOtaBootloader.hex b/Bootloader/MyOtaBootloader.hex new file mode 100644 index 0000000..6fd8c58 --- /dev/null +++ b/Bootloader/MyOtaBootloader.hex @@ -0,0 +1,214 @@ +:1070000011E0A0E0B1E0E0E4FDE702C005900D92E0 +:10701000A030B107D9F711E0A0E0B1E001C01D92A6 +:10702000A83DB107E1F711248091610090E08F70D5 +:107030009070909301018093000182E090E020E83D +:107040000FB6F89420936100809361000FBE14BEC8 +:1070500029E288E190E00FB6F894A895809360004B +:107060000FBE20936000A8951092CF011092D0011E +:107070001092D1011092D2011092D3011092D4013A +:107080001092D5011092D60184B18E6284B92498F1 +:1070900085B1857D85B92A9A8AE090E0B3D184E0F4 +:1070A0006FE520D281E06BE31DD285E06CE41AD25B +:1070B00086E0E5D1682F607D676286E013D280E0CC +:1070C000DED1682F6C6080E00DD287E060E70AD2E5 +:1070D0002A9881E090E096D180E5A6D183E7A4D1FB +:1070E0002A9A81E090E08ED18DE1C9D1682F666047 +:1070F0008DE1F8D18CE1C3D1682F6F638CE1F2D1BF +:1071000082EEADD181EEABD104D280E0B8D1682F50 +:107110006E7F80E0E7D182E00FEF1CEF21EE38EACE +:1071200048EA50E060E070E002D2A89582E091E089 +:1071300060E070E043E050E02DEE39E38AD5A89599 +:107140008091020180930E0180930D014FD22FEFA9 +:10715000209313018091110192E0909310012093EC +:107160000401887083608093110120930F0187E0F0 +:1071700068E04AD3882309F42AD310920F01809142 +:1071800002018F3F09F038C081E00FEF1CEF21EEC4 +:1071900038EA48EA50E060E070E0C9D183E064E09A +:1071A00033D3882309F407D38091320182958695E1 +:1071B0008770813019F48091350105C0882329F446 +:1071C00085E391E028D580930201809102018F3FF1 +:1071D00009F4F1D280910201F999FECF1FBA12BCD5 +:1071E00011BC80BD0FB6F894FA9AF99A0FBE80933D +:1071F0000E0180930D01FAD185E091E06BE171E021 +:1072000048E050E02DEE39E324D5909111019871BA +:10721000809110018770806280931001946C90932C +:1072200011018091050190910601909315018093C1 +:1072300014018091070190910801909317018093A8 +:10724000160180E061E0E0D2882309F4C0D22091E9 +:10725000370130913801809107019091080128177A +:107260003907C9F42091390130913A01809109011F +:1072700090910A012817390771F420913B01309150 +:107280003C0180910B0190910C012817390719F4EA +:107290005DD281119CD240913501509136015093BD +:1072A0000601409305012091370130913801309358 +:1072B0000801209307018091390190913A01909340 +:1072C0000A018093090180913B0190913C019093C8 +:1072D0000C0180930B01F4E1EF2EF1E0FF2E809181 +:1072E00010018770806380931001509315014093C3 +:1072F00014013093170120931601C0E0D0E000E99B +:107300001FEFBB24B394E5E0CE2E71E1D72E4DC024 +:10731000F701D583C48382E063E076D2882309F441 +:107320004AD2902F90599F77E92FF0E0E15BFE4F12 +:10733000ABE3B1E080E10D9001928150E1F790372D +:1073400009F030C03FB7F894F999FECF83E0F80117 +:1073500080935700E89507B600FCFDCFA801AFE485 +:10736000B1E02C9111968C91119790E0982F88277D +:10737000820F911D1296FA010C01B0925700E89508 +:1073800011244E5F5F4FF1E0AF3CBF0751F7F801AA +:10739000C0925700E89507B600FCFDCFD092570089 +:1073A000E8953FBF2196005F1F4F809109019091A2 +:1073B0000A01C817D90708F4ABCFA895C7D188230D +:1073C00049F08BE191E065E071E048E050E024EFA6 +:1073D00039E35BD4F0D180E090E0F999FECF92BD23 +:1073E00081BDF89A80B50895F999FECF1FBA92BD74 +:1073F00081BD60BD0FB6F894FA9AF99A0FBE089550 +:107400000000089540E050E00BC020E030E00000B4 +:107410002F5F3F4F61E0243F3607C9F74F5F5F4F53 +:107420004817590790F308950F931F93182F00E002 +:10743000E7DF17FD02C02B9801C02B9AE1DF2D9AE0 +:1074400083B1110F82958170182BDADF2D98D8DF68 +:107450000F5F083069F7812F1F910F9108951F93D7 +:10746000182F2A9881E090E0CDDF812FDDDF182FE3 +:107470002A9A81E090E0C6DF812F1F9108951F9323 +:10748000182F2A9881E090E0BDDF812F8F71CCDF2B +:107490008FEFCADF182F2A9A81E090E0B3DF812FA7 +:1074A0001F9108950F931F93CF93DF93182FEB0134 +:1074B000042F2A9881E090E0A5DF1F71812F806260 +:1074C000B3DF182F03C08991AFDF01500023D9F734 +:1074D0002A9A81E090E096DF812FDF91CF911F9172 +:1074E0000F9108950F931F93182F062F2A9881E06C +:1074F00090E088DF1F71812F806296DF182F802F28 +:1075000093DF2A9A81E090E07DDF812F1F910F9118 +:10751000089580E0B4DF81FD09C080E0B0DF682F0E +:10752000626080E0DFDF85E090E06CDF08957F92AD +:107530008F929F92AF92BF92CF92DF92EF92FF9283 +:107540000F931F93DF93CF93CDB7DEB728970FB676 +:10755000F894DEBF0FBECDBF782E480159016A01F5 +:107560007B01882381F40093CF019092D001209376 +:10757000D101B092D2014093D301D092D401609353 +:10758000D501F092D60189829A82AB82BC82CD82EB +:10759000DE82EF82F88681E0871538F0872D865FDE +:1075A000BE016F5F7F4F45E07DDF82E068DF21E055 +:1075B00030E002C0220F331F7A94E2F7682F622B6B +:1075C00082E090DF28960FB6F894DEBF0FBECDBFE5 +:1075D000CF91DF911F910F91FF90EF90DF90CF90AF +:1075E000BF90AF909F908F907F9008957F928F92E1 +:1075F0009F92AF92BF92CF92DF92EF92FF920F9342 +:107600001F93DF93CF93CDB7DEB728970FB6F894CB +:10761000DEBF0FBECDBF8F3F09F483C0082F10E03F +:1076200020E030E040E050E060E070E089831A83C1 +:107630002B833C834D835E836F83788788243CEF64 +:10764000932E21EEA22E98EAB92E88EAC82EDD24C8 +:10765000EE24FF24080D81E09981091708F080E0ED +:10766000192D91E0AA819A1608F090E0E82FE10F19 +:1076700081E0E11708F080E0982B1E2F2A2D772457 +:107680007394BB81AB1608F07724920F81E09217B8 +:1076900008F080E0782A292F3B2D91E0EC81BE167E +:1076A00008F090E0E72DE30F81E0E31708F080E0B9 +:1076B000982B3E2F4C2D77247394FD81CF1608F024 +:1076C0007724940F81E0941708F080E0782A492FFE +:1076D0005D2D91E08E81D81608F090E0E72DE50F42 +:1076E00081E0E51708F080E0982B5E2F6E2D77245F +:1076F0007394AF81EA1608F07724960F81E096170D +:1077000008F080E0782A692F7F0C772D59016A01F3 +:10771000E92E80E00CDF81E09501A6016E2D772D2A +:1077200006DF28960FB6F894DEBF0FBECDBFCF910F +:10773000DF911F910F91FF90EF90DF90CF90BF905E +:10774000AF909F908F907F90089518954091090178 +:1077500050910A0164E0440F551F6A95E1F7E0E09B +:10776000F0E02FEF3FEF19C084912827822F8295F8 +:107770008227082E869586958025082E8695802559 +:107780008770022E232F869507948795302D282702 +:1077900006948795302528273196E417F50720F3BE +:1077A00040E080910B0190910C012817390709F4F2 +:1077B00041E0842F089528E088E190E00FB6F89426 +:1077C000A895809360000FBE20936000FFCFBEDFBE +:1077D0008823C1F088E10FB6F8948093600010927E +:1077E00060000FBE809100019091010120E80FB66A +:1077F000F89420936100809361000FBEE0E0F0E018 +:1078000009950895D8DF08952F923F924F925F9285 +:107810006F927F928F929F92AF92BF92CF92DF92A0 +:10782000EF92FF920F931F93DF93CF93CDB7DEB705 +:10783000E3970FB6F894DEBF0FBECDBF6BA7809362 +:1078400012016624B9E02B2E312C2C0E3D1E2E0188 +:107850000894411C511CD101EDE0F1E081E201905E +:107860000D928150E1F7109103017C842B852AA7AA +:1078700050DE299881E090E0C5DD81EEF0DD82EEFA +:10788000EEDD80E0FCDD682F6E7F80E02BDE30E0F7 +:1078900040E050E060E070E080E090E01CA73DA791 +:1078A0004EA75FA768AB79AB8AAB9BAB8824ACEFE4 +:1078B0009A2EF1EEAF2EE8EABE2E78EAC72EDD242E +:1078C000EE24FF24012F080D81E0011708F080E06D +:1078D000192D91E0EDA59E1608F090E0E82FE10F3C +:1078E00081E0E11708F080E0982B1E2F2A2DE1E0BF +:1078F000FEA5AF1608F0E0E0920F81E0921708F0C5 +:1079000080E0E82B292F3B2D91E08FA5B81608F0D9 +:1079100090E0E30F81E0E31708F080E0982B3E2F22 +:107920004C2DE1E0F8A9CF1608F0E0E0940F81E0DB +:10793000941708F080E0E82B492F5D2D91E089A98C +:10794000D81608F090E0E50F81E0E51708F080E038 +:10795000982B5E2F6E2D81E0EAA9EE1608F080E0EC +:10796000E92FE60F91E0E61708F090E0892B8F0DE4 +:1079700009831A832B833C834D835E83EF8388873F +:107980008AE0B20145E08EDD80E1B20145E08ADDAA +:10799000FAA5FF3F11F010EA01C010EB769476943F +:1079A000769427E0720E2A9881E090E02BDD812FFB +:1079B0003BDD81017101E70CF11C04C0F8018191EC +:1079C0008F0132DD0E151F05C9F72A9A81E090E07C +:1079D00019DD299A81E090E015DD299881E090E099 +:1079E00011DD8FEF3CDD8073E1F387E060E77ADD46 +:1079F00084FF02C081EE33DD8CDD80E040DD682F46 +:107A0000616080E06FDD87E060E76CDD0091CF01B1 +:107A10001091D0012091D1013091D2014091D30138 +:107A20005091D4016091D5017091D601802F812BA6 +:107A3000822B832B842B852B862B872B61F009834C +:107A40001A832B833C834D835E836F8378878AE020 +:107A5000B20145E027DD82EE02DD81EE00DD299AEC +:107A600081E090E0CFDCAA24BB24A895CC24DD24BF +:107A7000A89587E104DD80FD9CC08FEFF0DCE82E47 +:107A8000E694F7E0EF22EF1609F493C02A9881E01C +:107A900090E0B8DC80E6C8DC8FEFC6DCF82E2A9ACE +:107AA00081E090E0AFDC20E22F1518F482EED7DC05 +:107AB000FF242A9881E090E0A5DC81E6B5DC0EE2A7 +:107AC00011E006C08FEFB0DCF80181938F01FA94CA +:107AD000FF20C1F72A9A81E090E094DC87E060E71C +:107AE00001DD2A9881E090E08DDC8E2D886A9CDC97 +:107AF0008E2D9ADC2A9A81E090E084DC809131011D +:107B00008770823009F058C0909130018091020155 +:107B1000981709F04EC080913201682F70E0CB01B8 +:107B200087709070039709F034C0809133018830DA +:107B300009F02FC040913501242F332727FD3095C0 +:107B40008091040190E001972817390714F5242F3C +:107B50002F5F2093040140912F0140930301F99975 +:107B6000FECF1FBA81E090E092BD81BD40BD0FB64F +:107B7000F894FA9AF99A0FBEF999FECF1FBA42E02B +:107B800050E052BD41BD20BD0FB6F894FA9AF99A63 +:107B90000FBE8091110190E0862797278770907023 +:107BA000892B39F4809133015BA5851711F481E0AD +:107BB00019C081E090E026DC0894C11CD11C64E669 +:107BC000C616D10409F054CF0894A11CB11C74E16D +:107BD000A716B10409F049CF63948AE0681609F04A +:107BE0003ACE80E0E3960FB6F894DEBF0FBECDBF6D +:107BF000CF91DF911F910F91FF90EF90DF90CF9089 +:107C0000BF90AF909F908F907F906F905F904F90BC +:107C10003F902F900895FC0188279927E89421919F +:107C20002032E9F3293010F02E30C8F32B3241F026 +:107C30002D3239F4689404C00E94613E820F911D78 +:107C4000219120532A30C0F31EF4909581959F4FC7 +:107C50000895A0E0B0E0EFE2FEE30C94733E7C01F7 +:107C6000EB018A01690109C0CE012196F60109954F +:107C7000F70181937F010150104001151105A1F713 +:107C8000CDB7DEB7E8E00C948F3EA0E0B0E0EBE4C7 +:107C9000FEE30C94733EEC017B018A01690109C08B +:107CA000CE012196F70161917F01F60109950150FE +:107CB000104001151105A1F7CDB7DEB7E8E00C942F +:107CC0008F3E7AE0979F902D879F802D910D1124F4 +:107CD00008952F923F924F925F926F927F928F9270 +:107CE0009F92AF92BF92CF92DF92EF92FF920F934B +:107CF0001F93CF93DF93CDB7DEB7CA1BDB0B0FB655 +:107D0000F894DEBF0FBECDBF09942A883988488811 +:107D10005F846E847D848C849B84AA84B984C884A7 +:107D2000DF80EE80FD800C811B81AA81B981CE0F9E +:107D3000D11D0FB6F894DEBF0FBECDBFED01089583 +:040000030000700089 +:00000001FF From 3c1820ca93266b638a5f4efeb168dcd69fcad962 Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Wed, 20 Aug 2014 09:51:54 +0200 Subject: [PATCH 311/326] NodeJsController adjust to ack in serial message protocol --- NodeJsController/NodeJsController.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NodeJsController/NodeJsController.js b/NodeJsController/NodeJsController.js index 2a127fa..f033e1c 100644 --- a/NodeJsController/NodeJsController.js +++ b/NodeJsController/NodeJsController.js @@ -524,8 +524,9 @@ function rfReceived(data, db, gw) { var sender = +datas[0]; var sensor = +datas[1]; var command = +datas[2]; - var type = +datas[3]; - var rawpayload = datas[4].trim(); + var ack = +datas[3]; + var type = +datas[4]; + var rawpayload = datas[5].trim(); var payload; if (command == C_STREAM) { payload = []; From 9dd396b89b721b77f738b719234b9507ab229d3f Mon Sep 17 00:00:00 2001 From: Mr Skrivning Date: Thu, 21 Aug 2014 00:11:24 +0200 Subject: [PATCH 312/326] Fixed appending of commands + a serial initialization what worked better (only tested Serial, not ethernet) --- NodeJsController/NodeJsController.js | 34 +++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/NodeJsController/NodeJsController.js b/NodeJsController/NodeJsController.js index f033e1c..6c52630 100644 --- a/NodeJsController/NodeJsController.js +++ b/NodeJsController/NodeJsController.js @@ -4,6 +4,7 @@ const gwPort = 9999; //const gwType = 'Serial'; //const gwPort = 'COM4'; +//const gwPort = '/dev/ttyUSB0'; //const gwBaud = 115200; const dbAddress = '127.0.0.1'; @@ -116,6 +117,7 @@ const P_ULONG32 = 5; const P_CUSTOM = 6; var fs = require('fs'); +var appendedString=""; function crcUpdate(old, value) { var c = old ^ value; @@ -516,6 +518,22 @@ function sendRebootMessage(destination, gw) { gw.write(td); } + +function appendData(str, db, gw) { + pos=0; + while (str.charAt(pos) != '\n' && pos < str.length) { + appendedString=appendedString+str.charAt(pos); + pos++; + } + if (str.charAt(pos) == '\n') { + rfReceived(appendedString.trim(), db, gw); + appendedString=""; + } + if (pos < str.length) { + appendData(str.substr(pos+1,str.length-pos-1), db, gw); + } +} + function rfReceived(data, db, gw) { if ((data != null) && (data != "")) { console.log('<- ' + data); @@ -526,7 +544,10 @@ function rfReceived(data, db, gw) { var command = +datas[2]; var ack = +datas[3]; var type = +datas[4]; - var rawpayload = datas[5].trim(); + var rawpayload=""; + if (datas[5]) { + rawpayload = datas[5].trim(); + } var payload; if (command == C_STREAM) { payload = []; @@ -635,9 +656,7 @@ dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err gw.on('connect', function() { console.log('connected to ethernet gateway at ' + gwAddress + ":" + gwPort); }).on('data', function(rd) { - var rds = rd.toString().split('\n'); - for (var i = 0; i < rds.length; i++) - rfReceived(rds[i].trim(), db, gw); + appendData(rd.toString(), db, gw); }).on('end', function() { console.log('disconnected from gateway'); }).on('error', function() { @@ -646,14 +665,13 @@ dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err gw.setEncoding('ascii'); }); } else if (gwType == 'Serial') { - gw = require('serialport').SerialPort(gwPort, { baudrate: gwBaud }, false); + var SerialPort = require('serialport').SerialPort; + gw = new SerialPort(gwPort, { baudrate: gwBaud }); gw.open(); gw.on('open', function() { console.log('connected to serial gateway at ' + gwPort); }).on('data', function(rd) { - var rds = rd.toString().split('\n'); - for (var i = 0; i < rds.length; i++) - rfReceived(rds[i].trim(), db, gw); + appendData(rd.toString(), db, gw); }).on('end', function() { console.log('disconnected from gateway'); }).on('error', function() { From b936e0aebed36394801162bdcb1195cd38d2ceff Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Thu, 21 Aug 2014 00:50:57 +0200 Subject: [PATCH 313/326] RPi init script to run NodeJsController at boot time execute the following to install: chmod 755 *.sh sudo cp NodeJsControllerInitScript.sh /etc/init.d/mysensors sudo update-rc.d mysensors defaults sudo mkdir -p /usr/local/var/run sudo chmod -R 777 /usr/local/var/run --- NodeJsController/NodeJsController.sh | 8 + .../NodeJsControllerInitScript.sh | 160 ++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 NodeJsController/NodeJsController.sh create mode 100644 NodeJsController/NodeJsControllerInitScript.sh diff --git a/NodeJsController/NodeJsController.sh b/NodeJsController/NodeJsController.sh new file mode 100644 index 0000000..99bf500 --- /dev/null +++ b/NodeJsController/NodeJsController.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# pidfile must be first argument +PIDFILE=$1 +mkdir -p /home/pi/logs +cd /home/pi/Arduino/NodeJsController +/opt/node/bin/node NodeJsController.js >>/home/pi/logs/NodeJsController.log 2>&1 $PIDFILE diff --git a/NodeJsController/NodeJsControllerInitScript.sh b/NodeJsController/NodeJsControllerInitScript.sh new file mode 100644 index 0000000..2c934a5 --- /dev/null +++ b/NodeJsController/NodeJsControllerInitScript.sh @@ -0,0 +1,160 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: mysensors +# Required-Start: $remote_fs $syslog $mongodb +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: MySensors NodeJs Controller +# Description: MySensors NodeJs Controller +### END INIT INFO + +# Author: Foo Bar +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="MySensors NodeJs Controller" +NAME="NodeJsController.sh" +PIDFILE="/usr/local/var/run/$NAME.pid" +DAEMON="/home/pi/Arduino/NodeJsController/$NAME" +DAEMON_ARGS="$PIDFILE" +DAEMON_USER="pi" +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --chuid $DAEMON_USER --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --chuid $DAEMON_USER --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: + From dfcd3d44d39ee58e86384db437601a7a352c2a71 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Thu, 21 Aug 2014 09:59:30 +0200 Subject: [PATCH 314/326] Simplify gateway code (handles interrupts internally) and remove external dependencies. Ledmode is now always "enabled". Remove unused libraries. Add default CS/CE pin to Config.h Add new variebles for voltage and current. Add new internal command sent to controller when gateway is ready and online --- SerialGateway/SerialGateway.ino | 25 +- .../MsTimer2/examples/FlashLed/FlashLed.pde | 42 -- libraries/MsTimer2/keywords.txt | 4 - libraries/MySensors/MyConfig.h | 4 + libraries/MySensors/MyGateway.cpp | 66 +- libraries/MySensors/MyGateway.h | 25 +- libraries/MySensors/MyMessage.h | 4 +- libraries/MySensors/MySensor.h | 4 +- .../utility}/MsTimer2.cpp | 0 .../utility}/MsTimer2.h | 0 .../utility}/PinChangeInt.h | 0 .../Examples/ByteBuffer/ByteBuffer.cpp | 272 ------- .../Examples/ByteBuffer/ByteBuffer.h | 104 --- .../PinChangeInt/Examples/GetPSTR/GetPSTR.h | 21 - .../PinChangeIntExample.pde | 77 -- .../PinChangeIntSpeedTest.pde | 265 ------- .../PinChangeIntTest/PinChangeIntTest.pde | 344 --------- .../PinChangeIntTest2/PinChangeIntTest2.ino | 275 ------- libraries/PinChangeInt/RELEASE_NOTES | 332 --------- libraries/PinChangeInt/gpl.txt | 674 ------------------ libraries/PinChangeInt/keywords.txt | 10 - 21 files changed, 52 insertions(+), 2496 deletions(-) delete mode 100644 libraries/MsTimer2/examples/FlashLed/FlashLed.pde delete mode 100644 libraries/MsTimer2/keywords.txt rename libraries/{MsTimer2 => MySensors/utility}/MsTimer2.cpp (100%) rename libraries/{MsTimer2 => MySensors/utility}/MsTimer2.h (100%) rename libraries/{PinChangeInt => MySensors/utility}/PinChangeInt.h (100%) delete mode 100644 libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.cpp delete mode 100644 libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.h delete mode 100644 libraries/PinChangeInt/Examples/GetPSTR/GetPSTR.h delete mode 100644 libraries/PinChangeInt/Examples/PinChangeIntExample/PinChangeIntExample.pde delete mode 100644 libraries/PinChangeInt/Examples/PinChangeIntSpeedTest/PinChangeIntSpeedTest.pde delete mode 100644 libraries/PinChangeInt/Examples/PinChangeIntTest/PinChangeIntTest.pde delete mode 100644 libraries/PinChangeInt/Examples/PinChangeIntTest2/PinChangeIntTest2.ino delete mode 100644 libraries/PinChangeInt/RELEASE_NOTES delete mode 100644 libraries/PinChangeInt/gpl.txt delete mode 100644 libraries/PinChangeInt/keywords.txt diff --git a/SerialGateway/SerialGateway.ino b/SerialGateway/SerialGateway.ino index a78a2de..83e9715 100644 --- a/SerialGateway/SerialGateway.ino +++ b/SerialGateway/SerialGateway.ino @@ -1,4 +1,4 @@ -/* + /* * Copyright (C) 2013 Henrik Ekblad * * This program is free software; you can redistribute it and/or @@ -22,8 +22,6 @@ */ #include -#include -#include #include #include @@ -31,7 +29,7 @@ #define INCLUSION_MODE_PIN 3 // Digital pin used for inclusion mode button -MyGateway gw(9, 10, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4); +MyGateway gw(DEFAULT_CE_PIN, DEFAULT_CS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4); char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface int inputPos = 0; @@ -40,34 +38,15 @@ boolean commandComplete = false; // whether the string is complete void setup() { gw.begin(); - - // C++ classes and interrupts really sucks. Need to attach interrupt - // outside thw Gateway class due to language shortcomings! Gah! - - if (gw.isLedMode()) { - // Add led timer interrupt - MsTimer2::set(300, ledTimersInterrupt); - MsTimer2::start(); - // Add interrupt for inclustion button to pin - PCintPort::attachInterrupt(INCLUSION_MODE_PIN, startInclusionInterrupt, RISING); - } } void loop() { gw.processRadioMessage(); checkSerialInput(); - } -void startInclusionInterrupt() { - gw.startInclusionInterrupt(); -} - -void ledTimersInterrupt() { - gw.ledTimersInterrupt(); -} void checkSerialInput() { if (commandComplete) { diff --git a/libraries/MsTimer2/examples/FlashLed/FlashLed.pde b/libraries/MsTimer2/examples/FlashLed/FlashLed.pde deleted file mode 100644 index 4d04496..0000000 --- a/libraries/MsTimer2/examples/FlashLed/FlashLed.pde +++ /dev/null @@ -1,42 +0,0 @@ -char dummyvar; // to get Arduinoi IDE to include core headers properly - -/* - MsTimer2 is a small and very easy to use library to interface Timer2 with - humans. It's called MsTimer2 because it "hardcodes" a resolution of 1 - millisecond on timer2 - For Details see: http://www.arduino.cc/playground/Main/MsTimer2 -*/ -#include - -// Switch on LED on and off each half second - -#if defined(ARDUINO) && ARDUINO >= 100 -const int led_pin = LED_BUILTIN; // 1.0 built in LED pin var -#else -#if defined(CORE_LED0_PIN) -const int led_pin = CORE_LED0_PIN; // 3rd party LED pin define -#else -const int led_pin = 13; // default to pin 13 -#endif -#endif - - -void flash() -{ - static boolean output = HIGH; - - digitalWrite(led_pin, output); - output = !output; -} - -void setup() -{ - pinMode(led_pin, OUTPUT); - - MsTimer2::set(500, flash); // 500ms period - MsTimer2::start(); -} - -void loop() -{ -} diff --git a/libraries/MsTimer2/keywords.txt b/libraries/MsTimer2/keywords.txt deleted file mode 100644 index 8b4fff7..0000000 --- a/libraries/MsTimer2/keywords.txt +++ /dev/null @@ -1,4 +0,0 @@ -MsTimer2 KEYWORD1 -set KEYWORD2 -start KEYWORD2 -stop KEYWORD2 diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 9cb8cb6..aedf051 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -10,6 +10,10 @@ #define RF24_PA_LEVEL_GW RF24_PA_LEVEL //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway. #define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) // This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network. +// MySensors online examples defaults +#define DEFAULT_CE_PIN 9 +#define DEFAULT_CS_PIN 10 + /*** * Enable/Disable debug logging diff --git a/libraries/MySensors/MyGateway.cpp b/libraries/MySensors/MyGateway.cpp index b39e0a8..c8848cf 100644 --- a/libraries/MySensors/MyGateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -10,15 +10,21 @@ */ #include "MyGateway.h" +#include "utility/MsTimer2.h" +#include "utility/PinChangeInt.h" -MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : MySensor(_cepin, _cspin) { - ledMode = false; - inclusionTime = _inclusion_time; -} +uint8_t pinRx; +uint8_t pinTx; +uint8_t pinEr; +boolean buttonTriggeredInclusion; +volatile uint8_t countRx; +volatile uint8_t countTx; +volatile uint8_t countErr; +boolean inclusionMode; // Keeps track on inclusion mode + MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er) : MySensor(_cepin, _cspin) { - ledMode = true; pinInclusion = _inclusion_pin; inclusionTime = _inclusion_time; pinRx = _rx; @@ -26,6 +32,7 @@ MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, ui pinEr = _er; } + void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) { Serial.begin(BAUD_RATE); repeaterMode = true; @@ -47,25 +54,23 @@ void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e da countTx = 0; countErr = 0; - if (ledMode) { - // Setup led pins - pinMode(pinRx, OUTPUT); - pinMode(pinTx, OUTPUT); - pinMode(pinEr, OUTPUT); - digitalWrite(pinRx, LOW); - digitalWrite(pinTx, LOW); - digitalWrite(pinEr, LOW); + // Setup led pins + pinMode(pinRx, OUTPUT); + pinMode(pinTx, OUTPUT); + pinMode(pinEr, OUTPUT); + digitalWrite(pinRx, LOW); + digitalWrite(pinTx, LOW); + digitalWrite(pinEr, LOW); - // Setup digital in that triggers inclusion mode - pinMode(pinInclusion, INPUT); - digitalWrite(pinInclusion, HIGH); + // Setup digital in that triggers inclusion mode + pinMode(pinInclusion, INPUT); + digitalWrite(pinInclusion, HIGH); - // Set initial state of leds - digitalWrite(pinRx, HIGH); - digitalWrite(pinTx, HIGH); - digitalWrite(pinEr, HIGH); + // Set initial state of leds + digitalWrite(pinRx, HIGH); + digitalWrite(pinTx, HIGH); + digitalWrite(pinEr, HIGH); - } // Start up the radio library setupRadio(paLevel, channel, dataRate); @@ -73,21 +78,24 @@ void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e da RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID); RF24::startListening(); + // Add led timer interrupt + MsTimer2::set(300, ledTimersInterrupt); + MsTimer2::start(); + + // Add interrupt for inclusion button to pin + PCintPort::attachInterrupt(pinInclusion, startInclusionInterrupt, RISING); + // Send startup log message on serial - serial(PSTR("0;0;%d;0;%d;Arduino startup complete.\n"), C_INTERNAL, I_LOG_MESSAGE); - + serial(PSTR("0;0;%d;0;%d;Gateway startup complete.\n"), C_INTERNAL, I_GATEWAY_READY); } -boolean MyGateway::isLedMode() { - return ledMode; -} - -void MyGateway::startInclusionInterrupt() { +void startInclusionInterrupt() { buttonTriggeredInclusion = true; } + void MyGateway::checkButtonTriggeredInclusion() { if (buttonTriggeredInclusion) { // Ok, someone pressed the inclusion button on the gateway @@ -243,7 +251,7 @@ void MyGateway::serial(MyMessage &msg) { } -void MyGateway::ledTimersInterrupt() { +void ledTimersInterrupt() { if(countRx && countRx != 255) { // switch led on digitalWrite(pinRx, LOW); diff --git a/libraries/MySensors/MyGateway.h b/libraries/MySensors/MyGateway.h index e527bc3..21a93d6 100644 --- a/libraries/MySensors/MyGateway.h +++ b/libraries/MySensors/MyGateway.h @@ -14,8 +14,8 @@ #include "MySensor.h" -#define MAX_RECEIVE_LENGTH 100 // Max buffersize needed for messages coming from vera -#define MAX_SEND_LENGTH 120 // Max buffersize needed for messages coming from vera +#define MAX_RECEIVE_LENGTH 100 // Max buffersize needed for messages coming from controller +#define MAX_SEND_LENGTH 120 // Max buffersize needed for messages destined for controller class MyGateway : public MySensor { @@ -36,43 +36,27 @@ class MyGateway : public MySensor * @param _er Digital pin for error led * */ - MyGateway(uint8_t _cepin=9, uint8_t _cspin=10, uint8_t _inclusion_time = 1); - MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er); + MyGateway(uint8_t _cepin=DEFAULT_CE_PIN, uint8_t _cspin=DEFAULT_CS_PIN, uint8_t _inclusion_time = 1, uint8_t _inclusion_pin = 3, uint8_t _rx=6, uint8_t _tx=5, uint8_t _er=4); /* Use this and pass a function that should be called when you want to process commands that arrive from radio network */ void begin(rf24_pa_dbm_e paLevel=RF24_PA_LEVEL_GW, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE, void (*dataCallback)(char *)=NULL); void processRadioMessage(); void parseAndSend(char *inputString); - boolean isLedMode(); - void ledTimersInterrupt(); - void startInclusionInterrupt(); private: char convBuf[MAX_PAYLOAD*2+1]; char serialBuffer[MAX_SEND_LENGTH]; // Buffer for building string when sending data to vera unsigned long inclusionStartTime; - boolean inclusionMode; // Keeps track on inclusion mode - boolean buttonTriggeredInclusion; - volatile uint8_t countRx; - volatile uint8_t countTx; - volatile uint8_t countErr; - boolean ledMode; boolean useWriteCallback; void (*dataCallback)(char *); - - uint8_t pinInclusion; uint8_t inclusionTime; - uint8_t pinRx; - uint8_t pinTx; - uint8_t pinEr; uint8_t h2i(char c); void serial(const char *fmt, ... ); void serial(MyMessage &msg); - void interruptStartInclusion(); void checkButtonTriggeredInclusion(); void setInclusionMode(boolean newMode); void checkInclusionFinished(); @@ -82,6 +66,7 @@ class MyGateway : public MySensor void errBlink(uint8_t cnt); }; - +void ledTimersInterrupt(); +void startInclusionInterrupt(); #endif diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 729dacd..bc7a086 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -39,7 +39,7 @@ typedef enum { V_IMPEDANCE, V_ARMED, V_TRIPPED, V_WATT, V_KWH, V_SCENE_ON, V_SCENE_OFF, V_HEATER, V_HEATER_SW, 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_DUST_LEVEL + V_DUST_LEVEL, V_VOLTAGE, V_CURRENT } data; // Type of internal messages (for internal messages) @@ -47,7 +47,7 @@ typedef enum { I_BATTERY_LEVEL, I_TIME, I_VERSION, I_ID_REQUEST, I_ID_RESPONSE, I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE, I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION, - I_REBOOT + I_REBOOT, I_GATEWAY_READY } internal; // Type of sensor (for presentation message) diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 45544ba..f6dbba1 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -88,10 +88,10 @@ class MySensor : public RF24 * * Creates a new instance of Sensor class. * - * @param _cepin The pin attached to RF24 Chip Enable on the RF module (defualt 9) + * @param _cepin The pin attached to RF24 Chip Enable on the RF module (default 9) * @param _cspin The pin attached to RF24 Chip Select (default 10) */ - MySensor(uint8_t _cepin=9, uint8_t _cspin=10); + MySensor(uint8_t _cepin=DEFAULT_CE_PIN, uint8_t _cspin=DEFAULT_CS_PIN); /** * Begin operation of the MySensors library diff --git a/libraries/MsTimer2/MsTimer2.cpp b/libraries/MySensors/utility/MsTimer2.cpp similarity index 100% rename from libraries/MsTimer2/MsTimer2.cpp rename to libraries/MySensors/utility/MsTimer2.cpp diff --git a/libraries/MsTimer2/MsTimer2.h b/libraries/MySensors/utility/MsTimer2.h similarity index 100% rename from libraries/MsTimer2/MsTimer2.h rename to libraries/MySensors/utility/MsTimer2.h diff --git a/libraries/PinChangeInt/PinChangeInt.h b/libraries/MySensors/utility/PinChangeInt.h similarity index 100% rename from libraries/PinChangeInt/PinChangeInt.h rename to libraries/MySensors/utility/PinChangeInt.h diff --git a/libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.cpp b/libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.cpp deleted file mode 100644 index db49dce..0000000 --- a/libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - ByteBuffer.cpp - A circular buffer implementation for Arduino - Created by Sigurdur Orn, July 19, 2010. - siggi@mit.edu - Updated by GreyGnome (aka Mike Schwager) Thu Feb 23 17:25:14 CST 2012 - added the putString() method and the fillError variable. - added the checkError() and resetError() methods. The checkError() method resets the fillError variable - to false as a side effect. - added the ByteBuffer(unsigned int buf_size) constructor. - added the init() method, and had the constructor call it automagically. - Also made the capacity, position, length, and fillError variables volatile, for safe use by interrupts. - */ - -#include "ByteBuffer.h" - -void ByteBuffer::init(){ - ByteBuffer::init(DEFAULTBUFSIZE); -} - -void ByteBuffer::init(unsigned int buf_length){ - data = (byte*)malloc(sizeof(byte)*buf_length); - capacity = buf_length; - position = 0; - length = 0; - fillError=false; -} - -void ByteBuffer::deAllocate(){ - free(data); -} - -void ByteBuffer::clear(){ - position = 0; - length = 0; -} - -void ByteBuffer::resetError(){ - fillError=false; -} - -boolean ByteBuffer::checkError(){ - /* - if (fillError) { - Serial.print("E: checkError: length "); - Serial.println(length, DEC); - } - */ - - boolean result=fillError; - fillError=false; - return(result); -} - -int ByteBuffer::getSize(){ - return length; -} - -int ByteBuffer::getCapacity(){ - return capacity; -} - -byte ByteBuffer::peek(unsigned int index){ - byte b = data[(position+index)%capacity]; - return b; -} - -uint8_t ByteBuffer::put(byte in){ - if(length < capacity){ - // save data byte at end of buffer - data[(position+length) % capacity] = in; - // increment the length - length++; - return 1; - } - // return failure - //Serial.print("E: put: "); - //Serial.println(length, DEC); - fillError=true; - return 0; -} - - -uint8_t ByteBuffer::putString(char *in){ - uint8_t count=0; - char *inString; - - inString=in; - uint8_t oldSREG = SREG; cli(); - while(length <= capacity){ - if (length == capacity) { - fillError=true; - return count; - } - // save data byte at end of buffer - data[(position+length) % capacity] = *inString; - // increment the length - length++; - inString++; - count++; - if (*inString == 0) { - if (count==0) fillError=true; // Serial.println("E: putString"); }; - SREG = oldSREG; // Restore register; reenables interrupts - return count; - } - } - SREG = oldSREG; // Restore register; reenables interrupts - return count; -} - -uint8_t ByteBuffer::putInFront(byte in){ - uint8_t oldSREG = SREG; cli(); - if(length < capacity){ - // save data byte at end of buffer - if( position == 0 ) - position = capacity-1; - else - position = (position-1)%capacity; - data[position] = in; - // increment the length - length++; - SREG = oldSREG; // Restore register; reenables interrupts - return 1; - } - // return failure - //Serial.println("E: putInFront"); - fillError=true; - SREG = oldSREG; // Restore register; reenables interrupts - return 0; -} - -byte ByteBuffer::get(){ - uint8_t oldSREG = SREG; cli(); - byte b = 0; - - if(length > 0){ - b = data[position]; - // move index down and decrement length - position = (position+1)%capacity; - length--; - } - SREG = oldSREG; // Restore register; reenables interrupts - return b; -} - -byte ByteBuffer::getFromBack(){ - byte b = 0; - if(length > 0){ - uint8_t oldSREG = SREG; cli(); - b = data[(position+length-1)%capacity]; - length--; - SREG = oldSREG; // Restore register; reenables interrupts - } - - return b; -} - -// -// Ints -// - -void ByteBuffer::putIntInFront(int in){ - byte *pointer = (byte *)∈ - putInFront(pointer[0]); - putInFront(pointer[1]); -} - -void ByteBuffer::putInt(int in){ - byte *pointer = (byte *)∈ - put(pointer[1]); - put(pointer[0]); -} - - -int ByteBuffer::getInt(){ - int ret; - byte *pointer = (byte *)&ret; - pointer[1] = get(); - pointer[0] = get(); - return ret; -} - -int ByteBuffer::getIntFromBack(){ - int ret; - byte *pointer = (byte *)&ret; - pointer[0] = getFromBack(); - pointer[1] = getFromBack(); - return ret; -} - -// -// Longs -// - -void ByteBuffer::putLongInFront(long in){ - byte *pointer = (byte *)∈ - putInFront(pointer[0]); - putInFront(pointer[1]); - putInFront(pointer[2]); - putInFront(pointer[3]); -} - -void ByteBuffer::putLong(long in){ - byte *pointer = (byte *)∈ - put(pointer[3]); - put(pointer[2]); - put(pointer[1]); - put(pointer[0]); -} - - -long ByteBuffer::getLong(){ - long ret; - byte *pointer = (byte *)&ret; - pointer[3] = get(); - pointer[2] = get(); - pointer[1] = get(); - pointer[0] = get(); - return ret; -} - -long ByteBuffer::getLongFromBack(){ - long ret; - byte *pointer = (byte *)&ret; - pointer[0] = getFromBack(); - pointer[1] = getFromBack(); - pointer[2] = getFromBack(); - pointer[3] = getFromBack(); - return ret; -} - - -// -// Floats -// - -void ByteBuffer::putFloatInFront(float in){ - byte *pointer = (byte *)∈ - putInFront(pointer[0]); - putInFront(pointer[1]); - putInFront(pointer[2]); - putInFront(pointer[3]); -} - -void ByteBuffer::putFloat(float in){ - byte *pointer = (byte *)∈ - put(pointer[3]); - put(pointer[2]); - put(pointer[1]); - put(pointer[0]); -} - -float ByteBuffer::getFloat(){ - float ret; - byte *pointer = (byte *)&ret; - pointer[3] = get(); - pointer[2] = get(); - pointer[1] = get(); - pointer[0] = get(); - return ret; -} - -float ByteBuffer::getFloatFromBack(){ - float ret; - byte *pointer = (byte *)&ret; - pointer[0] = getFromBack(); - pointer[1] = getFromBack(); - pointer[2] = getFromBack(); - pointer[3] = getFromBack(); - return ret; -} - - diff --git a/libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.h b/libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.h deleted file mode 100644 index 8916e58..0000000 --- a/libraries/PinChangeInt/Examples/ByteBuffer/ByteBuffer.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - ByteBuffer.h - A circular buffer implementation for Arduino - Created by Sigurdur Orn, July 19, 2010. siggi@mit.edu - Updated by GreyGnome (aka Mike Schwager) Thu Feb 23 17:25:14 CST 2012 - added the putString() method and the fillError variable. - added the checkError() and resetError() methods. The checkError() method resets the fillError variable - to false as a side effect. - added the ByteBuffer(unsigned int buf_size) constructor. - added the init() method, and had the constructor call it automagically. - protected certain sections of the code with cli()/sei() calls, for safe use by interrupts. - Also made the capacity, position, length, and fillError variables volatile, for safe use by interrupts. - */ - -#ifndef ByteBuffer_h -#define ByteBuffer_h - -#if defined(ARDUINO) && ARDUINO >= 100 - #include -#else - #include -#endif -//#include - -#define DEFAULTBUFSIZE 32 -class ByteBuffer -{ -public: - ByteBuffer() { - init(); - }; - ByteBuffer(unsigned int buf_size) { - init(buf_size); - }; - - // This method initializes the datastore of the buffer to a certain size. - void init(unsigned int buf_size); - - // This method initializes the datastore of the buffer to the default size. - void init(); - - // This method resets the buffer into an original state (with no data) - void clear(); - - // This method resets the fillError variable to false. - void resetError(); - - // This method tells you if your buffer overflowed at some time since the last - // check. The error state will be reset to false. - boolean checkError(); - - // This releases resources for this buffer, after this has been called the buffer should NOT be used - void deAllocate(); - - // Returns how much space is used in the buffer - int getSize(); - - // Returns the maximum capacity of the buffer - int getCapacity(); - - // This method returns the byte that is located at index in the buffer but doesn't modify the buffer like the get methods (doesn't remove the retured byte from the buffer) - byte peek(unsigned int index); - - // - // Put methods, either a regular put in back or put in front - // - uint8_t putInFront(byte in); - uint8_t put(byte in); - uint8_t putString(char *in); - - void putIntInFront(int in); - void putInt(int in); - - void putLongInFront(long in); - void putLong(long in); - - void putFloatInFront(float in); - void putFloat(float in); - - // - // Get methods, either a regular get from front or from back - // - byte get(); - byte getFromBack(); - - int getInt(); - int getIntFromBack(); - - long getLong(); - long getLongFromBack(); - - float getFloat(); - float getFloatFromBack(); - -private: - byte* data; - - volatile unsigned int capacity; - volatile unsigned int position; - volatile unsigned int length; - volatile boolean fillError; -}; - -#endif - diff --git a/libraries/PinChangeInt/Examples/GetPSTR/GetPSTR.h b/libraries/PinChangeInt/Examples/GetPSTR/GetPSTR.h deleted file mode 100644 index aaab06a..0000000 --- a/libraries/PinChangeInt/Examples/GetPSTR/GetPSTR.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef INCLUDE_GETPSTR -#define INCLUDE_GETPSTR - -#if defined(ARDUINO) && ARDUINO >= 100 - #include -#else - #include "pins_arduino.h" - #include "WProgram.h" - #include "wiring.h" -#endif - -#define getPSTR(s) pgmStrToRAM(PSTR(s)) - -char *_pstr_to_print; -char *pgmStrToRAM(PROGMEM char *theString) { - free(_pstr_to_print); - _pstr_to_print=(char *) malloc(strlen_P(theString)); - strcpy_P(_pstr_to_print, theString); - return (_pstr_to_print); -} -#endif diff --git a/libraries/PinChangeInt/Examples/PinChangeIntExample/PinChangeIntExample.pde b/libraries/PinChangeInt/Examples/PinChangeIntExample/PinChangeIntExample.pde deleted file mode 100644 index 2f254d4..0000000 --- a/libraries/PinChangeInt/Examples/PinChangeIntExample/PinChangeIntExample.pde +++ /dev/null @@ -1,77 +0,0 @@ -// PinChangeIntExample, version 1.1 Sun Jan 15 06:24:19 CST 2012 -// See the Wiki at http://code.google.com/p/arduino-pinchangeint/wiki for more information. -//-------- define these in your sketch, if applicable ---------------------------------------------------------- -// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts -// on any one or two of the three ports. If only a single port remains, the handler will be declared inline -// reducing the size and latency of the handler. -//#define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts -//#define NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts -// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts -// if there is only one PCInt vector in use the code can be inlined -// reducing latency and code size -// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation. -// #define DISABLE_PCINT_MULTI_SERVICE -//-------- define the above in your sketch, if applicable ------------------------------------------------------ -#include - -// This example demonstrates a configuration of 3 interrupting pins and 2 interrupt functions. -// All interrupts are serviced immediately, but one of the pins (pin 4) will show you immediately -// on the Terminal. The other function connected to 2 pins sets an array member that is queried in loop(). -// You can then query the array at your leisure. -// This makes loop timing non-critical. - -// Add more Pins at your leisure. -// For the Analog Input pins used as digital input pins, and you can use 14, 15, 16, etc. -// or you can use A0, A1, A2, etc. (the Arduino code comes with #define's -// for the Analog Input pins and will properly recognize e.g., pinMode(A0, INPUT); -#define PIN1 2 -#define PIN2 3 -#define PIN3 4 - -uint8_t latest_interrupted_pin; -uint8_t interrupt_count[20]={0}; // 20 possible arduino pins -void quicfunc() { - latest_interrupted_pin=PCintPort::arduinoPin; - interrupt_count[latest_interrupted_pin]++; -}; - -// You can assign any number of functions to any number of pins. -// How cool is that? -void pin3func() { - Serial.print("Pin "); Serial.print(PIN3, DEC); Serial.println("!"); -} - -void setup() { - pinMode(PIN1, INPUT); digitalWrite(PIN1, HIGH); - PCintPort::attachInterrupt(PIN1, &quicfunc, FALLING); // add more attachInterrupt code as required - pinMode(PIN2, INPUT); digitalWrite(PIN2, HIGH); - PCintPort::attachInterrupt(PIN2, &quicfunc, FALLING); - pinMode(PIN3, INPUT); digitalWrite(PIN3, HIGH); - PCintPort::attachInterrupt(PIN3, &pin3func, CHANGE); - Serial.begin(115200); - Serial.println("---------------------------------------"); -} - -uint8_t i; -void loop() { - uint8_t count; - Serial.print("."); - delay(1000); - for (i=0; i < 20; i++) { - if (interrupt_count[i] != 0) { - count=interrupt_count[i]; - interrupt_count[i]=0; - Serial.print("Count for pin "); - if (i < 14) { - Serial.print("D"); - Serial.print(i, DEC); - } else { - Serial.print("A"); - Serial.print(i-14, DEC); - } - Serial.print(" is "); - Serial.println(count, DEC); - } - } -} - diff --git a/libraries/PinChangeInt/Examples/PinChangeIntSpeedTest/PinChangeIntSpeedTest.pde b/libraries/PinChangeInt/Examples/PinChangeIntSpeedTest/PinChangeIntSpeedTest.pde deleted file mode 100644 index f0ac0f2..0000000 --- a/libraries/PinChangeInt/Examples/PinChangeIntSpeedTest/PinChangeIntSpeedTest.pde +++ /dev/null @@ -1,265 +0,0 @@ -// PinChangeIntSpeedTest by GreyGnome aka Mike Schwager. Version numbers here refer to this sketch. -// Version 1.0 - initial version -// Version 1.1 - added code to test digitalRead() -// Version 1.2 - added new comments for the #define's for the NO_PORTx_PINCHANGES. -// Version 1.3 - includes cbiface.h with ooPinChangeInt, rather than cb.h -// Version 1.4 - testing version 2.10Beta with robtillaart's optimization -// Also added a #define/#undef INLINE_PCINTFUNC for inlining of the function called by the interrupt. -// Default: #undef for using the function as per usual. Changed PCIVERSION so that -// ooPinChangeInt starts at 1000 instead of 200. Modified the "Start" message to show "Start..", pause -// for 1 second, show "*\n" (where \n is a newline), pause for 1 second, then run the test. -// Version 1.4 - made this compatible with version 1.5 of PinChangeInt -// Version 1.5 - modified it to use #define OOPCIVERSION for ooPinChangeInt - -// This version number is for ooPinChangeInt -//#define OOPCIVERSION 1030 -#ifndef OOPCIVERSION -#define PCIVERSION 217 // 110 if using PinChangeInt-1.1, 120 for version 1.2 - // 1000 for ooPinChangeIntversion 1.00, 1001 for ooPinChangeInt version 1.01, etc. -#endif - -//-------- define these in your sketch, if applicable ---------------------------------------------------------- -// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts -// on any one or two of the three ports. If only a single port remains, the handler will be declared inline -// reducing the size and latency of the handler. -#undef NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts -#undef NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts -// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts -// You can reduce the code size by 20-50 bytes, and you can speed up the interrupt routine -// slightly by declaring that you don't care if the static variables PCintPort::pinState and/or -// PCintPort::arduinoPin are set and made available to your interrupt routine. -// #define NO_PIN_STATE // to indicate that you don't need the pinState -// #define NO_PIN_NUMBER // to indicate that you don't need the arduinoPin -// if there is only one PCInt vector in use the code can be inlined -// reducing latency and code size -// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation. -//#define DISABLE_PCINT_MULTI_SERVICE -//-------- define the above in your sketch, if applicable ------------------------------------------------------ -#if defined(OOPCIVERSION) - #define LIBRARYUNDERTEST "ooPinChangeInt" - #include - #if PCIVERSION == 1001 - #include - #else - #include - #endif -#else - #define LIBRARYUNDERTEST "PinChangeInt" - #include -#endif - -#define SERIALSTUFF // undef to take out all serial statements. Default: #define for measuring time. -#undef MEMTEST // undef to take out memory tests. Default: #undef for measuring time. -#undef INLINE_PCINTFUNC // define to inline the function called from the interrupt. This should have no effect, - // because the compiler will store the registers upon calling the interrupt routine, just - // like calling a function. Still, we test all assumptions. -//----------------------- -// NOTE: BECAUSE OF COLLISIONS in these libraries, you CANNOT have both libraries: PinChangeInt -// and ooPinChangeInt in the libraries directory at the same time. That said, under UNIX-y operating -// systems, it's easy to move the library directory to a name such as "PinChangeInt-1.3", which the -// Arduino will not recognize, and then create a symbolic link when you want to use a library. Such as: -// cd ~/Documents/Arduino/libaries -// mv PinChangeInt PinChangeInt-1.30 -// mv ooPinChangeInt ooPinChangeInt-1.00 -// ln -s PinChangeInt-1.30 PinChangeInt - -#undef FLASH // to flash LED on pin 13 during test - -#ifdef MEMTEST -#include -#endif - -#define TEST 6 - -#if TEST == 1 -#define PTEST 2 // pin to trigger interrupt. pins 0 and 1 are used -#define PLOW 2 // by Serial, so steer clear of them! -#define PHIGH 2 // Interrupts are attached to these pins - -#elif TEST == 2 // see the #if TEST == 2 || TEST == 3 code, below -#define PTEST 2 -#define PLOW 2 -#define PHIGH 2 // need to attachInterrupt to 5 in the code - -#elif TEST == 3 // see the #if TEST == 2 || TEST == 3 code, below -#define PTEST 5 -#define PLOW 2 -#define PHIGH 2 // need to attachInterrupt to 5 in the code - -#elif TEST == 4 -#define PTEST 2 -#define PLOW 2 -#define PHIGH 5 - -#elif TEST == 5 -#define PTEST 3 -#define PLOW 2 -#define PHIGH 5 - -#elif TEST == 6 -#define PTEST 4 -#define PLOW 2 -#define PHIGH 5 - -#elif TEST == 7 -#define PTEST 5 -#define PLOW 2 -#define PHIGH 5 -#endif - -uint8_t qf0; - -#ifdef INLINE_PCINTFUNC -#define INLINE_PCINTFUNC inline -#else -#define INLINE_PCINTFUNC -#endif -INLINE_PCINTFUNC void quicfunc(); -void quicfunc() { - qf0=TCNT0; -} - -#if defined(OOPCIVERSION) -class speedy : public CallBackInterface -{ - public: - uint8_t id; - static uint8_t var0; - speedy () { id=0; }; - speedy (uint8_t _i): id(_i) {}; - - void cbmethod() { - speedy::var0=TCNT0; - //Serial.print("Speedy method "); // debugging - //Serial.println(id, DEC); - }; -}; -uint8_t speedy::var0=0; -#endif - -volatile uint8_t *led_port; -volatile uint8_t *pinT_OP; -volatile uint8_t *pinT_IP; -uint8_t led_mask, not_led_mask; -uint8_t pinT_M, not_pinT_M; -volatile uint8_t pintest, pinIntLow, pinIntHigh; -uint8_t totalpins; -#if defined(OOPCIVERSION) -speedy speedster[8]={speedy(0), speedy(1), speedy(2), speedy(3), speedy(4), speedy(5), speedy(6), speedy(7) }; -#endif -#ifdef MEMTEST -int freemem; -#endif - -int i=0; - -#define PINLED 13 -void setup() -{ -#ifdef SERIALSTUFF - Serial.begin(115200); Serial.println("---------------------------------------"); -#endif // SERIALSTUFF - // set up ports for trigger - pinMode(0, OUTPUT); digitalWrite(0, HIGH); - pinMode(1, OUTPUT); digitalWrite(1, HIGH); - pinMode(2, OUTPUT); digitalWrite(2, HIGH); - pinMode(3, OUTPUT); digitalWrite(3, HIGH); - pinMode(4, OUTPUT); digitalWrite(4, HIGH); - pinMode(5, OUTPUT); digitalWrite(5, HIGH); - pinMode(6, OUTPUT); digitalWrite(6, HIGH); - pinMode(7, OUTPUT); digitalWrite(7, HIGH); -#ifdef FLASH - led_port=portOutputRegister(digitalPinToPort(PINLED)); - led_mask=digitalPinToBitMask(PINLED); - not_led_mask=led_mask^0xFF; - pinMode(PINLED, OUTPUT); digitalWrite(PINLED, LOW); -#endif - // ***************************************************************************** - // set up ports for output ************ PIN TO TEST IS GIVEN HERE ************** - // ***************************************************************************** - pintest=PTEST; - pinIntLow=PLOW; pinIntHigh=PHIGH; // Interrupts are attached to these pins - // ***************************************************************************** - // ***************************************************************************** - pinT_OP=portOutputRegister(digitalPinToPort(pintest)); // output port - pinT_IP=portInputRegister(digitalPinToPort(pintest)); // input port - pinT_M=digitalPinToBitMask(pintest); // mask - not_pinT_M=pinT_M^0xFF; // not-mask - *pinT_OP|=pinT_M; - for (i=pinIntLow; i <= pinIntHigh; i++) { -#if defined(OOPCIVERSION) - PCintPort::attachInterrupt(i, &speedster[i], CHANGE); // C++ technique; v1.3 or better -#endif -#if defined(PCIVERSION) - PCintPort::attachInterrupt((uint8_t) i, &quicfunc, CHANGE); // C technique; v1.2 or earlier -#endif - } -#if TEST == 2 || TEST == 3 - i=5; totalpins=2; -#if defined(OOPCIVERSION) - PCintPort::attachInterrupt(i, &speedster[i], CHANGE); // C++ technique; v1.3 or better -#endif -#if defined(PCIVERSION) - PCintPort::attachInterrupt(i, &quicfunc, CHANGE); // C technique; v1.2 or earlier -#endif -#else - totalpins=pinIntHigh - pinIntLow + 1; -#endif - i=0; -} // end setup() - -uint8_t k=0; -unsigned long milliStart, milliEnd, elapsed; -void loop() { - k=0; - *pinT_OP|=pinT_M; // pintest to 1 -#ifdef SERIALSTUFF - Serial.print(LIBRARYUNDERTEST); Serial.print(" "); - Serial.print("TEST: "); Serial.print(TEST, DEC); Serial.print(" "); -#ifndef MEMTEST - Serial.print("test pin mask: "); Serial.print(pinT_M, HEX); - Serial.print(". Total of "); Serial.print(totalpins, DEC); Serial.println(" pins enabled."); -#endif -#ifdef MEMTEST - freemem=freeMemory(); Serial.print("Free memory: "); Serial.println(freemem, DEC); -#endif -#endif - delay(1000); - Serial.print("Start.."); - delay(1000); Serial.print("*"); - #ifdef FLASH - *led_port|=led_mask; - #endif - milliStart=millis(); - while (k < 10) { - i=0; - while (i < 10000) { - *pinT_OP&=not_pinT_M; // pintest to 0 ****************************** 16.8 us - *pinT_OP|=pinT_M; // pintest to 1 ****************************** ...to get here - i++; - } - k++; - } - milliEnd=millis(); - #ifdef FLASH - *led_port&=not_led_mask; - #endif - elapsed=milliEnd-milliStart; - #ifndef MEMTEST - Serial.print(" Elapsed: "); - Serial.println(elapsed, DEC); - #endif - #ifdef SERIALSTUFF - Serial.print("Interrupted pin: "); - #if defined(OOPCIVERSION) - Serial.println(speedster[pintest].id, DEC); - #else - Serial.println(PCintPort::arduinoPin, DEC); - #endif -#ifdef MEMTEST - freemem=freeMemory(); Serial.print("END-Free memory: "); Serial.println(freemem, DEC); -#endif -#endif - delay(500); -} - diff --git a/libraries/PinChangeInt/Examples/PinChangeIntTest/PinChangeIntTest.pde b/libraries/PinChangeInt/Examples/PinChangeIntTest/PinChangeIntTest.pde deleted file mode 100644 index c8df127..0000000 --- a/libraries/PinChangeInt/Examples/PinChangeIntTest/PinChangeIntTest.pde +++ /dev/null @@ -1,344 +0,0 @@ -// PinChangeIntTest -// version 1.0 Wed Feb 15 07:25:09 CST 2012 -// Version 1.1 Fri Jun 22 19:10:50 CDT 2012 minor tweaks to eliminate compiler warnings. Also, there were bugfixes in ByteBuffer. -// I had some "cli()" without "sei()" in there. -// See the Wiki at http://code.google.com/p/arduino-pinchangeint/wiki for more information. -// This sketch requires the ByteBuffer library, which is found in the PinChangeInt zipfile. -//-------- define these in your sketch, if applicable ---------------------------------------------------------- -//-------- This must go ahead of the #include statement -------------------------------------------------------- -// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts -// on any one or two of the three ports. If only a single port remains, the handler will be declared inline -// reducing the size and latency of the handler. -// #define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts -// #define NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts -// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts -// You can reduce the code size by 20-50 bytes, and you can speed up the interrupt routine -// slightly by declaring that you don't care if the static variables PCintPort::pinState and/or -// PCintPort::arduinoPin are set and made available to your interrupt routine. -// #define NO_PIN_STATE // to indicate that you don't need the pinState -// #define NO_PIN_NUMBER // to indicate that you don't need the arduinoPin -// if there is only one PCInt vector in use the code can be inlined -// reducing latency and code size -// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation. -// #define DISABLE_PCINT_MULTI_SERVICE -// The following is intended for testing purposes. If defined, then a variable PCintPort::pinMode can be read -// in your interrupt subroutine. It is not defined by default: -// #define PINMODE -//-------- define the above in your sketch, if applicable ------------------------------------------------------ -#define PINMODE -#define FLASH -#include -#include - -// This example demonstrates a configuration of 6 interrupting pins and 3 interrupt functions. -// A variety of interrupting pins have been chosen, so as to test all PORTs on the Arduino. -// The pins are as follows: -#define tPIN1 2 // port D -#define tPIN2 3 -#define tPIN3 11 // Port B -#define tPIN4 12 -#define tPIN5 A3 // Port C, also can be given as "17" -#define tPIN6 A4 // starts and stops the count -// All pins send interrupts. Arduino pins 2 and A4 (tPIN1,6) interrupt on FALLING. -// Arduino pins 3 and 12 (tPIN2,4) interrupt on RISING. -// Arduino pins 11 and A3 (tPIN5) interrupts on CHANGE. -// quicfunc0 is attached to Arduino pins 2, 3, 11, and 12 (tPIN1-4) -// quicfunc1 is attached to Arduino pin A3 (tPIN5) -// quicfunc2 is attached to Arduino pin A4 (tPIN6). -// NOTE: -// For the Analog Input pins used as digital input pins, you can use numbers such as 14, 15, 16, etc. -// or you can use A0, A1, A2, etc. (the Arduino code comes with #define's for the Analog Input pin -// names and will properly recognize e.g., pinMode(A0, INPUT)); - -// HOW IT WORKS -// The interrupt on Arduino pin A4 (tPIN6) will, when triggered, start the counting of interrupts. -// The array interrupt_count0[20] is updated in the interrupts; each cell keeps track of the number -// of interrupts on one of the 20 available interrupt pins on the Arduino. Every second in the main -// loop the array is scanned and registered interrupts are reported for all pins interrupted since -// the previous second. If no interrupts, the output is quiet. - -// tPIN6 is special. Not only does it start the counting of the interrups, but it turns on and off -// interrupts on pins 2, 11, and A3/17 (tPIN1, tPIN3, tPIN5). All pins start by interrupting, but after -// the count is turned on and then turned off, the 3 pins are detached from interrupts. -// Everytime thereafter when the count is turned off the 3 pins are detached. They are reattached -// when turned on. - -// Output is copied to a buffer, because we can't do a Serial.print() statement in an interrupt -// routine. The main loop checks for entries in the buffer and prints them if found. -// Output looks like this: -// -F- - an interrupt triggered by a falling signal occurred. -// +R+ - an interrupt triggered by a rising signal occurred. -// *C* - an interrupt triggered by a change in signal occurred. -// f#p#-P# - f# shows the interrupt subroutine that was called: 0, 1, or 2 -// - p# shows the pin number that triggered the interrupt -// - P# shows the port that this pin number is attached to. 2 is PORTB, 3 is PORTC, 4 is PORTD - -// HOW TO CONNECT -// Each pin gets a momentary contact switch connected to it. One side of the switch should connect -// to ground. The other side of the switch connects to the Arduino pin. For my purposes, I am using -// two rotary encoders. Each encoder contains 3 switches. But 6 regular pushbuttons would work, too. - -/* WHAT TO LOOK FOR - Output is sent to the serial line, so the Arduino IDE's serial terminal should be opened. - Upon startup, press tPINS1-5. You will see output like this: --F-f0p2-P4 (counting off) -..*C*f0p11-P2 (counting off) -+R+f0p3-P4 (counting off) - This shows that - 1. an interrupt was triggered on a falling signal (*F*). It called (f0) function 0, which is quicfunc0. - The triggering pin was (p2) Arduuino pin 2, which is on (P4) Port 4 (PORTD). Counting of this interrupt is - off, so you will not see any output from the main loop. - 2. Two dots appeared. Dots came from iterations of loop(), so these 2 dots show that the two interrupts happened 2 seconds apart. - 3. an interrupt was triggered on a change in signal (*C*). It called quicfunc0, from Arduino pin 11, on Port 2 (PORTB). - The interrupt was not counted. - 4. an interrupt was triggered on a rising signal (+R+). It called quicfunc0, from Arduino pin 3, on Purt 4 (PORTD). - The pin should have started out at the high level, so likely the signal fell during onother interrupt, and now - the rise has been caught. - - Now press the button attached to tPIN6 (in our case, A4 or D18). You will see something like this: --F-START! f2p18-P3 -.Count for pin A4 is 1 - This shows that - 1. The counting machanism (START!) was triggered by a folling signal (-F-) on pin 18 (p18) which is in Port 3 (P3) (which == PORTC) and - function f2 was called (f2). - 2. A dot appeared, which came from loop() because a second passed. - 3. The count for p18 or A4 was displayed. - - Now you will see messages for all the pins that you manipulate, for example: -*C*f0p11-P2 -+R+f0p3-P4 -*C*f0p11-P2 -+R+f0p3-P4 -*C*f0p11-P2 -.Count for pin D3 is 6 -Count for pin D11 is 9 -.+R+f0p3-P4 --F-f0p2-P4 -.Count for pin D2 is 1 -Count for pin D3 is 1 - These codes reflect the interrupts, as described above. This output will take place until you press tPIN6: --F-f2: STOP! Counting off. -Interrupt OFF on tPIN1 (2) tPIN3 (11) tPIN5 (17) - Then you will see output like this: -.....................+R+f0p12-P2 (counting off) -.+R+f0p12-P2 (counting off) -+R+f0p12-P2 (counting off) -+R+f0p12-P2 (counting off) - and tPIN1, tPIN3, and tPIN5 will not trigger interrupts. -*/ -// NOTES -// Output overwrites: -// It's possible during moderately fast interrupts to see your print output get garbled; eg, -// +R+f0p12-P2 (+R+f0p12-P2 (counting +R+f0p12-P2 (cou+R+f0p12-P+R+f0p12 -// This is because the print of the buffer takes place inside a while loop, and it can -// be interrupted and new data inserted into the buffer at a midpoint of the buffer's text. -// Just by spinning my rotary encoders I can readily generate over 200 interrupts per second -// on a pin, which is easily fast enough to overrun Serial output at 115,200 bps. -// The lesson here? ...Interrupts are tricky, and interrupt service routines should be fast. -// Just sayin'. - -// Pins: -// We want to use pins from each of ports B, C and D. So choose wisely. Ports are shown in -// this diagram of the ATmega328P chip. PD0 means "Port D, pin 0". PC3 means "Port C, Pin 3", -// PB2 means "Port B, pin 2" and so on. The corresponding Arduino pins are in parentheses. -// So PB2 is Arduino pin D 10, for example. -/* - +-\/-+ - PC6 1| |28 PC5 (AI 5) - (D 0) PD0 2| |27 PC4 (AI 4) - (D 1) PD1 3| |26 PC3 (AI 3) - (D 2) PD2 4| |25 PC2 (AI 2) - PWM+ (D 3) PD3 5| |24 PC1 (AI 1) - (D 4) PD4 6| |23 PC0 (AI 0) - VCC 7| |22 GND - GND 8| |21 AREF - PB6 9| |20 AVCC - PB7 10| |19 PB5 (D 13) - PWM+ (D 5) PD5 11| |18 PB4 (D 12) - PWM+ (D 6) PD6 12| |17 PB3 (D 11) PWM - (D 7) PD7 13| |16 PB2 (D 10) PWM - (D 8) PB0 14| |15 PB1 (D 9) PWM - +----+ -*/ - -uint8_t pins[6]={ tPIN1, tPIN2, tPIN3, tPIN4, tPIN5, tPIN6 }; -uint8_t ports[6]={ 0, 0, 0, 0, 0, 0 }; - -uint8_t latest_interrupted_pin; -uint8_t interrupt_count[20]={0}; // 20 possible arduino pins -uint8_t port; -uint8_t mode; - -ByteBuffer printBuffer(80); -char charArray[16]; -char numBuffer[4] = { 0, 0, 0, 0 }; -uint8_t printFull=0; - -volatile boolean start=0; -volatile boolean initial=true; -long begintime=0; -long now=0; - -void uint8ToString(char *outString, uint8_t number) { - uint8_t hundreds=0; - uint8_t tens=0; - uint8_t ones=0; - - while (number >= 100 ) { - hundreds++; - number-=100; - } - while (number >= 10 ) { - tens++; - number-=10; - } - ones=number; - ones+=48; - if (hundreds > 0) { hundreds+=48; tens+=48; outString[0]=hundreds; outString[1]=tens; outString[2]=ones; outString[3]=0; } - else if (tens > 0) { tens+=48; outString[0]=tens; outString[1]=ones; outString[2]=0; } - else { outString[0]=ones; outString[1]=0; }; -} - -void showMode() { - switch (mode) { - case FALLING: - printBuffer.putString((char *) "-F-"); - break; - case RISING: - printBuffer.putString((char *) "+R+"); - break; - case CHANGE: - printBuffer.putString((char *) "*C*"); - break; - } -} - -void quicfunc0() { - latest_interrupted_pin=PCintPort::arduinoPin; - mode=PCintPort::pinmode; - showMode(); - if (start==1) { - interrupt_count[latest_interrupted_pin]++; - } - uint8ToString(numBuffer, latest_interrupted_pin); - printBuffer.putString((char *) "f0p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P"); - uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin)); - printBuffer.putString(numBuffer); - if (start !=1) printBuffer.putString((char *) " (counting off)"); - printBuffer.putString((char *) "\n"); -}; - -void quicfunc1() { - latest_interrupted_pin=PCintPort::arduinoPin; - mode=PCintPort::pinmode; - showMode(); - if (start==1) { - interrupt_count[latest_interrupted_pin]++; - } - uint8ToString(numBuffer, latest_interrupted_pin); - printBuffer.putString((char *) "f1p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P"); - uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin)); - printBuffer.putString(numBuffer); - if (start !=1) printBuffer.putString((char *) " (counting off)"); - printBuffer.putString((char *) "\n"); -}; - -void quicfunc2() { - latest_interrupted_pin=PCintPort::arduinoPin; - mode=PCintPort::pinmode; - showMode(); - if (start == 1) { - printBuffer.putString((char *) "f2: STOP! Counting off.\n"); - printBuffer.putString((char *) "Interrupt OFF on tPIN1 ("); uint8ToString(numBuffer, tPIN1), printBuffer.putString(numBuffer); - printBuffer.putString((char *) ") tPIN3 (");uint8ToString(numBuffer, tPIN3), printBuffer.putString(numBuffer); - printBuffer.putString((char *) ") tPIN5 (");uint8ToString(numBuffer, tPIN5), printBuffer.putString(numBuffer); - printBuffer.putString((char *) ")\n"); - PCintPort::detachInterrupt(tPIN1); PCintPort::detachInterrupt(tPIN3); PCintPort::detachInterrupt(tPIN5); - start=0; - } else { - start=1; - interrupt_count[latest_interrupted_pin]++; - printBuffer.putString((char *) "START! f2p"); - uint8ToString(numBuffer, latest_interrupted_pin); - printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P"); - uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin)); - printBuffer.putString(numBuffer); printBuffer.putString((char *) "\n"); - if (! initial) { - PCintPort::attachInterrupt(tPIN1, &quicfunc0, FALLING); - PCintPort::attachInterrupt(tPIN3, &quicfunc0, CHANGE); - PCintPort::attachInterrupt(tPIN5, &quicfunc1, CHANGE); - } else { - initial=false; - } - } -}; - -uint8_t i; -void setup() { - Serial.begin(115200); - delay(250); - Serial.println("Test"); - delay(500); - for (i=0; i < 7; i++) { - pinMode(pins[i], INPUT); digitalWrite(pins[i], HIGH); - ports[i]=digitalPinToPort(pins[i]); - switch (pins[i]) { - case tPIN1: - PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING); - break; - case tPIN3: - PCintPort::attachInterrupt(pins[i], &quicfunc0, CHANGE); - break; - case tPIN2: - case tPIN4: - PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING); - break; - case tPIN5: - PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE); - break; - case tPIN6: - PCintPort::attachInterrupt(pins[i], &quicfunc2, FALLING); - break; - } - } - //Serial.println(printBuffer.getCapacity(), DEC); - //Serial.println("*---------------------------------------*"); - Serial.print("*---*"); - delay(250); - begintime=millis(); -} - -void loop() { - now=millis(); - uint8_t count; - char outChar; - // uint8_t bufsize; - //if (printBuffer.getSize() != 0) { Serial.print("SZ:"); Serial.println (printBuffer.getSize(), DEC); }; - //bufsize=printBuffer.getSize(); - //if (bufsize > 0) { Serial.print("S:"); Serial.println(bufsize); } - while ((outChar=(char)printBuffer.get()) != 0) Serial.print(outChar); - if ((now - begintime) > 1000) { - Serial.print("."); - if (printBuffer.checkError()) { - Serial.println("NOTICE: Some output lost due to filled buffer."); - } - for (i=0; i < 20; i++) { - if (interrupt_count[i] != 0) { - count=interrupt_count[i]; - interrupt_count[i]=0; - Serial.print("Count for pin "); - if (i < 14) { - Serial.print("D"); - Serial.print(i, DEC); - } else { - Serial.print("A"); - Serial.print(i-14, DEC); - } - Serial.print(" is "); - Serial.println(count, DEC); - } - } - begintime=millis(); - } -} - diff --git a/libraries/PinChangeInt/Examples/PinChangeIntTest2/PinChangeIntTest2.ino b/libraries/PinChangeInt/Examples/PinChangeIntTest2/PinChangeIntTest2.ino deleted file mode 100644 index aec34c4..0000000 --- a/libraries/PinChangeInt/Examples/PinChangeIntTest2/PinChangeIntTest2.ino +++ /dev/null @@ -1,275 +0,0 @@ -//#define DISABLE_PCINT_MULTI_SERVICE -#define PINMODE -#define FLASH -#include -#include -#include - -// This example demonstrates a configuration of 6 interrupting pins and 3 interrupt functions. -// A variety of interrupting pins have been chosen, so as to test all PORTs on the Arduino. -// The pins are as follows: -#define tPIN1 2 // port D -#define tPIN2 3 -#define tPIN3 11 // Port B -#define tPIN4 12 -#define tPIN5 A3 // Port C, also can be given as "17" -#define tPIN6 A4 // starts and stops the count - -uint8_t pins[6]={ tPIN1, tPIN2, tPIN3, tPIN4, tPIN5, tPIN6 }; -uint8_t ports[6]={ 0, 0, 0, 0, 0, 0 }; - -uint8_t latest_interrupted_pin; -uint8_t interrupt_count[20]={0}; // 20 possible arduino pins -uint8_t port; -uint8_t mode; - -ByteBuffer printBuffer(200); -char charArray[16]; -char numBuffer[5] = { 0, 0, 0, 0, 0 }; -uint8_t printFull=0; - -volatile boolean start=0; -volatile boolean initial=true; -long begintime=0; -long now=0; - -void uint8ToHexString(char *outString, uint8_t theByte) { - outString[0]='0'; outString[1]='x'; - uint8_t hinybble=theByte>>4; - uint8_t lonybble=theByte & 0x0F; - if (hinybble < 0x0a) outString[2]=hinybble+48; - else outString[2]=hinybble+55; - if (lonybble < 0x0a) outString[3]=lonybble+48; - else outString[3]=lonybble+55; - outString[4]=0; -} - -void uint8ToString(char *outString, uint8_t number) { - uint8_t hundreds=0; - uint8_t tens=0; - uint8_t ones=0; - - while (number >= 100 ) { - hundreds++; - number-=100; - } - while (number >= 10 ) { - tens++; - number-=10; - } - ones=number; - ones+=48; - if (hundreds > 0) { hundreds+=48; tens+=48; outString[0]=hundreds; outString[1]=tens; outString[2]=ones; outString[3]=0; } - else if (tens > 0) { tens+=48; outString[0]=tens; outString[1]=ones; outString[2]=0; } - else { outString[0]=ones; outString[1]=0; }; -} - -void showMode() { - switch (mode) { - case FALLING: - printBuffer.putString(getPSTR("-F-")); - break; - case RISING: - printBuffer.putString(getPSTR("+R+")); - break; - case CHANGE: - printBuffer.putString(getPSTR("*C*")); - break; - } -} - -/* -void quicfunc0() { - latest_interrupted_pin=PCintPort::arduinoPin; - mode=PCintPort::pinmode; - showMode(); - if (start==1) { - interrupt_count[latest_interrupted_pin]++; - } - uint8ToString(numBuffer, latest_interrupted_pin); - printBuffer.putString((char *) "f0p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P"); - uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin)); - printBuffer.putString(numBuffer); - if (start !=1) printBuffer.putString(getPSTR(" no count")); - printBuffer.putString((char *) "\n"); -}; - -void quicfunc1() { - latest_interrupted_pin=PCintPort::arduinoPin; - mode=PCintPort::pinmode; - showMode(); - if (start==1) { - interrupt_count[latest_interrupted_pin]++; - } - uint8ToString(numBuffer, latest_interrupted_pin); - printBuffer.putString(getPSTR("f1p")); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P"); - uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin)); - printBuffer.putString(numBuffer); - if (start !=1) printBuffer.putString(getPSTR(" (counting off)")); - printBuffer.putString((char *) "\n"); -}; -*/ - -void quicfunc2() { - //*led_port|=led_mask; - //*led_port&=not_led_mask; // 2 micros to here (ie, 2 micros used to push registers and call subroutine) - latest_interrupted_pin=PCintPort::arduinoPin; - mode=PCintPort::pinmode; - showMode(); - *led_port|=led_mask; // 73 micros to get here from above. Used in "Rigol Timing Example" - *led_port&=not_led_mask; - //uint8ToString(numBuffer, PCintPort::s_count); printBuffer.putString(numBuffer); - *led_port|=led_mask; // 73 micros to get here from above. Second pulse in "Rigol Timing Example" - *led_port&=not_led_mask; - printBuffer.putString(getPSTR(" f2: P"));/* - uint8ToHexString(numBuffer, *portInputRegister(3)); printBuffer.putString(numBuffer);// C port - printBuffer.putString(getPSTR(" pin:")); uint8ToString(numBuffer, latest_interrupted_pin); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" c")); uint8ToHexString(numBuffer, PCintPort::curr); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" l")); uint8ToHexString(numBuffer, PCintPort::s_lastPinView); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" r")); uint8ToHexString(numBuffer, PCintPort::s_portRisingPins); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" f")); uint8ToHexString(numBuffer, PCintPort::s_portFallingPins); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" m")); uint8ToHexString(numBuffer, PCintPort::s_pmask); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" P")); printBuffer.put(PCintPort::s_PORT); printBuffer.putString("\r\n"); - printBuffer.putString(getPSTR("cp")); uint8ToHexString(numBuffer, PCintPort::s_changedPins); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" cXORlpv")); uint8ToHexString(numBuffer, PCintPort::s_currXORlastPinView); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" rp_nCurr")); uint8ToHexString(numBuffer, PCintPort::s_portRisingPins_nCurr); printBuffer.putString(numBuffer); - printBuffer.putString(getPSTR(" fp_nNCurr")); uint8ToHexString(numBuffer, PCintPort::s_portFallingPins_nNCurr); printBuffer.putString(numBuffer); - */printBuffer.putString("\r\n"); - if (PCintPort::pcint_multi > 0) { - printBuffer.putString("MULTI!\n"); PCintPort::pcint_multi=0; - } - if (PCintPort::PCIFRbug > 0) { printBuffer.putString("ERROR: BUG- PCIFR should be reset!"); PCintPort::PCIFRbug=0; } - //s_registers, if it existed, could be used to keep a running queue of the latest interrupts that have - //been serviced by the PCint(). But generally I don't think it's necessary for debugging at this point (famous last words?) - /*if (PCintPort::s_count > 2) { - for (uint8_t i=0; i < PCintPort::s_count; i++) { - uint8ToHexString(numBuffer, PCintPort::s_registers[i]); printBuffer.putString(numBuffer); printBuffer.putString(" "); - } - } - PCintPort::s_count=0;*/ - /* - if (start == 1) { - printBuffer.putString(getPSTR("STOP Count off\n")); - printBuffer.putString(getPSTR("Intr OFF: (")); uint8ToString(numBuffer, tPIN1), printBuffer.putString(numBuffer); - printBuffer.putString((char *) " "); uint8ToString(numBuffer, tPIN3), printBuffer.putString(numBuffer); - printBuffer.putString((char *) " "); uint8ToString(numBuffer, tPIN5), printBuffer.putString(numBuffer); - printBuffer.putString((char *) ")\n"); - PCintPort::detachInterrupt(tPIN1); PCintPort::detachInterrupt(tPIN3); PCintPort::detachInterrupt(tPIN5); - start=0; - } else { - start=1; - interrupt_count[latest_interrupted_pin]++; - printBuffer.putString(getPSTR("START! p")); - uint8ToString(numBuffer, latest_interrupted_pin); - printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P"); - // MIKE put the REAL PORT HERE - uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin)); - printBuffer.putString(numBuffer); printBuffer.putString((char *) "\n"); - if (! initial) { - PCintPort::attachInterrupt(tPIN1, &quicfunc0, FALLING); - PCintPort::attachInterrupt(tPIN3, &quicfunc0, CHANGE); - PCintPort::attachInterrupt(tPIN5, &quicfunc1, CHANGE); - } else { - initial=false; - } - }*/ -}; - -uint8_t i; -char hexBuffer[5]; -void setup() { - int8_t returncode=1; - Serial.begin(115200); - Serial.println("Test"); - delay(500); - for (i=5; i < 6; i++) { - pinMode(pins[i], INPUT); digitalWrite(pins[i], HIGH); - ports[i]=digitalPinToPort(pins[i]); - switch (pins[i]) { - /*case tPIN1: - #if PCINT_VERSION > 2100 - returncode=PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING); - #else - PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING); - #endif - Serial.println(getPSTR("FIRST FAILURE OK.")); - break; - case tPIN3: - #if PCINT_VERSION > 2100 - returncode=PCintPort::attachInterrupt(pins[i], &quicfunc0, CHANGE); - #else - PCintPort::attachInterrupt(pins[i], &quicfunc0, CHANGE); - #endif - break; - case tPIN2: - case tPIN4: - #if PCINT_VERSION > 2100 - returncode=PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING); - #else - PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING); - #endif - break; - case tPIN5: - #if PCINT_VERSION > 2100 - returncode=PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE); - #else - PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE); - #endif - break;*/ - case tPIN6: - #if PCINT_VERSION > 2100 - returncode=PCintPort::attachInterrupt(pins[i], &quicfunc2, FALLING); - #else - PCintPort::attachInterrupt(pins[i], &quicfunc2, FALLING); - #endif - break; - } - #if PCINT_VERSION > 2100 - Serial.print(getPSTR("setup(): Interrupt attach ")); - if (returncode != 1) Serial.print(getPSTR("unsuccessful ")); - else Serial.print(getPSTR("GOOD ")); - Serial.print(pins[i], DEC); - Serial.print(getPSTR(":pin, code: ")); Serial.println(returncode, DEC); - #endif - } - //Serial.println(printBuffer.getCapacity(), DEC); - //Serial.println("*---------------------------------------*"); - Serial.print("*---*"); - delay(250); - begintime=millis(); -} - -void loop() { - now=millis(); - uint8_t count; - char outChar; - // uint8_t bufsize; - //if (printBuffer.getSize() != 0) { Serial.print("SZ:"); Serial.println (printBuffer.getSize(), DEC); }; - //bufsize=printBuffer.getSize(); - //if (bufsize > 0) { Serial.print("S:"); Serial.println(bufsize); } - while ((outChar=(char)printBuffer.get()) != 0) Serial.print(outChar); - if ((now - begintime) > 1000) { - Serial.print("."); - if (printBuffer.checkError()) { - Serial.println(getPSTR("!Some output lost due to full buffer!")); - } - for (i=0; i < 20; i++) { - if (interrupt_count[i] != 0) { - count=interrupt_count[i]; - interrupt_count[i]=0; - Serial.print(getPSTR("Count for pin ")); - if (i < 14) { - Serial.print("D"); - Serial.print(i, DEC); - } else { - Serial.print("A"); - Serial.print(i-14, DEC); - } - Serial.print(" is "); - Serial.println(count, DEC); - } - } - begintime=millis(); - } -} - diff --git a/libraries/PinChangeInt/RELEASE_NOTES b/libraries/PinChangeInt/RELEASE_NOTES deleted file mode 100644 index 635e4f8..0000000 --- a/libraries/PinChangeInt/RELEASE_NOTES +++ /dev/null @@ -1,332 +0,0 @@ -****************************************************************************** - - PinChangeInt - ---- RELEASE NOTES --- - -Version 2.19 (beta) Tue Nov 20 07:33:37 CST 2012 -SANGUINO SUPPORT! ...And Mioduino! -...The ATmega644 chip is so cool, how can I not? 4 full ports of Pin Change Interrupt bliss! 32 i/o pins! 64k Flash! 4k RAM! Well I wish I had one. That said, Sanguino users, PLEASE send in your bug or bliss reports! Your interrupt-loving brethren and sistren are depending on you, so I can assure everyone that my changes work on that platform. Thanks. - -Modified the addPin() method to save 12 bytes; thanks again robtilllart! - if (firstPin != NULL) { - tmp=firstPin; - do { - if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0); } - if (tmp->next == NULL) break; - tmp=tmp->next; - } while (true); - } -Also changed the goto in the PCint() loop to be a while/break combination. No change to -code speed, but it looks better and passes the cleanliness "gut check". - -Includes PinChangeIntTest 1.5 sketch. - -...Ooops! Forgot the GetPSTR library, needed for the PinChangeIntTest code! Now it's included. -****************************************************************************** -Version 2.17 (beta) Sat Nov 17 09:46:50 CST 2012 -Another bugfix in the PCINT_MULTI_SERVICE section. I was using sbi(PCIFR, PCICRbit); -I didn't realize that was for I/O ports, and not ATmega registers. -But according to "deprecated.h", -"These macros became obsolete, as reading and writing IO ports can - be done by simply using the IO port name in an expression, and all - bit manipulation (including those on IO ports) can be done using - generic C bit manipulation operators." -So now I do: - PCIFR |= PCICRbit; -****************************************************************************** -Version 2.15 (beta) Sat Nov 17 01:17:44 CST 2012 -Fixed it so that attachInterrupt() will now follow your changes to the user function, -as well as the mode. detachInterrupt() still does not delete the PCintPin object but -at least you can detach and reattach at will, using different modes (RISING, FALLING, -CHANGE) and different functions as you wish. - -****************************************************************************** -Version 2.13 (beta) Mon Nov 12 09:33:06 CST 2012 -SIGNIFICANT BUGFIX release! Significant changes: -1. PCintPort::curr bug. Interrupts that occur rapidly will likely not get serviced properly by PCint(). -2. PCint() interrupt handler optimization. -3. PCIFR port bit set bug fix. -4. Many static variables added for debugging; used only when #define PINMODE is on. -5. detachInterrupt() no longer does a delete(), since that wasn't working anyway. When you detachInterrupt(), the PORT just disables interrupts for that pin; the PCintPin object remains in memory and in the linked list of pins (possibly slowing down your interrupts a couple of micros). You can reenable a detached interrupt- but you must do it within the PinChangeInt library (would anyone ever enable an interrupt on a pin, then disable it, then have need to reenable it but not using the library?). -6. attachInterrupt() now returns a uint8_t value: 1 on successful attach, 0 on successful attach but using an already-enabled pin, and -1 if the new() operator failed to create a PCintPin object. -Also, modified these release notes. - -Details: - -Uncovered a nasty bug, thanks to robtillaart on the Arduino Forums and Andre' Franken who posted to the PinChangeInt groups. This bug was introduced by me when I assigned PCintPort::curr early in the interrupt handler: -ISR(PCINT0_vect) { - PCintPort::curr = portB.portInputReg; - portB.PCint(); -} -Later, in the interrupt handler PCint(), we loop as long as PCIFR indicates a new interrupt wants to be triggered, provided DISABLE_PCINT_MULTI_SERVICE is not defined (it is not by default): -#ifndef DISABLE_PCINT_MULTI_SERVICE - pcifr = PCIFR & PCICRbit; - PCIFR = pcifr; // clear the interrupt if we will process it (no effect if bit is zero) -} while(pcifr); -#endif -...Well. Problem is, if a pin pops up and causes the PCIFR to change, we have to reread the port and look at how it is now! I wasn't doing that before, so if a new interrupt appeared while I was still servicing the old one, odd behavior would take place. For example, an interrupt would register but then the userFunc would not be called upon to service it. The code needs to be: - pcifr = PCIFR & PCICRbit; - PCIFR = pcifr; // clear the interrupt if we will process it (no effect if bit is zero) - PCintPort::curr=portInputReg; // ...Fixed in 2.11beta. -} while(pcifr); - -Also, made the interrupt handler even faster with an optimization from robtillaart to take out the checks for changed pins from the while() loop that steps through the pins: -uint8_t changedPins = (PCintPort::curr ^ lastPinView) & - ((portRisingPins & PCintPort::curr ) | ( portFallingPins & ~PCintPort::curr )); - -...This speedup is offset by more changes in the PCint() handler, which now looks like the following; there are two bug fixes: ----------------------------- -FIX 1: sbi(PCIFR, PCICRbit); -FIX 2: ...the aforementioned PCintPort::curr=portInputReg; -Here's the new code: ----------------------------- - #ifndef DISABLE_PCINT_MULTI_SERVICE - pcifr = PCIFR & PCICRbit; - if (pcifr) { - //if (PCIFR & PCICRbit) { // believe it or not, this adds .6 micros - sbi(PCIFR, PCICRbit); // This was a BUG: PCIFR = pcifr ...And here is the fix. - #ifdef PINMODE - PCintPort::pcint_multi++; - if (PCIFR & PCICRbit) PCintPort::PCIFRbug=1; // PCIFR & PCICRbit should ALWAYS be 0 here! - #endif - PCintPort::curr=portInputReg; // ...Fixed in 2.11beta. - goto loop; // A goto!!! Don't want to look at the portInputReg gratuitously, so the while() will not do. - } - #endif - -Also I added a lot of variables for debugging when PINMODE is defined, for routing out nasty bugs. I may need them in the future... :-( - -Finally, I am not putting newlines in this commentary so I can make it easier to paste online. - -****************************************************************************** -Version 2.11 (beta) Mon Nov 12 09:33:06 CST 2012 -See version 2.13 (beta) above. No change other than tidying up the release notes. -****************************************************************************** - Version 2.01 (beta) Thu Jun 28 12:35:48 CDT 2012 - ...Wow, Version 2! What? Why? - Modified the way that the pin is tested inside the interrupt subroutine (ISR) PCintPort::PCint(), - to make the interrupt quicker and slightly reduce the memory footprint. The interrupt's time is - reduced by 2 microseconds or about 7%. Instead of using the mode variable, two bitmasks are maintained - for each port. One bitmask contains all the pins that are configured to work on RISING signals, the other - on FALLING signals. A pin configured to work on CHANGE signals will appear in both bitmasks of the port. - Then, the test for a change goes like this: - if (thisChangedPin) { - if ((thisChangedPin & portRisingPins & PCintPort::curr ) || - (thisChangedPin & portFallingPins & ~PCintPort::curr )) { - where portRisingPins is the bitmask for the pins configured to interrupt on RISING signals, and - portFallingPins is the bitmask for the pins configured to interrupt on FALLING signals. Each port includes - these two bitmask variables. - - This is a significant change to some core functionality to the library, and it saves an appreciable amount - of time (2 out of 36 or so micros). Hence, the 2.00 designation. - - Tue Jun 26 12:42:20 CDT 2012 - I was officially given permission to use the PCint library: - - Re: PCint library - « Sent to: GreyGnome on: Today at 08:10:33 AM » - « You have forwarded or responded to this message. » - Quote Reply Remove - HI, - Yeah, I wrote the original PCint library. It was a bit of a hack and the new one has better features. - I intended the code to be freely usable. Didn't really think about a license. Feel free to use it in - your code: I hereby grant you permission. - - I'll investigate the MIT license, and see if it is appropriate. - Chris J. Kiick - Robot builder and all around geek. - - Version 1.81 (beta) Tue Jun 19 07:29:08 CDT 2012 - Created the getPCIntVersion function, and its associated GET_PCINT_VERSION preprocessor macro. The version - is a 16-bit int, therefore versions are represented as a 4-digit integer. 1810, then, is the first beta - release of 1.81x series. 1811 would be a bugfix of 1.810. 1820 would be the production release. - - Reversed the order of this list, so the most recent notes come first. - - Made some variables "volatile", because they are changed in the interrupt code. Thanks, Tony Cappellini! - - Added support for the Arduino Mega! Thanks to cserveny...@gmail.com! - NOTE: I don't have a Mega, so I rely on you to give me error (or working) reports! - To sum it up for the Mega: No Port C, no Port D. Instead, you get Port J and Port K. Port B remains. - Port J, however, is practically useless because there is only 1 pin available for interrupts. - Most of the Port J pins are not even connected to a header connector. Caveat Programmer. - - Created a function to report the version of this code. Put this #define ahead of the #include of this file, - in your sketch: - #define GET_PCINT_VERSION - Then you can call - uint16_t getPCIntVersion (); - and it will return a 16-bit integer representation of the version of this library. That is, version 1.73beta - will be reported as "1730". 1.74, then, will return "1740". And so on, for whatever version of the library - this happens to be. The odd number in the 10's position will indicate a beta version, as per usual, and the - number in the 1s place will indicate the beta revision (bugs may necessitate a 1.731, 1.732, etc.). - - Here are some of his notes based on his changes: - Mega and friends are using port B, J and K for interrupts. B is working without any modifications. - - J is mostly useless, because of the hardware UART. I was not able to get pin change notifications from - the TX pin (14), so only 15 left. All other (PORT J) pins are not connected on the Arduino boards. - - K controls Arduino pin A8-A15, working fine. - - 328/168 boards use C and D. So in case the lib is compiled with Mega target, the C and D will be - disabled. Also you cannot see port J/K with other targets. For J and K new flags introduced: - NO_PORTJ_PINCHANGES and NO_PORTK_PINCHANGES. - Maybe we should have PORTJ_PINCHANGES to enable PJ, because they will be most likely unused. - - Enjoy! - - Note: To remain consistent, I have not included PORTJ_PINCHANGES. All ports behave the same, - no matter how trivial those ports may seem... no surprises... - - Version 1.72 Wed Mar 14 18:57:55 CDT 2012 - Release. - - Version 1.71beta Sat Mar 10 12:57:05 CST 2012 - Code reordering: Starting in version 1.3 of this library, I put the code that enables - interrupts for the given pin, and the code that enables Pin Change Interrupts, ahead of actually - setting the user's function for the pin. Thus in the small interval between turning on the - interrupts and actually creating a valid link to an interrupt handler, it is possible to get an - interrupt. At that point the value of the pointer is 0, so this means that the Arduino - will start over again from memory location 0- just as if you'd pressed the reset button. Oops! - - I corrected it so the code now operates in the proper order. - (EDITORIAL NOTE: If you want to really learn something, teach it!) - - Minor code clean-up: All references to PCintPort::curr are now explicit. This changes the compiled - hex code not one whit. I just sleep better at night. - - Numbering: Changed the numbering scheme. Beta versions will end with an odd number in the hundredths - place- because they may be odd- and continue to be marked "beta". I'll just sleep better at night. :-) - - Version 1.70beta Mon Feb 27 07:20:42 CST 2012 - Happy Birthday to me! Happy Birthday tooooo meee! Happy Birthday, Dear Meeeeee-eeeee! - Happy Birthday to me! - - Yes, it is on this auspicious occasion of mine (and Elizabeth Taylor's [R.I.P.]) birthday that I - humbly submit to you, gracious Arduino PinChangeInt user, version 1.70beta of the PinChangeInt - library. I hope you enjoy it. - - New in this release: - The PinChangeIntTest sketch was created, which can be found in the Examples directory. It exercises: - * Two interrupting pins, one on each of the Arduino's PORTs. - * detachInterrupt() (and subsequent attachInterrupt()s). - Hopefully this will help avoid the embarrassing bugs that I have heretofore missed. - - As well, it has come to this author's (GreyGnome) attention that the Serial class in Arduino 1.0 - uses an interrupt that, if you attempt to print from an interrupt (which is what I was doing in my - tests) can easily lock up the Arduino. So I have taken SigurðurOrn's excellent ByteBuffer library - and modified it for my own nefarious purposes. (see http://siggiorn.com/?p=460). The zipfile - comes complete with the ByteBuffer library; see the ByteBuffer/ByteBuffer.h file for a list of - changes, and see the PinChangeIntTest sketch for a usage scenario. Now the (interrupt-less and) - relatively fast operation of filling a circular buffer is used in the interrupt routines. The buffer - is then printed from loop(). - - The library has been modified so it can be used in other libraries, such as my AdaEncoder library - (http://code.google.com/p/adaencoder/). When #include'd by another library you should #define - the LIBCALL_PINCHANGEINT macro. For example: - #ifndef PinChangeInt_h - #define LIBCALL_PINCHANGEINT - #include "../PinChangeInt/PinChangeInt.h" - #endif - This is necessary because the IDE compiles both your sketch and the .cpp file of your library, and - the .h file is included in both places. But since the .h file actually contains the code, any variable - or function definitions would occur twice and cause compilation errors- unless #ifdef'ed out. - - Version 1.6beta Fri Feb 10 08:48:35 CST 2012 - Set the value of the current register settings, first thing in each ISR; e.g., - ISR(PCINT0_vect) { - PCintPort::curr = portB.portInputReg; // version 1.6 - ... - ...instead of at the beginning of the PCintPort::PCint() static method. This means that the port is read - closer to the beginning of the interrupt, and may be slightly more accurate- only by a couple of microseconds, - really, but it's a cheap win. - - Fixed a bug- a BUG!- in the attachInterrupt() and detachInterrupt() methods. I didn't have breaks in my - switch statements! Augh! What am I, a (UNIX) shell programmer? ...Uh, generally, yes... - - Added the PINMODE define and the PCintPort::pinmode variable. - - Version 1.51 Sun Feb 5 23:28:02 CST 2012 - Crap, a bug! Changed line 392 from this: - PCintPort::pinState=curr & changedPins ? HIGH : LOW; - to this: - PCintPort::pinState=curr & p->mask ? HIGH : LOW; - Also added a few lines of (commented-out) debug code. - - Version 1.5 Thu Feb 2 18:09:49 CST 2012 - Added the PCintPort::pinState static variable to allow the programmer to query the state of the pin - at the time of interrupt. - Added two new #defines, NO_PIN_STATE and NO_PIN_NUMBER so as to reduce the code size by 20-50 bytes, - and to speed up the interrupt routine slightly by declaring that you don't care if the static variables - PCintPort::pinState and/or PCintPort::arduinoPin are set and made available to your interrupt routine. - // #define NO_PIN_STATE // to indicate that you don't need the pinState - // #define NO_PIN_NUMBER // to indicate that you don't need the arduinoPin - - Version 1.4 Tue Jan 10 09:41:14 CST 2012 - All the code has been moved into this .h file, so as to allow #define's to work from the user's - sketch. Thanks to Paul Stoffregen from pjrc.com for the inspiration! (Check out his website for - some nice [lots more memory] Arduino-like boards at really good prices. ...This has been an unsolicited - plug. Now back to our regular programming. ...Hehe, "programming", get it?) - - As a result, we no longer use the PinChangeIntConfig.h file. The user must #define things in his/her - sketch. Which is better anyway. - - Removed the pcIntPorts[] array, which created all the ports by default no matter what. Now, only - those ports (PCintPort objects) that you need will get created if you use the NO_PORTx_PINCHANGES #defines. - This saves flash memory, and actually we get a bit of a memory savings anyway even if all the ports are - left enabled. - - The attachInterrupt and detachInterrupt routines were modified to handle the new PCintPort objects. - - Version 1.3 Sat Dec 3 22:56:20 CST 2011 - Significant internal changes: - Tested and modified to work with Arduino 1.0. - - Modified to use the new() operator and symbolic links instead of creating a pre-populated - PCintPins[]. Renamed some variables to simplify or make their meaning more obvious (IMHO anyway). - Modified the PCintPort::PCint() code (ie, the interrupt code) to loop over a linked-list. For - those who love arrays, I have left some code in there that should work to loop over an array - instead. But it is commented out in the release version. - - For Arduino versions prior to 1.0: The new() operator requires the cppfix.h library, which is - included with this package. For Arduino 1.0 and above: new.h comes with the distribution, and - that is #included. - - Version 1.2 Sat Dec 3 Sat Dec 3 09:15:52 CST 2011 - Modified Thu Sep 8 07:33:17 CDT 2011 by GreyGnome. Fixes a bug with the initial port - value. Now it sets the initial value to be the state of the port at the time of - attachInterrupt(). The line is port.PCintLast=port.portInputReg; in attachInterrupt(). - See GreyGnome comment, below. - - Added the "arduinoPin" variable, so the user's function will know exactly which pin on - the Arduino was triggered. - - Version 1.1 Sat Dec 3 00:06:03 CST 2011 - ...updated to fix the "delPin" function as per "pekka"'s bug report. Thanks! - - ---- ^^^ VERSIONS ^^^ (NOTE TO SELF: Update the PCINT_VERSION define, below) ------------- - - See google code project for latest, bugs and info http://code.google.com/p/arduino-pinchangeint/ - For more information Refer to avr-gcc header files, arduino source and atmega datasheet. - - This library was inspired by and derived from "johnboiles" (it seems) - PCInt Arduino Playground example here: http://www.arduino.cc/playground/Main/PcInt - If you are the original author, please let us know at the google code page - - It provides an extension to the interrupt support for arduino by - adding pin change interrupts, giving a way for users to have - interrupts drive off of any pin. - - 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 . diff --git a/libraries/PinChangeInt/gpl.txt b/libraries/PinChangeInt/gpl.txt deleted file mode 100644 index 94a9ed0..0000000 --- a/libraries/PinChangeInt/gpl.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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 . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/libraries/PinChangeInt/keywords.txt b/libraries/PinChangeInt/keywords.txt deleted file mode 100644 index 24e1cec..0000000 --- a/libraries/PinChangeInt/keywords.txt +++ /dev/null @@ -1,10 +0,0 @@ -# LITERAL1 specifies constants - -# KEYWORD1 specifies datatypes and C/C++ keywords -pinState KEYWORD1 PinState -arduinoPin KEYWORD1 ArduinoPin -PCintPort KEYWORD1 PCInterruptPort - -# KEYWORD2 specifies methods and functions -attachInterrupt KEYWORD2 AttachInterrupt -detachInterrupt KEYWORD2 DetachInterrupt From c8c56078b1f3f6a997f72c8090392b941e429575 Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Thu, 21 Aug 2014 16:45:24 +0200 Subject: [PATCH 315/326] Renamed files (init requires file names < 15 characters) and added initial version of readme for e2e setup --- .../{NodeJsController.sh => MySensors.sh} | 0 ...erInitScript.sh => MySensorsInitScript.sh} | 2 +- NodeJsController/Readme.html | 298 ++++++++++++++++++ NodeJsController/package.json | 3 +- 4 files changed, 301 insertions(+), 2 deletions(-) rename NodeJsController/{NodeJsController.sh => MySensors.sh} (100%) rename NodeJsController/{NodeJsControllerInitScript.sh => MySensorsInitScript.sh} (99%) create mode 100644 NodeJsController/Readme.html diff --git a/NodeJsController/NodeJsController.sh b/NodeJsController/MySensors.sh similarity index 100% rename from NodeJsController/NodeJsController.sh rename to NodeJsController/MySensors.sh diff --git a/NodeJsController/NodeJsControllerInitScript.sh b/NodeJsController/MySensorsInitScript.sh similarity index 99% rename from NodeJsController/NodeJsControllerInitScript.sh rename to NodeJsController/MySensorsInitScript.sh index 2c934a5..38a9feb 100644 --- a/NodeJsController/NodeJsControllerInitScript.sh +++ b/NodeJsController/MySensorsInitScript.sh @@ -19,7 +19,7 @@ # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="MySensors NodeJs Controller" -NAME="NodeJsController.sh" +NAME="MySensors.sh" PIDFILE="/usr/local/var/run/$NAME.pid" DAEMON="/home/pi/Arduino/NodeJsController/$NAME" DAEMON_ARGS="$PIDFILE" diff --git a/NodeJsController/Readme.html b/NodeJsController/Readme.html new file mode 100644 index 0000000..a2ca54e --- /dev/null +++ b/NodeJsController/Readme.html @@ -0,0 +1,298 @@ +

MySensors with NodeJS Controller and OTA bootloader on RPi

+

My RPi runs Arduino to compile and flash sketches and the bootloader. With this setup no separate PC/Mac is needed. Exactly the same could be achieved (faster) using a PC/Mac for all the Arduino compilation and flashing activities – just skip the steps and execute on PC/Mac instead. +

Hardware

+

Headless Raspberry Pi model B with wired Ethernet

+

I'm connecting a monitor only for first time boot to get the IP address and then connect via Putty/xRDP

+

Download and install latest Raspbian version on a fresh SD-Card

+

Tutorials for this task are available on the internet (download, copy to SD)

+

Setup base parameters

+

(adjust to your needs - here: RPi-IP: 10.0.1.5 / Router-IP: 10.0.1.1 / password: test / timezone: Europe,Berlin / hostname: mysensors)

+
    +
  • login as pi / raspberry +
  • sudo nano /etc/network/interfaces +

    #iface eth0 inet dhcp
    +iface eth0 inet static
    +address 10.0.1.5
    +netmask 255.255.255.0
    +gateway 10.0.1.1
    +nameserver 10.0.1.1

    +
  • sudo raspi-config +
      +
    • 1. Expand Filesystem +
    • 2. Change User Password
      +

      test

      +
    • 4. Internationalisation Options +
        +
      • I2. Change Timezone +

        Europe / Berlin

        +
      +
    • 8. Advanced Options +
        +
      • A2. Hostname +

        mysensors

        +
      • A3. Memory Split +

        16

        +
      +
    +
  • Finish +
  • Reboot +
+

Install latest updates

+
    +
  • login as pi / test +
  • sudo apt-get update +
  • sudo apt-get upgrade +
  • sudo reboot +
+

Install Remote Desktop

+

Optional… my RPi is headless and I use xRDP to connect from a Windows PC to run a graphical UI if needed (e.g. for Arduino IDE)

+
    +
  • login as pi / test +
  • sudo apt-get install xrdp +
  • to confirm it's working as expected try to connect from a remote computer to the RPi (e.g. from a Windows PC using the "Remote Desktop Connection" tool) +
+

Install NodeJS (here: latest stable version available for RPi: 0.10.28)

+
    +
  • login as pi / test +
  • sudo mkdir /opt/node +
  • wget http://nodejs.org/dist/v0.10.28/node-v0.10.28-linux-arm-pi.tar.gz +
  • tar xvzf node-v0.10.28-linux-arm-pi.tar.gz +
  • rm node-v0.10.28-linux-arm-pi.tar.gz +
  • sudo cp -r node-v0.10.28-linux-arm-pi/* /opt/node +
  • rm -rf node-v0.10.28-linux-arm-pi/ +
  • sudo nano /etc/profile +

    NODE_JS_HOME="/opt/node"
    +PATH="$PATH:$NODE_JS_HOME/bin"
    +export PATH

    +
  • sudo reboot +
  • to confirm it's working as expected login as pi/test and run "node -v" which should return the installed node version (here: v0.10.28) +
+

Install MongoDB

+
    +
  • login as pi / test +
  • sudo useradd -c mongodb -d /home/mongodb -m -U mongodb +
  • sudo mkdir -p /opt/mongo/bin +
  • sudo mkdir /var/lib/mongodb +
  • sudo chown mongodb:nogroup /var/lib/mongodb +
  • sudo mkdir /var/log/mongodb +
  • sudo chown mongodb:nogroup /var/log/mongodb +
  • wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/mongo +
  • wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/mongod +
  • wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/mongodump +
  • wget https://github.com/brice-morin/ArduPi/raw/master/mongodb-rpi/mongo/bin/bsondump +
  • sudo mv mongo /opt/mongo/bin/ +
  • sudo mv mongod /opt/mongo/bin/ +
  • sudo mv mongodump /opt/mongo/bin/ +
  • sudo mv bsondump /opt/mongo/bin/ +
  • sudo chmod -R 775 /opt/mongo/bin +
  • sudo ln -s /opt/mongo/bin/mongod /usr/bin/mongod +
  • wget https://raw.githubusercontent.com/skrabban/mongo-nonx86/master/debian/mongodb.conf +
  • wget https://raw.githubusercontent.com/skrabban/mongo-nonx86/master/debian/init.d +
  • sudo mv mongodb.conf /etc/mongodb.conf +
  • sudo mv init.d /etc/init.d/mongodb +
  • sudo chmod 755 /etc/init.d/mongodb +
  • sudo update-rc.d mongodb defaults +
  • sudo reboot +
  • to confirm it's working as expected login as pi/test and run "/opt/mongo/bin/mongo" which should start the mongo shell (type "exit" to close the shell again) +
  • if the RPi loses power without proper shutdown while mongod is running, you can't restart without manually removing the pid file first. To do so run “sudo rm /var/lib/mongodb/mongod.lock” +
+

Install Arduino (and update to latest 1.0.5)

+
    +
  • login as pi / test +
  • sudo apt-get install arduino +
  • wget http://arduino.googlecode.com/files/arduino-1.0.5-linux32.tgz +
  • tar zxvf arduino-1.0.5-linux32.tgz +
  • rm arduino-1.0.5-linux32.tgz +
  • cd arduino-1.0.5 +
  • rm -rf hardware/tools +
  • sudo cp -ru lib /usr/share/arduino +
  • sudo cp -ru libraries /usr/share/arduino +
  • sudo cp -ru tools /usr/share/arduino +
  • sudo cp -ru hardware /usr/share/arduino +
  • sudo cp -ru examples /usr/share/doc/arduino-core +
  • sudo cp -ru reference /usr/share/doc/arduino-core +
  • cd ~ +
  • rm -rf arduino-1.0.5 +
+

Enable programming via GPIO UART (for serial gateway only)

+
    +
  • login as pi / test +
  • wget https://github.com/SpellFoundry/avrdude-rpi/archive/master.zip +
  • unzip master.zip +
  • rm master.zip +
  • cd avrdude-rpi-master +
  • sudo cp autoreset /usr/bin +
  • sudo cp avrdude-autoreset /usr/bin +
  • sudo mv /usr/bin/avrdude /usr/bin/avrdude-original +
  • sudo ln -s /usr/bin/avrdude-autoreset /usr/bin/avrdude +
  • cd .. +
  • rm -rf avrdude-rpi-master +
+

Install MySensors from Git branch development (here: password: test)

+
    +
  • login as pi / test +
  • git clone https://github.com/mysensors/Arduino.git +
  • cd Arduino +
  • git checkout development +
  • cd NodeJsController +
  • npm install +
  • nano NodeJsController.js (optional : removing sample firmwares) +

    const fwHexFiles = [ ];

    +
  • to confirm that the NodeJsController is working as expected and connects to the local mongo DB: "node NodeJsController.js" which should start the controller, connect to the database and run into an error connecting to the gateway. Stop node again via Ctrl-c +
+

Start NodeJsController on boot

+
    +
  • cd ~/Arduino/NodeJsController +
  • chmod 755 *.sh +
  • sudo cp MySensorsInitScript.sh /etc/init.d/mysensors +
  • sudo mkdir -p /usr/local/var/run +
  • sudo chmod -R 777 /usr/local/var/run +
  • sudo update-rc.d mysensors defaults +
  • sudo reboot +
  • to confirm that NodeJsController is starting at boot, check the log file /home/pi/logs/NodeJsController.log +
+

Configure Arduino

+
    +
  • start graphical UI (startx on RPi or via RDP) +
  • start Arduino IDE +
  • File - Preferences +
      +
    • set sketchbook location to /home/pi/Arduino +
    • enable verbose output for compilation +
    • enable verbose output for upload +
    +
  • Close Arduino IDE +
+ +

Serial Gateway

+

Hardware

+
    +
  • as described for the standard Serial Gateway +
  • I'm using the version with inclusion mode button and LEDs based on no Arduino but a plain ATmega328p on a breadboard +
  • Connect directly to the serial GPIO pins of the RPi: +

    Arduino GND <-> Raspberry Pi GND (pin 6)
    +Arduino RX <-> Raspberry Pi TX (pin 8)
    +Arduino TX <-> Raspberry Pi RX (pin 10)
    +Arduino RESET <-> Raspberry Pi GPIO22 (pin 15)

    +
  • Right now I'm using a separate power supply for the serial gateway instead of the 3.3V or 5V pins of the RPi because I ran in some issues with power consumption during programming +
+

Disable default use of serial port

+
    +
  • sudo nano /etc/inittab +

    #T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

    +
  • sudo nano /boot/cmdline.txt - remove part of the file: +

    console=ttyAMA0,115200

    +
  • cd /dev +
  • sudo ln -s ttyAMA0 ttyUSB9 +
  • sudo apt-get install minicom +
  • sudo reboot +
+

Flash (assuming that the ATmega has a valid Arduino bootloader installed)

+
    +
  • login as pi / test +
  • start Arduino IDE +
      +
    • Tools - Board - Arduino - Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega328 +
    • Tools - Serial Port - /dev/ttyUSB9 +
    • File - Sketchbook - SerialGateway +
    • File - Upload +
    +
+

Setup NodeJsController

+
    +
  • nano ~/Arduino/NodeJsController/NodeJsController.js +

    //const gwType = 'Ethernet';
    +//const gwAddress = '10.0.1.99';
    +//const gwPort = 9999;
    +const gwType = 'Serial';
    +const gwPort = '/dev/ttyAMA0';
    +const gwBaud = 115200;

    +
  • sudo service mysensors restart +
+ +

Ethernet Gateway

+

Hardware

+
    +
  • as described for the standard Ethernet Gateway +
  • I'm using the version with inclusion mode button and LEDs based on no Arduino but a plain ATmega328p on a breadboard and the ENC28J60 Ethernet module +
+

Adjust Sourcecode (here: address 10.0.1.99 / port: 9999)

+
    +
  • [I you are running into compilation errors due to missing files in the utility directory, do the following] +cp -R ~/Arduino/libraries/UIPEthernet/src/utility ~/Arduino/libraries/UIPEthernet/utility +
  • nano ~/Arduino/libraries/MySensors/MyConfig.h +

    //#define DEBUG

    +
  • start Arduino IDE +
      +
    • File - Sketchbook - EthernetGateway +
    • Edit source code +

      #define IP_PORT 9999
      +IPAddress myIp (10, 0, 1, 99);

      +
    • Optional : adjust source code to given hardware +
        +
      • Correct include for Ethernet module type (if not using ENC28J60) +
      • Switch MyGateway constructor if using inclusion mode button and LEDs +
      +
    • File - Save +
    +

    Flash (assuming that the ATmega has a valid Arduino bootloader installed)

    +
      +
    • Connect ethernet gateway to RPi (setup as above for serial gateway or via USB FTDI cable +
    • login as pi / test +
    • start Arduino IDE +
        +
      • Tools - Board - Arduino - Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega328 +
      • Tools - Serial Port - /dev/ttyUSB9 (or /dev/ttyUSB0 if connecting through USB cable) +
      • File - Sketchbook - EthernetGateway +
      • File - Upload +
      +
    +

    Setup NodeJsController

    +
      +
    • nano ~/Arduino/NodeJsController/NodeJsController.js +

      const gwAddress = '10.0.1.99';

      +
    • sudo service mysensors restart +
    + +

    Sensor Node Bootloader

    +

    Hardware

    +
      +
    • as described for a simple Sensor Node +
    • here: no Arduino but a plain ATmega328p on a breadboard +
    • using DallasTemperature example (one sensor connected to pin 3) +
    +

    Compile Bootloader (can be skipped but good to see if everything is working)

    +
      +
    • cd ~/Arduino/Bootloader +
    • make +
    • this should compile the bootloader and produce three files in the same directory: MyOtaBootloader.o / MyOtaBootloader.elf / MyOtaBootloader.hex +
    +

    Flash Bootloader

    +
      +
    • cd ~/Arduino/Bootloader +
    • nano Makefile +
    • adjust avrdude settings to the available programmer. In my case (mySmartUSB MK3 programmer temporarily connected to RPi USB port): +

      ISP_PORT = /dev/ttyUSB0
      +ISP_SPEED = 115200
      +ISP_PROTOCOL = stk500v2
      +ISP_MCU = m328p

      +
    • make load +
    + +

    End-to-End Test

    +

    Add Firmware to Controller

    +
      +
    • nano ~/Arduino/NodeJsController/NodeJsController.js +

      const fwHexFiles = [ 'DallasTemperatureSensor.cpp.hex' ];
      +const fwDefaultType = 0;

      +
    • sudo service mysensors restart +
    +

    Flash Firmware OTA

    +
      +
    • tail -f ~/logs/NodeJsController.log +
    • Power up sensor node with OTA bootloader +
    +

    Check functionality

    +
      +
    • in addition to watching the log file (tail command above) you can connect to the mongo db using a client (e.g. Robomongo) and check if new values are written to the database as a real end-to-end test +
    diff --git a/NodeJsController/package.json b/NodeJsController/package.json index f9ec6ff..a4866a6 100644 --- a/NodeJsController/package.json +++ b/NodeJsController/package.json @@ -3,7 +3,8 @@ "version": "0.0.1", "description": "MySensors Controller based on Node JS", "dependencies": { + "mongodb": "~1.4.7", "net": "~1.0.2", - "mongodb": "~1.4.7" + "serialport": "^1.4.5" } } From e01dc2ecccbc261c4f8811a85e6e640d4d8187bb Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 22 Aug 2014 20:35:22 +0200 Subject: [PATCH 316/326] Fix a few warnings in MQTTGateway. Move all gateway examples to "examples" to work better with codebender and Arduino IDE. Ledmode is always enabled for gateway. Still optional to connect the wires though ;) Move MsTimer to local utility folder (no external dependencies). --- libraries/MySensors/MyMQTT.cpp | 83 +++++++---------- libraries/MySensors/MyMQTT.h | 21 ++--- .../EthernetGateway}/EthernetGateway.ino | 69 ++++---------- .../examples/MQTTGateway}/MQTTGateway.ino | 14 +-- .../RFIDLockSensor/RFIDLockSensor.ino | 8 +- .../examples/SerialGateway/SerialGateway.ino | 89 +++++++++++++++++++ 6 files changed, 150 insertions(+), 134 deletions(-) rename {EthernetGateway => libraries/MySensors/examples/EthernetGateway}/EthernetGateway.ino (79%) rename {MQTTGateway => libraries/MySensors/examples/MQTTGateway}/MQTTGateway.ino (94%) mode change 100755 => 100644 create mode 100644 libraries/MySensors/examples/SerialGateway/SerialGateway.ino diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp index d2a0352..0884906 100755 --- a/libraries/MySensors/MyMQTT.cpp +++ b/libraries/MySensors/MyMQTT.cpp @@ -10,6 +10,9 @@ version 2 as published by the Free Software Foundation. */ #include "MyMQTT.h" +#include "utility/MsTimer2.h" + + char broker[] PROGMEM = MQTT_BROKER_PREFIX; @@ -75,26 +78,19 @@ PROGMEM const char *sType[] = { }; -MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin, uint8_t _rx, uint8_t _tx, uint8_t _er) : +extern volatile uint8_t countRx; +extern volatile uint8_t countTx; +extern volatile uint8_t countErr; +extern uint8_t pinRx; +extern uint8_t pinTx; +extern uint8_t pinEr; + +MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin) : MySensor(_cepin, _cspin) { - if (_rx != NULL) { - pinRx = _rx; - pinMode(pinRx, OUTPUT); - ledMode = true; - } - if (_tx != NULL) { - pinTx = _tx; - pinMode(pinTx, OUTPUT); - ledMode = true; - } - if (_er != NULL) { - pinEr = _er; - pinMode(pinEr, OUTPUT); - ledMode = true; - } + } -void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *, int *)) { +void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(const char *, int *), uint8_t _rx, uint8_t _tx, uint8_t _er) { Serial.begin(BAUD_RATE); repeaterMode = true; isGateway = true; @@ -110,6 +106,18 @@ void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataR RF24::openReadingPipe(WRITE_PIPE, BASE_RADIO_ID); RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID); RF24::startListening(); + + pinRx = _rx; + pinMode(pinRx, OUTPUT); + pinTx = _tx; + pinMode(pinTx, OUTPUT); + pinEr = _er; + pinMode(pinEr, OUTPUT); + + MsTimer2::set(200, ledTimersInterrupt); + MsTimer2::start(); + + // Send startup log message on serial //Serial.print(PSTR("Started\n"));//TODO: progmem gives error..? error: sType causes a section type conflict with __c Serial.print("Started\n");//TODO: fix this... @@ -127,7 +135,7 @@ void MyMQTT::processRadioMessage() { } void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { - char *str, *p, *value=NULL; + char *str, *p; char i = 0; buffer[0]= 0; buffsize = 0; @@ -233,7 +241,7 @@ void MyMQTT::SendMQTT(MyMessage &msg) { } else { // we have to check every message if its a newly assigned id or not. // Ack on I_ID_RESPONSE does not work, and checking on C_PRESENTATION isn't reliable. - char newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; + uint8_t newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID; if (msg.sender==newNodeID) { saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); @@ -259,7 +267,7 @@ void MyMQTT::SendMQTT(MyMessage &msg) { } else if (mGetCommand(msg)==C_PRESENTATION && (msg.type==S_ARDUINO_NODE || msg.type==S_ARDUINO_REPEATER_NODE)) { //Doesnt work to check new sensorID here, this message does not always arrive.. See above. } else if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { - char newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; + unsigned char newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID; if (newNodeID >= MQTT_LAST_SENSORID) return; // Sorry no more id's left :( msg.destination = msg.sender; //NodeID @@ -289,7 +297,7 @@ void MyMQTT::SendMQTT(MyMessage &msg) { Serial.println((char*)&buffer[4]); #endif msg.getString(convBuf); - for (int a=0; a -#include -#include #include #include -#include // Use this if you have attached a Ethernet ENC28J60 shields -//#include // Use this fo WizNET module and Arduino Ethernet Shield +// Use this if you have attached a Ethernet ENC28J60 shields +#include + +// Use this fo WizNET module and Arduino Ethernet Shield +//#include #define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled @@ -99,17 +97,6 @@ void setup() // start listening for clients server.begin(); - - // C++ classes and interrupts really sucks. Need to attach interrupt - // outside thw Gateway class due to language shortcomings! Gah! - - if (gw.isLedMode()) { - // Add led timer interrupt - MsTimer2::set(300, ledTimersInterrupt); - MsTimer2::start(); - // Add interrupt for inclustion button to pin - PCintPort::attachInterrupt(INCLUSION_MODE_PIN, startInclusionInterrupt, RISING); - } } // This will be called when data should be written to ethernet @@ -118,24 +105,22 @@ void writeEthernet(char *writeBuffer) { } -void processEthernetMessages() +void loop() { - // if an incoming client connects, there will be - // bytes available to read via the client object - EthernetClient client = server.available(); + // if an incoming client connects, there will be + // bytes available to read via the client object + EthernetClient client = server.available(); - if (client) - { + if (client) { // if got 1 or more bytes - if (client.available()) - { + if (client.available()) { // read the bytes incoming from the client char inChar = client.read(); if (inputPos #include #include -#include // Use this for IBOARD modded to use standard MISO/MOSI/SCK, see note *1 above! @@ -135,12 +134,8 @@ void processEthernetMessages() { } } -void writeEthernet(char *writeBuffer, int *writeSize) { - server.write(writeBuffer, *writeSize); // Todo: Should this really be *writeSize? -} - -void ledTimersInterrupt() { - gw.ledTimersInterrupt(); +void writeEthernet(const char *writeBuffer, int *writeSize) { + server.write((const uint8_t *)writeBuffer, *writeSize); // Todo: Should this really be *writeSize? } int main(void) { @@ -149,11 +144,6 @@ int main(void) { delay(1000); // Wait for Ethernet to get configured. gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet); server.begin(); - // Add led timer interrupt - if (gw.isLedMode()) { - MsTimer2::set(200, ledTimersInterrupt); - MsTimer2::start(); - } while (1) { processEthernetMessages(); gw.processRadioMessage(); diff --git a/libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino b/libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino index 96f7e25..ecb9756 100644 --- a/libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino +++ b/libraries/MySensors/examples/RFIDLockSensor/RFIDLockSensor.ino @@ -34,9 +34,7 @@ const uint8_t maxKeyLength = 7; uint8_t validKeys[][maxKeyLength] = { { 0xB3, 0xC6, 0xD9, 0x80, 0x00, 0x00, 0x00 }, - { 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, // ADD YOUR KEYS HERE! { 0, 0, 0, 0, 0, 0, 0 }}; int keyCount = sizeof validKeys / maxKeyLength; @@ -84,11 +82,12 @@ void setup() { } void loop() { + gw.process(); // Process incomming messages + boolean success; uint8_t key[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t currentKeyLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) - gw.process(); // Process incomming messages // Wait for an ISO14443A type cards (Mifare, etc.). When one is found // 'uid' will be populated with the UID, and uidLength will indicate @@ -159,3 +158,4 @@ void incomingMessage(const MyMessage &message) { } } + diff --git a/libraries/MySensors/examples/SerialGateway/SerialGateway.ino b/libraries/MySensors/examples/SerialGateway/SerialGateway.ino new file mode 100644 index 0000000..83e9715 --- /dev/null +++ b/libraries/MySensors/examples/SerialGateway/SerialGateway.ino @@ -0,0 +1,89 @@ + /* + * Copyright (C) 2013 Henrik Ekblad + * + * 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. + * + * DESCRIPTION + * The ArduinoGateway prints data received from sensors on the serial link. + * The gateway accepts input on seral which will be sent out on radio network. + * + * The GW code is designed for Arduino Nano 328p / 16MHz + * + * Wire connections (OPTIONAL): + * - Inclusion button should be connected between digital pin 3 and GND + * - RX/TX/ERR leds need to be connected between +5V (anode) and digital ping 6/5/4 with resistor 270-330R in a series + * + * LEDs (OPTIONAL): + * - RX (green) - blink fast on radio message recieved. In inclusion mode will blink fast only on presentation recieved + * - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly + * - ERR (red) - fast blink on error during transmission error or recieve crc error + */ + +#include +#include +#include + +#define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled +#define INCLUSION_MODE_PIN 3 // Digital pin used for inclusion mode button + + +MyGateway gw(DEFAULT_CE_PIN, DEFAULT_CS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4); + +char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface +int inputPos = 0; +boolean commandComplete = false; // whether the string is complete + +void setup() +{ + gw.begin(); +} + +void loop() +{ + gw.processRadioMessage(); + checkSerialInput(); +} + + + +void checkSerialInput() { + if (commandComplete) { + // A command wass issued from serial interface + // We will now try to send it to the actuator + gw.parseAndSend(inputString); + commandComplete = false; + inputPos = 0; + } +} + +/* + SerialEvent occurs whenever a new data comes in the + hardware serial RX. This routine is run between each + time loop() runs, so using delay inside loop can delay + response. Multiple bytes of data may be available. + */ +void serialEvent() { + while (Serial.available()) { + // get the new byte: + char inChar = (char)Serial.read(); + // if the incoming character is a newline, set a flag + // so the main loop can do something about it: + if (inputPos Date: Fri, 22 Aug 2014 20:43:29 +0200 Subject: [PATCH 317/326] Clean up unused libraries --- SerialGateway/SerialGateway.ino | 89 - .../examples/SerialGateway/SerialGateway.ino | 7 +- libraries/RF24/.gitignore | 14 - libraries/RF24/Doxyfile | 1551 ----------------- libraries/RF24/FAQ | 53 - libraries/RF24/README.md | 41 - libraries/RF24/RF24.cpp | 1228 ------------- libraries/RF24/RF24.h | 1133 ------------ libraries/RF24/RF24_config.h | 129 -- libraries/RF24/RPi/RF24/Makefile | 55 - libraries/RF24/RPi/RF24/RF24.cpp | 1145 ------------ libraries/RF24/RPi/RF24/RF24.h | 1041 ----------- libraries/RF24/RPi/RF24/RF24_config.h | 34 - libraries/RF24/RPi/RF24/bcm2835.c | 1399 --------------- libraries/RF24/RPi/RF24/bcm2835.h | 1353 -------------- libraries/RF24/RPi/RF24/examples/Makefile | 40 - .../RF24/RPi/RF24/examples/extra/Makefile | 40 - .../RF24/RPi/RF24/examples/extra/rpi-hub.cpp | 134 -- .../RF24/RPi/RF24/examples/extra/scanner.cpp | 147 -- .../RF24/RPi/RF24/examples/gettingstarted.cpp | 187 -- .../examples/gettingstarted_call_response.cpp | 137 -- .../RF24/RPi/RF24/examples/pingpair_dyn.cpp | 181 -- libraries/RF24/RPi/RF24/examples/transfer.cpp | 148 -- libraries/RF24/RPi/RF24/nRF24L01.h | 127 -- libraries/RF24/RPi/RF24/readme.md | 117 -- libraries/RF24/RPi/readme.md | 117 -- libraries/RF24/doxygen-custom.css | 835 --------- .../GettingStarted/GettingStarted.ino | 136 -- .../RF24/examples/GettingStarted/printf.h | 37 - .../GettingStarted_CallResponse.ino | 124 -- .../GettingStarted_CallResponse/printf.h | 37 - libraries/RF24/examples/Transfer/Transfer.ino | 144 -- libraries/RF24/examples/Transfer/printf.h | 37 - .../TransferTimeouts/TransferTimeouts.ino | 189 -- .../RF24/examples/TransferTimeouts/printf.h | 37 - .../RF24/examples/Usage/led_remote/Jamfile | 206 --- .../examples/Usage/led_remote/led_remote.pde | 255 --- .../RF24/examples/Usage/led_remote/printf.h | 37 - .../RF24/examples/Usage/nordic_fob/Jamfile | 219 --- .../examples/Usage/nordic_fob/nordic_fob.pde | 142 -- .../RF24/examples/Usage/nordic_fob/printf.h | 37 - .../examples/Usage/pingpair_maple/Jamfile | 182 -- .../examples/Usage/pingpair_maple/main.cpp | 87 - .../Usage/pingpair_maple/pingpair_maple.pde | 242 --- .../examples/pingpair_ack/pingpair_ack.ino | 123 -- libraries/RF24/examples/pingpair_ack/printf.h | 37 - libraries/RF24/examples/pingpair_dyn/Jamfile | 206 --- .../examples/pingpair_dyn/pingpair_dyn.ino | 232 --- libraries/RF24/examples/pingpair_dyn/printf.h | 37 - .../examples/pingpair_irq/pingpair_irq.ino | 137 -- libraries/RF24/examples/pingpair_irq/printf.h | 37 - .../pingpair_sleepy/pingpair_sleepy.ino | 225 --- .../RF24/examples/pingpair_sleepy/printf.h | 37 - libraries/RF24/examples/scanner/Jamfile | 210 --- libraries/RF24/examples/scanner/output/core.a | Bin 187630 -> 0 bytes .../RF24/examples/scanner/output/scanner.cpp | 128 -- .../RF24/examples/scanner/output/scanner.elf | Bin 100436 -> 0 bytes .../RF24/examples/scanner/output/scanner.hex | 325 ---- libraries/RF24/examples/scanner/printf.h | 31 - libraries/RF24/examples/scanner/scanner.ino | 126 -- libraries/RF24/examples/starping/Jamfile | 206 --- libraries/RF24/examples/starping/printf.h | 37 - libraries/RF24/examples/starping/starping.pde | 293 ---- libraries/RF24/keywords.txt | 16 - libraries/RF24/nRF24L01.h | 127 -- libraries/RF24/tests/README | 7 - libraries/RF24/tests/native/Jamfile | 300 ---- libraries/RF24/tests/native/pingpair_irq.pde | 223 --- libraries/RF24/tests/native/printf.h | 33 - .../RF24/tests/pingpair_blocking/Jamfile | 219 --- .../pingpair_blocking/pingpair_blocking.pde | 273 --- .../RF24/tests/pingpair_blocking/printf.h | 37 - .../RF24/tests/pingpair_blocking/runtest.py | 25 - .../RF24/tests/pingpair_blocking/runtests.sh | 5 - .../RF24/tests/pingpair_blocking/test.ex | 11 - libraries/RF24/tests/pingpair_test/Jamfile | 219 --- .../tests/pingpair_test/pingpair_test.pde | 435 ----- libraries/RF24/tests/pingpair_test/printf.h | 37 - libraries/RF24/tests/pingpair_test/runtest.py | 25 - .../RF24/tests/pingpair_test/runtests.sh | 21 - libraries/RF24/tests/pingpair_test/test.ex | 11 - libraries/RF24/wikidoc.xslt | 41 - libraries/SimpleFIFO/README | 7 - libraries/SimpleFIFO/SimpleFIFO.h | 89 - .../HelloSimpleFIFO/HelloSimpleFIFO.ino | 27 - libraries/SimpleFIFO/keywords.txt | 25 - 86 files changed, 1 insertion(+), 18269 deletions(-) delete mode 100644 SerialGateway/SerialGateway.ino delete mode 100644 libraries/RF24/.gitignore delete mode 100644 libraries/RF24/Doxyfile delete mode 100644 libraries/RF24/FAQ delete mode 100644 libraries/RF24/README.md delete mode 100644 libraries/RF24/RF24.cpp delete mode 100644 libraries/RF24/RF24.h delete mode 100644 libraries/RF24/RF24_config.h delete mode 100644 libraries/RF24/RPi/RF24/Makefile delete mode 100644 libraries/RF24/RPi/RF24/RF24.cpp delete mode 100644 libraries/RF24/RPi/RF24/RF24.h delete mode 100644 libraries/RF24/RPi/RF24/RF24_config.h delete mode 100644 libraries/RF24/RPi/RF24/bcm2835.c delete mode 100644 libraries/RF24/RPi/RF24/bcm2835.h delete mode 100644 libraries/RF24/RPi/RF24/examples/Makefile delete mode 100644 libraries/RF24/RPi/RF24/examples/extra/Makefile delete mode 100644 libraries/RF24/RPi/RF24/examples/extra/rpi-hub.cpp delete mode 100644 libraries/RF24/RPi/RF24/examples/extra/scanner.cpp delete mode 100644 libraries/RF24/RPi/RF24/examples/gettingstarted.cpp delete mode 100644 libraries/RF24/RPi/RF24/examples/gettingstarted_call_response.cpp delete mode 100644 libraries/RF24/RPi/RF24/examples/pingpair_dyn.cpp delete mode 100644 libraries/RF24/RPi/RF24/examples/transfer.cpp delete mode 100644 libraries/RF24/RPi/RF24/nRF24L01.h delete mode 100644 libraries/RF24/RPi/RF24/readme.md delete mode 100644 libraries/RF24/RPi/readme.md delete mode 100644 libraries/RF24/doxygen-custom.css delete mode 100644 libraries/RF24/examples/GettingStarted/GettingStarted.ino delete mode 100644 libraries/RF24/examples/GettingStarted/printf.h delete mode 100644 libraries/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino delete mode 100644 libraries/RF24/examples/GettingStarted_CallResponse/printf.h delete mode 100644 libraries/RF24/examples/Transfer/Transfer.ino delete mode 100644 libraries/RF24/examples/Transfer/printf.h delete mode 100644 libraries/RF24/examples/TransferTimeouts/TransferTimeouts.ino delete mode 100644 libraries/RF24/examples/TransferTimeouts/printf.h delete mode 100644 libraries/RF24/examples/Usage/led_remote/Jamfile delete mode 100644 libraries/RF24/examples/Usage/led_remote/led_remote.pde delete mode 100644 libraries/RF24/examples/Usage/led_remote/printf.h delete mode 100644 libraries/RF24/examples/Usage/nordic_fob/Jamfile delete mode 100644 libraries/RF24/examples/Usage/nordic_fob/nordic_fob.pde delete mode 100644 libraries/RF24/examples/Usage/nordic_fob/printf.h delete mode 100644 libraries/RF24/examples/Usage/pingpair_maple/Jamfile delete mode 100644 libraries/RF24/examples/Usage/pingpair_maple/main.cpp delete mode 100644 libraries/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde delete mode 100644 libraries/RF24/examples/pingpair_ack/pingpair_ack.ino delete mode 100644 libraries/RF24/examples/pingpair_ack/printf.h delete mode 100644 libraries/RF24/examples/pingpair_dyn/Jamfile delete mode 100644 libraries/RF24/examples/pingpair_dyn/pingpair_dyn.ino delete mode 100644 libraries/RF24/examples/pingpair_dyn/printf.h delete mode 100644 libraries/RF24/examples/pingpair_irq/pingpair_irq.ino delete mode 100644 libraries/RF24/examples/pingpair_irq/printf.h delete mode 100644 libraries/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino delete mode 100644 libraries/RF24/examples/pingpair_sleepy/printf.h delete mode 100644 libraries/RF24/examples/scanner/Jamfile delete mode 100644 libraries/RF24/examples/scanner/output/core.a delete mode 100644 libraries/RF24/examples/scanner/output/scanner.cpp delete mode 100755 libraries/RF24/examples/scanner/output/scanner.elf delete mode 100644 libraries/RF24/examples/scanner/output/scanner.hex delete mode 100644 libraries/RF24/examples/scanner/printf.h delete mode 100644 libraries/RF24/examples/scanner/scanner.ino delete mode 100644 libraries/RF24/examples/starping/Jamfile delete mode 100644 libraries/RF24/examples/starping/printf.h delete mode 100644 libraries/RF24/examples/starping/starping.pde delete mode 100644 libraries/RF24/keywords.txt delete mode 100644 libraries/RF24/nRF24L01.h delete mode 100644 libraries/RF24/tests/README delete mode 100644 libraries/RF24/tests/native/Jamfile delete mode 100644 libraries/RF24/tests/native/pingpair_irq.pde delete mode 100644 libraries/RF24/tests/native/printf.h delete mode 100644 libraries/RF24/tests/pingpair_blocking/Jamfile delete mode 100644 libraries/RF24/tests/pingpair_blocking/pingpair_blocking.pde delete mode 100644 libraries/RF24/tests/pingpair_blocking/printf.h delete mode 100755 libraries/RF24/tests/pingpair_blocking/runtest.py delete mode 100755 libraries/RF24/tests/pingpair_blocking/runtests.sh delete mode 100755 libraries/RF24/tests/pingpair_blocking/test.ex delete mode 100644 libraries/RF24/tests/pingpair_test/Jamfile delete mode 100644 libraries/RF24/tests/pingpair_test/pingpair_test.pde delete mode 100644 libraries/RF24/tests/pingpair_test/printf.h delete mode 100755 libraries/RF24/tests/pingpair_test/runtest.py delete mode 100755 libraries/RF24/tests/pingpair_test/runtests.sh delete mode 100755 libraries/RF24/tests/pingpair_test/test.ex delete mode 100644 libraries/RF24/wikidoc.xslt delete mode 100644 libraries/SimpleFIFO/README delete mode 100644 libraries/SimpleFIFO/SimpleFIFO.h delete mode 100644 libraries/SimpleFIFO/examples/HelloSimpleFIFO/HelloSimpleFIFO.ino delete mode 100644 libraries/SimpleFIFO/keywords.txt diff --git a/SerialGateway/SerialGateway.ino b/SerialGateway/SerialGateway.ino deleted file mode 100644 index 83e9715..0000000 --- a/SerialGateway/SerialGateway.ino +++ /dev/null @@ -1,89 +0,0 @@ - /* - * Copyright (C) 2013 Henrik Ekblad - * - * 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. - * - * DESCRIPTION - * The ArduinoGateway prints data received from sensors on the serial link. - * The gateway accepts input on seral which will be sent out on radio network. - * - * The GW code is designed for Arduino Nano 328p / 16MHz - * - * Wire connections (OPTIONAL): - * - Inclusion button should be connected between digital pin 3 and GND - * - RX/TX/ERR leds need to be connected between +5V (anode) and digital ping 6/5/4 with resistor 270-330R in a series - * - * LEDs (OPTIONAL): - * - RX (green) - blink fast on radio message recieved. In inclusion mode will blink fast only on presentation recieved - * - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly - * - ERR (red) - fast blink on error during transmission error or recieve crc error - */ - -#include -#include -#include - -#define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled -#define INCLUSION_MODE_PIN 3 // Digital pin used for inclusion mode button - - -MyGateway gw(DEFAULT_CE_PIN, DEFAULT_CS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4); - -char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface -int inputPos = 0; -boolean commandComplete = false; // whether the string is complete - -void setup() -{ - gw.begin(); -} - -void loop() -{ - gw.processRadioMessage(); - checkSerialInput(); -} - - - -void checkSerialInput() { - if (commandComplete) { - // A command wass issued from serial interface - // We will now try to send it to the actuator - gw.parseAndSend(inputString); - commandComplete = false; - inputPos = 0; - } -} - -/* - SerialEvent occurs whenever a new data comes in the - hardware serial RX. This routine is run between each - time loop() runs, so using delay inside loop can delay - response. Multiple bytes of data may be available. - */ -void serialEvent() { - while (Serial.available()) { - // get the new byte: - char inChar = (char)Serial.read(); - // if the incoming character is a newline, set a flag - // so the main loop can do something about it: - if (inputPos
  • git checkout development
  • cd NodeJsController
  • npm install -
  • nano NodeJsController.js (optional : removing sample firmwares) -

    const fwHexFiles = [ ];

  • to confirm that the NodeJsController is working as expected and connects to the local mongo DB: "node NodeJsController.js" which should start the controller, connect to the database and run into an error connecting to the gateway. Stop node again via Ctrl-c

Start NodeJsController on boot

@@ -215,7 +212,7 @@ Arduino RESET <-> Raspberry Pi GPIO22 (pin 15)

//const gwType = 'Serial';
//const gwPort = '/dev/ttyAMA0';
//const gwBaud = 115200;

-
  • sudo service mysensors restart +
  • sudo service mysensors restart (at this point stop/restart doesn't work. use "sudo killall node" followed by "sudo service mysensors start" instead)

    Ethernet Gateway

    @@ -262,7 +259,7 @@ IPAddress myIp (10, 0, 1, 99);

    • nano ~/Arduino/NodeJsController/NodeJsController.js

      const gwAddress = '10.0.1.99';

      -
    • sudo service mysensors restart +
    • sudo service mysensors restart (at this point stop/restart doesn't work. use "sudo killall node" followed by "sudo service mysensors start" instead)

    Sensor Node Bootloader

    @@ -291,12 +288,14 @@ ISP_MCU = m328p

    End-to-End Test

    -

    Add Firmware to Controller

    +

    Add Firmware to Controller (here: DallasTemperatureSensor)

      +
    • use Arduino IDE or codebender or whatever other way to compile your sketch +
    • copy the compiled DallasTemperatureSensor.cpp.hex file to the NodeJsController directory (if using the Arduino IDE, enable verbose logging in preferences and then watch the compilation process to find the temp directory where the output is stored)
    • nano ~/Arduino/NodeJsController/NodeJsController.js -

      const fwHexFiles = [ 'DallasTemperatureSensor.cpp.hex' ];
      -const fwDefaultType = 0;

      -
    • sudo service mysensors restart +

      const fwHexFiles = [ 'DallasTemperatureSensor.cpp.hex' ];
      +const fwDefaultType = 0;

      +
    • sudo service mysensors restart (at this point stop/restart doesn't work. use "sudo killall node" followed by "sudo service mysensors start" instead)

    Flash Firmware OTA

      @@ -305,5 +304,5 @@ const fwDefaultType = 0;

    Check functionality

      -
    • in addition to watching the log file (tail command above) you can connect to the mongo db using a client (e.g. Robomongo) and check if new values are written to the database as a real end-to-end test +
    • in addition to watching the log file (tail command above) you can connect to the mongo db using a client (e.g. Robomongo) and check if new values are written to the database as a real end-to-end test once the bootloader finished uploading the firmware
    From 42c82fd3c79bec56ed4256f0fee83d7e0b046f0b Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Mon, 25 Aug 2014 17:38:25 +0200 Subject: [PATCH 321/326] use codebender to compile sketches I had codebender username/password included originally but compiling works without. Codebender usage guidelines allow "usage via API". --- NodeJsController/NodeJsController.js | 179 +++++++++++++++------------ NodeJsController/package.json | 1 + 2 files changed, 103 insertions(+), 77 deletions(-) diff --git a/NodeJsController/NodeJsController.js b/NodeJsController/NodeJsController.js index 53639a2..fbe1470 100644 --- a/NodeJsController/NodeJsController.js +++ b/NodeJsController/NodeJsController.js @@ -1,18 +1,18 @@ -const gwType = 'Ethernet'; -const gwAddress = '10.0.1.99'; -const gwPort = 9999; +//const gwType = 'Ethernet'; +//const gwAddress = '10.0.1.99'; +//const gwPort = 9999; -//const gwType = 'Serial'; +const gwType = 'Serial'; //const gwPort = 'COM4'; -//const gwPort = '/dev/ttyUSB0'; -//const gwBaud = 115200; +const gwPort = '/dev/ttyAMA0'; +const gwBaud = 115200; const dbAddress = '127.0.0.1'; const dbPort = 27017; const dbName = 'MySensorsDb'; -const fwHexFiles = [ ]; -const fwDefaultType = 0xFFFF; // index of hex file from array above (0xFFFF == none defined) +const fwSketches = [ '../libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino' ]; +const fwDefaultType = 0; // index of hex file from array above const FIRMWARE_BLOCK_SIZE = 16; const BROADCAST_ADDRESS = 255; @@ -117,6 +117,8 @@ const P_ULONG32 = 5; const P_CUSTOM = 6; var fs = require('fs'); +var path = require('path'); +var requestify = require('requestify'); var appendedString=""; function crcUpdate(old, value) { @@ -146,76 +148,99 @@ function pushDWord(arr, val) { arr.push((val >> 24) & 0x000000FF); } -function loadFirmware(fwtype, fwversion, filename, db) { - console.log("loading firmware: " + filename); - fwdata = []; - var start = 0; - var end = 0; - var pos = 0; - var hex = fs.readFileSync(filename).toString().split("\n"); - for(l in hex) { - line = hex[l].trim(); - if (line.length > 0) { - while (line.substring(0, 1) != ":") - line = line.substring(1); - var reclen = parseInt(line.substring(1, 3), 16); - var offset = parseInt(line.substring(3, 7), 16); - var rectype = parseInt(line.substring(7, 9), 16); - var data = line.substring(9, 9 + 2 * reclen); - var chksum = parseInt(line.substring(9 + (2 * reclen), 9 + (2 * reclen) + 2), 16); - if (rectype == 0) { - if ((start == 0) && (end == 0)) { - if (offset % 128 > 0) - throw new Error("error loading hex file - offset can't be devided by 128"); - start = offset; - end = offset; +function loadFirmware(fwtype, fwversion, sketch, db) { + var filename = path.basename(sketch); + console.log("compiling firmware: " + filename); + var req = { + files: [{ + filename: filename, + content: fs.readFileSync(sketch).toString() + }], + format: "hex", + version: "105", + build: { + mcu: "atmega328p", + f_cpu: "16000000L", + core: "arduino", + variant: "standard" + } + }; + requestify.post('https://codebender.cc/utilities/compile/', req).then(function(res) { + var body = JSON.parse(res.getBody()); + if (body.success) { + console.log("loading firmware: " + filename); + fwdata = []; + var start = 0; + var end = 0; + var pos = 0; + var hex = body.output.split("\n"); + for(l in hex) { + line = hex[l].trim(); + if (line.length > 0) { + while (line.substring(0, 1) != ":") + line = line.substring(1); + var reclen = parseInt(line.substring(1, 3), 16); + var offset = parseInt(line.substring(3, 7), 16); + var rectype = parseInt(line.substring(7, 9), 16); + var data = line.substring(9, 9 + 2 * reclen); + var chksum = parseInt(line.substring(9 + (2 * reclen), 9 + (2 * reclen) + 2), 16); + if (rectype == 0) { + if ((start == 0) && (end == 0)) { + if (offset % 128 > 0) + throw new Error("error loading hex file - offset can't be devided by 128"); + start = offset; + end = offset; + } + if (offset < end) + throw new Error("error loading hex file - offset lower than end"); + while (offset > end) { + fwdata.push(255); + pos++; + end++; + } + for (var i = 0; i < reclen; i++) { + fwdata.push(parseInt(data.substring(i * 2, (i * 2) + 2), 16)); + pos++; + } + end += reclen; + } } - if (offset < end) - throw new Error("error loading hex file - offset lower than end"); - while (offset > end) { - fwdata.push(255); - pos++; - end++; - } - for (var i = 0; i < reclen; i++) { - fwdata.push(parseInt(data.substring(i * 2, (i * 2) + 2), 16)); - pos++; - } - end += reclen; + } + var pad = end % 128; // ATMega328 has 64 words per page / 128 bytes per page + for (var i = 0; i < 128 - pad; i++) { + fwdata.push(255); + pos++; + end++; } - } - } - var pad = end % 128; // ATMega328 has 64 words per page / 128 bytes per page - for (var i = 0; i < 128 - pad; i++) { - fwdata.push(255); - pos++; - end++; - } - var blocks = (end - start) / FIRMWARE_BLOCK_SIZE; - var crc = 0xFFFF; - for (var i = 0; i < blocks * FIRMWARE_BLOCK_SIZE; ++i) { - var v = crc; - crc = crcUpdate(crc, fwdata[i]); - } - db.collection('firmware', function(err, c) { - c.update({ - 'type': fwtype, - 'version': fwversion - }, { - $set: { - 'filename': filename, - 'blocks': blocks, - 'crc': crc, - 'data': fwdata + var blocks = (end - start) / FIRMWARE_BLOCK_SIZE; + var crc = 0xFFFF; + for (var i = 0; i < blocks * FIRMWARE_BLOCK_SIZE; ++i) { + var v = crc; + crc = crcUpdate(crc, fwdata[i]); } - }, { - upsert: true - }, function(err, result) { - if (err) - console.log('Error writing firmware to database'); - }); - }); - console.log("loading firmware done. blocks: " + blocks + " / crc: " + crc); + db.collection('firmware', function(err, c) { + c.update({ + 'type': fwtype, + 'version': fwversion + }, { + $set: { + 'filename': filename, + 'blocks': blocks, + 'crc': crc, + 'data': fwdata + } + }, { + upsert: true + }, function(err, result) { + if (err) + console.log('Error writing firmware to database'); + }); + }); + console.log("loading firmware done. blocks: " + blocks + " / crc: " + crc); + } else { + console.log("error: %j", res.body); + } + }); } /* @@ -645,8 +670,8 @@ dbc.connect('mongodb://' + dbAddress + ':' + dbPort + '/' + dbName, function(err db.createCollection('firmware', function(err, collection) { }); // ToDo : check for new hex files / only load if new / get type and version from filename - for (var i = 0; i < fwHexFiles.length; i++) - loadFirmware(i, 1, fwHexFiles[i], db); + for (var i = 0; i < fwSketches.length; i++) + loadFirmware(i, 1, fwSketches[i], db); var gw; if (gwType == 'Ethernet') { diff --git a/NodeJsController/package.json b/NodeJsController/package.json index a4866a6..eaffa4a 100644 --- a/NodeJsController/package.json +++ b/NodeJsController/package.json @@ -5,6 +5,7 @@ "dependencies": { "mongodb": "~1.4.7", "net": "~1.0.2", + "requestify": "^0.1.16", "serialport": "^1.4.5" } } From 0036c5f52ad0632bd21c5d106d0f40b9fa7e0fcf Mon Sep 17 00:00:00 2001 From: ToSa27 Date: Mon, 25 Aug 2014 18:34:51 +0200 Subject: [PATCH 322/326] Added technical details for controller developers regarding OTA bootloader --- Bootloader/Readme.md | 48 +++++++++++++++++++++++++++- NodeJsController/NodeJsController.js | 4 +-- NodeJsController/Readme.md | 4 +-- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/Bootloader/Readme.md b/Bootloader/Readme.md index 810b16a..b6d7e33 100644 --- a/Bootloader/Readme.md +++ b/Bootloader/Readme.md @@ -1 +1,47 @@ -See NodeJsController/Readme.md for usage instructions... \ No newline at end of file +See NodeJsController/Readme.md for usage instructions... + +

    Technical details to write your own controller

    +All initialization the bootloader does (finding parent / requesting nodeId on first start) uses the same packets as a normal MySensors sketch. There is no need for the controller to distinguish between packets from the bootloader and packets from normal sketch execution. The controller only needs to care about two additional request/response communications. All communication is binary. +

    FirmwareConfig

    +
      +
    • the bootloader sends a FirmwareConfigRequest packet to the gateway to request information about the firmware it should execute: +

      +typedef struct
      +{
      + uint16_t type;
      + uint16_t version;
      +} FirmwareConfigRequest; +

      +
    • the gateway (the controller) responds with a FirmwareConfigResponse including details about the firmware the sensor should execute: +

      +typedef struct
      +{
      + uint16_t type;
      + uint16_t version;
      + uint16_t blocks;
      + uint16_t crc;
      +} FirmwareConfigResponse; +

      +
    +

    Firmware

    +
      +
    • the bootloader sends a FirmwareRequest packet to the gateway to request a specific subset (block) of the compiled firmware: +

      +typedef struct
      +{
      + uint16_t type;
      + uint16_t version;
      + uint16_t block;
      +} FirmwareRequest; +

      +
    • the gateway (the controller) responds with a FirmwareResponse including the specific block of the compiled firmware: +
    +

    +typedef struct
    +{
    + uint16_t type;
    + uint16_t version;
    + uint16_t block;
    + uint8_t data[FIRMWARE_BLOCK_SIZE];
    +} FirmwareResponse; +

    diff --git a/NodeJsController/NodeJsController.js b/NodeJsController/NodeJsController.js index fbe1470..8bb67da 100644 --- a/NodeJsController/NodeJsController.js +++ b/NodeJsController/NodeJsController.js @@ -11,8 +11,8 @@ const dbAddress = '127.0.0.1'; const dbPort = 27017; const dbName = 'MySensorsDb'; -const fwSketches = [ '../libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino' ]; -const fwDefaultType = 0; // index of hex file from array above +const fwSketches = [ ]; +const fwDefaultType = 0xFFFF; // index of hex file from array above (0xFFFF const FIRMWARE_BLOCK_SIZE = 16; const BROADCAST_ADDRESS = 255; diff --git a/NodeJsController/Readme.md b/NodeJsController/Readme.md index 98dee49..d8b364b 100644 --- a/NodeJsController/Readme.md +++ b/NodeJsController/Readme.md @@ -290,10 +290,8 @@ ISP_MCU = m328p

    End-to-End Test

    Add Firmware to Controller (here: DallasTemperatureSensor)

      -
    • use Arduino IDE or codebender or whatever other way to compile your sketch -
    • copy the compiled DallasTemperatureSensor.cpp.hex file to the NodeJsController directory (if using the Arduino IDE, enable verbose logging in preferences and then watch the compilation process to find the temp directory where the output is stored)
    • nano ~/Arduino/NodeJsController/NodeJsController.js -

      const fwHexFiles = [ 'DallasTemperatureSensor.cpp.hex' ];
      +

      const fwHexFiles = [ '../libraries/MySensors/examples/DallasTemperatureSensor/DallasTemperatureSensor.ino' ];
      const fwDefaultType = 0;

    • sudo service mysensors restart (at this point stop/restart doesn't work. use "sudo killall node" followed by "sudo service mysensors start" instead)
    From 5e5b756160b3b138d0c1c89a5737189c0c614bfd Mon Sep 17 00:00:00 2001 From: Daniel Wiegert Date: Wed, 27 Aug 2014 15:24:04 +0200 Subject: [PATCH 323/326] Bugfixes and rewrite parts of code --- libraries/MySensors/MyMQTT.cpp | 364 +++++++++--------- libraries/MySensors/MyMQTT.h | 29 +- .../examples/MQTTGateway/MQTTGateway.ino | 79 ++-- 3 files changed, 215 insertions(+), 257 deletions(-) mode change 100644 => 100755 libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp index 0884906..2ab42b5 100755 --- a/libraries/MySensors/MyMQTT.cpp +++ b/libraries/MySensors/MyMQTT.cpp @@ -12,71 +12,89 @@ version 2 as published by the Free Software Foundation. #include "MyMQTT.h" #include "utility/MsTimer2.h" +char V_0[] PROGMEM = "TEMP"; //V_TEMP +char V_1[] PROGMEM = "HUM"; //V_HUM +char V_2[] PROGMEM = "LIGHT"; //V_LIGHT +char V_3[] PROGMEM = "DIMMER"; //V_DIMMER +char V_4[] PROGMEM = "PRESSURE"; //V_PRESSURE +char V_5[] PROGMEM = "FORECAST"; //V_FORECAST +char V_6[] PROGMEM = "RAIN"; //V_RAIN +char V_7[] PROGMEM = "RAINRATE"; //V_RAINRATE +char V_8[] PROGMEM = "WIND"; //V_WIND +char V_9[] PROGMEM = "GUST"; //V_GUST +char V_10[] PROGMEM = "DIRECTON"; //V_DIRECTON +char V_11[] PROGMEM = "UV"; //V_UV +char V_12[] PROGMEM = "WEIGHT"; //V_WEIGHT +char V_13[] PROGMEM = "DISTANCE"; //V_DISTANCE +char V_14[] PROGMEM = "IMPEDANCE"; //V_IMPEDANCE +char V_15[] PROGMEM = "ARMED"; //V_ARMED +char V_16[] PROGMEM = "TRIPPED"; //V_TRIPPED +char V_17[] PROGMEM = "WATT"; //V_WATT +char V_18[] PROGMEM = "KWH"; //V_KWH +char V_19[] PROGMEM = "SCENE_ON"; //V_SCENE_ON +char V_20[] PROGMEM = "SCENE_OFF"; //V_SCENE_OFF +char V_21[] PROGMEM = "HEATER"; //V_HEATER +char V_22[] PROGMEM = "HEATER_SW"; //V_HEATER_SW +char V_23[] PROGMEM = "LIGHT_LEVEL"; //V_LIGHT_LEVEL +char V_24[] PROGMEM = "VAR1"; //V_VAR1 +char V_25[] PROGMEM = "VAR2"; //V_VAR2 +char V_26[] PROGMEM = "VAR3"; //V_VAR3 +char V_27[] PROGMEM = "VAR4"; //V_VAR4 +char V_28[] PROGMEM = "VAR5"; //V_VAR5 +char V_29[] PROGMEM = "UP"; //V_UP +char V_30[] PROGMEM = "DOWN"; //V_DOWN +char V_31[] PROGMEM = "STOP"; //V_STOP +char V_32[] PROGMEM = "IR_SEND"; //V_IR_SEND +char V_33[] PROGMEM = "IR_RECEIVE"; //V_IR_RECEIVE +char V_34[] PROGMEM = "FLOW"; //V_FLOW +char V_35[] PROGMEM = "VOLUME"; //V_VOLUME +char V_36[] PROGMEM = "LOCK_STATUS"; //V_LOCK_STATUS +char V_37[] PROGMEM = "DUST_LEVEL"; //V_DUST_LEVEL +char V_38[] PROGMEM = "VOLTAGE"; //V_VOLTAGE +char V_39[] PROGMEM = "CURRENT"; //V_CURRENT +char V_40[] PROGMEM = ""; // +char V_41[] PROGMEM = ""; // +char V_42[] PROGMEM = ""; // +char V_43[] PROGMEM = ""; // +char V_44[] PROGMEM = ""; // +char V_45[] PROGMEM = ""; // +char V_46[] PROGMEM = ""; // +char V_47[] PROGMEM = ""; // +char V_48[] PROGMEM = ""; // +char V_49[] PROGMEM = ""; // +char V_50[] PROGMEM = ""; // +char V_51[] PROGMEM = ""; // +char V_52[] PROGMEM = ""; // +char V_53[] PROGMEM = ""; // +char V_54[] PROGMEM = ""; // +char V_55[] PROGMEM = ""; // +char V_56[] PROGMEM = ""; // +char V_57[] PROGMEM = ""; // +char V_58[] PROGMEM = ""; // +char V_59[] PROGMEM = ""; // +char V_60[] PROGMEM = "Started!\n"; //Custom for MQTTGateway +char V_61[] PROGMEM = "SKETCH_NAME"; //Custom for MQTTGateway +char V_62[] PROGMEM = "SKETCH_VERSION"; //Custom for MQTTGateway +char V_63[] PROGMEM = "UNKNOWN"; //Custom for MQTTGateway + +////////////////////////////////////////////////////////////////// + +PROGMEM const char *vType[] = { + V_0, V_1, V_2, V_3, V_4, V_5, V_6, V_7, V_8, V_9, V_10, + V_11, V_12, V_13, V_14, V_15, V_16, V_17, V_18, V_19, V_20, + V_21, V_22, V_23, V_24, V_25, V_26, V_27, V_28, V_29, V_30, + V_31, V_32, V_33, V_34, V_35, V_36, V_37, V_38, V_39, V_40, + V_41, V_42, V_43, V_44, V_45, V_46, V_47, V_48, V_49, V_50, + V_51, V_52, V_53, V_54, V_55, V_56, V_57, V_58, V_59, V_60, + V_61, V_62, V_63 +}; char broker[] PROGMEM = MQTT_BROKER_PREFIX; -char S_0[] PROGMEM = "Temperature"; //V_TEMP -char S_1[] PROGMEM = "Humidity"; //V_HUM -char S_2[] PROGMEM = "Light"; //V_LIGHT -char S_3[] PROGMEM = "Dimmer"; //V_DIMMER -char S_4[] PROGMEM = "Pressure"; //V_PRESSURE -char S_5[] PROGMEM = "Forecast"; //V_FORECAST -char S_6[] PROGMEM = "Rain"; //V_RAIN -char S_7[] PROGMEM = "Rainrate"; //V_RAINRATE -char S_8[] PROGMEM = "Wind"; //V_WIND -char S_9[] PROGMEM = "Gust"; //V_GUST -char S_10[] PROGMEM = "Direction"; //V_DIRECTION -char S_11[] PROGMEM = "UV"; //V_UV -char S_12[] PROGMEM = "Weight"; //V_WEIGHT -char S_13[] PROGMEM = "Distance"; //V_DISTANCE -char S_14[] PROGMEM = "Impedance"; //V_IMPEDANCE -char S_15[] PROGMEM = "Armed"; //V_ARMED -char S_16[] PROGMEM = "Tripped"; //V_TRIPPED -char S_17[] PROGMEM = "Watt"; //V_WATT -char S_18[] PROGMEM = "KWH"; //V_KWH -char S_19[] PROGMEM = "Scene_ON"; //V_SCENE_ON -char S_20[] PROGMEM = "Scene_OFF"; //V_SCENE_OFF -char S_21[] PROGMEM = "Heater"; //V_HEATER -char S_22[] PROGMEM = "Heater_SW"; //V_HEATER_SW -char S_23[] PROGMEM = "LightLevel"; //V_LIGHT_LEVEL -char S_24[] PROGMEM = "Var1"; //V_VAR1 -char S_25[] PROGMEM = "Var2"; //V_VAR2 -char S_26[] PROGMEM = "Var3"; //V_VAR3 -char S_27[] PROGMEM = "Var4"; //V_VAR4 -char S_28[] PROGMEM = "Var5,"; //V_VAR5 -char S_29[] PROGMEM = "Up"; //V_UP -char S_30[] PROGMEM = "Down"; //V_DOWN -char S_31[] PROGMEM = "Stop"; //V_STOP -char S_32[] PROGMEM = "IR_Send"; //V_IR_SEND -char S_33[] PROGMEM = "IR_Receive"; //V_IR_RECEIVE -char S_34[] PROGMEM = "Flow"; //V_FLOW -char S_35[] PROGMEM = "Volume"; //V_VOLUME -char S_36[] PROGMEM = "Lock_Status";//V_LOCK_STATUS -char S_37[] PROGMEM = "DustLevel"; //V_DUST_LEVEL -char S_38[] PROGMEM = ""; -char S_39[] PROGMEM = ""; -char S_40[] PROGMEM = ""; -char S_41[] PROGMEM = ""; -char S_42[] PROGMEM = ""; -char S_43[] PROGMEM = ""; -char S_44[] PROGMEM = ""; -char S_45[] PROGMEM = "Volt_Batt"; // Does not exist in MyMessage.h! -char S_46[] PROGMEM = ""; -char S_47[] PROGMEM = ""; -char S_48[] PROGMEM = ""; -char S_49[] PROGMEM = "Sketch_name"; // Does not exist in MyMessage.h! -char S_50[] PROGMEM = "Sketch_version"; // Does not exist in MyMessage.h! - - -PROGMEM const char *sType[] = { - S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_10, - S_11, S_12, S_13, S_14, S_15, S_16, S_17, S_18, S_19, S_20, - S_21, S_22, S_23, S_24, S_25, S_26, S_27, S_28, S_29, S_30, - S_31, S_32, S_33, S_34, S_35, S_36, S_37, S_38, S_39, S_40, - S_41, S_42, S_43, S_44, S_45, S_46, S_47, S_48, S_49, S_50 -}; - +#define S_FIRSTCUSTOM 60 +#define TYPEMAXLEN 20 +#define V_TOTAL (sizeof(vType)/sizeof(char *))-1 extern volatile uint8_t countRx; extern volatile uint8_t countTx; @@ -90,11 +108,32 @@ MySensor(_cepin, _cspin) { } -void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(const char *, int *), uint8_t _rx, uint8_t _tx, uint8_t _er) { +inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { + msg.destination = destination; + msg.sender = sender; + msg.sensor = sensor; + msg.type = type; + mSetCommand(msg,command); + mSetRequestAck(msg,enableAck); + mSetAck(msg,false); + return msg; +} + +char *getType(char *b, const char **index) { + char *q = b; + char *p = (char *)pgm_read_ptr(index); + while (*q++ = pgm_read_byte(p++)); + *q=0; + return b; +} + +void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback) + (const char *, uint8_t *), uint8_t _rx, uint8_t _tx, uint8_t _er) { Serial.begin(BAUD_RATE); repeaterMode = true; isGateway = true; MQTTClientConnected = false; + setupRepeaterMode(); dataCallback = inDataCallback; @@ -117,10 +156,7 @@ void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataR MsTimer2::set(200, ledTimersInterrupt); MsTimer2::start(); - - // Send startup log message on serial - //Serial.print(PSTR("Started\n"));//TODO: progmem gives error..? error: sType causes a section type conflict with __c - Serial.print("Started\n");//TODO: fix this... + Serial.print(getType(convBuf, &vType[S_FIRSTCUSTOM])); } void MyMQTT::processRadioMessage() { @@ -134,104 +170,97 @@ void MyMQTT::processRadioMessage() { } -void MyMQTT::processMQTTMessage(char *inputString, int inputPos) { +void MyMQTT::processMQTTMessage(char *inputString, uint8_t inputPos) { char *str, *p; - char i = 0; + uint8_t i = 0; buffer[0]= 0; buffsize = 0; -#ifdef TCPDUMP - Serial.print("<<"); - char buf[4]; - for (int a=0; a> 4 == MQTTCONNECT) { + + if ((uint8_t)inputString[0] >> 4 == MQTTCONNECT) { buffer[buffsize++] = MQTTCONNACK << 4; - buffer[buffsize++] = 0x02; // Remaining length - buffer[buffsize++] = 0x00; // Connection accepted - buffer[buffsize++] = 0x00; // Reserved - MQTTClientConnected=true; + buffer[buffsize++] = 0x02; // Remaining length + buffer[buffsize++] = 0x00; // Connection accepted + buffer[buffsize++] = 0x00; // Reserved + MQTTClientConnected=true; // We have connection! } - if ((byte)inputString[0] >> 4 == MQTTPINGREQ) { + if ((uint8_t)inputString[0] >> 4 == MQTTPINGREQ) { buffer[buffsize++] = MQTTPINGRESP << 4; buffer[buffsize++] = 0x00; } - if ((byte)inputString[0] >> 4 == MQTTSUBSCRIBE) { - buffer[buffsize++] = MQTTSUBACK << 4; // Just ack everything, we actually dont really care! - buffer[buffsize++] = 0x03; // Remaining length - buffer[buffsize++] = (byte)inputString[2]; // Message ID MSB - buffer[buffsize++] = (byte)inputString[3]; // Message ID LSB - buffer[buffsize++] = MQTTQOS0; // QOS level + if ((uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE) { + buffer[buffsize++] = MQTTSUBACK << 4; // Just ack everything, we actually dont really care! + buffer[buffsize++] = 0x03; // Remaining length + buffer[buffsize++] = (uint8_t)inputString[2]; // Message ID MSB + buffer[buffsize++] = (uint8_t)inputString[3]; // Message ID LSB + buffer[buffsize++] = MQTTQOS0; // QOS level } - if ((byte)inputString[0] >> 4 == MQTTUNSUBSCRIBE) { + if ((uint8_t)inputString[0] >> 4 == MQTTUNSUBSCRIBE) { buffer[buffsize++] = MQTTUNSUBACK << 4; - buffer[buffsize++] = 0x02; // Remaining length - buffer[buffsize++] = (byte)inputString[2]; // Message ID MSB - buffer[buffsize++] = (byte)inputString[3]; // Message ID LSB + buffer[buffsize++] = 0x02; // Remaining length + buffer[buffsize++] = (uint8_t)inputString[2]; // Message ID MSB + buffer[buffsize++] = (uint8_t)inputString[3]; // Message ID LSB } - if ((byte)inputString[0] >> 4 == MQTTDISCONNECT) { - MQTTClientConnected=false; + if ((uint8_t)inputString[0] >> 4 == MQTTDISCONNECT) { + MQTTClientConnected=false; // We lost connection! } if (buffsize > 0) { -#ifdef TCPDUMP - Serial.print(">>"); - char buf[4]; - for (int a=0; a> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { + if ((uint8_t)inputString[0] >> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE)) { buffer[0]= 0; buffsize = 0; - if ((byte)inputString[0] >> 4 == MQTTSUBSCRIBE) { + // Cut out address and payload depending on message type. + if ((uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE) { strncat(buffer,inputString+6,inputString[5]); } else { strncat(buffer,inputString+4,inputString[3]); } - msg.sender = GATEWAY_ADDRESS; - for (str = strtok_r(buffer,"/",&p) ; str && i<4 ; str = strtok_r(NULL,"/",&p)) { //split using semicolon - // TODO: Check if we should send ack or not. + + // TODO: Check if we should send ack or not. + for (str = strtok_r(buffer,"/",&p) ; str && i<4 ; str = strtok_r(NULL,"/",&p)) { if (i == 0) { - if (strcmp_P(str,broker)!=0) { - return; //Message not for us or malformatted! + if (strcmp_P(str,broker)!=0) { //look for MQTT_BROKER_PREFIX + return; //Message not for us or malformatted! } } else if (i==1) { - msg.destination = atoi(str); + msg.destination = atoi(str); //NodeID } else if (i==2) { - msg.sensor = atoi(str); + msg.sensor = atoi(str); //SensorID } else if (i==3) { - char match=0; - for (int j=0; strcpy_P(convBuf, (char*)pgm_read_word(&(sType[j]))) ; j++) { - if (strcmp(str,convBuf)==0) { + char match=0; //SensorType + //strcpy(str,(char*)&str[2]); //Strip V_ + + for (uint8_t j=0; strcpy_P(convBuf, (char*)pgm_read_word(&(vType[j]))) ; j++) { + if (strcmp((char*)&str[2],convBuf)==0) { //Strip V_ and compare match=j; break; } + if (j >= V_TOTAL) { // No match found! + match=V_TOTAL; // last item. + break; + } } msg.type = match; } i++; - } - if ((char)inputString[1] > (char)(inputString[3]+2) && !((byte)inputString[0] >> 4 == MQTTSUBSCRIBE)) { + } //Check if packge has payload + if ((uint8_t)inputString[1] > (uint8_t)(inputString[3]+2) && !((uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE)) { strcpy(convBuf,inputString+(inputString[3]+4)); - msg.set(convBuf); + msg.set(convBuf); //Payload } else { - msg.set(""); + msg.set(""); //No payload } - msg.sender = GATEWAY_ADDRESS; - mSetCommand(msg,C_SET); - mSetRequestAck(msg,false); - mSetAck(msg,false); - mSetVersion(msg, PROTOCOL_VERSION); txBlink(1); - if (!sendRoute(msg)) errBlink(1); + if (!sendRoute(build(msg, GATEWAY_ADDRESS, msg.destination, msg.sensor, C_SET, msg.type, 0))) errBlink(1); + } } - void MyMQTT::SendMQTT(MyMessage &msg) { - //serial(PSTR("%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), msg.type, msg.getString(convBuf)); buffsize = 0; - if (!MQTTClientConnected) return; + if (!MQTTClientConnected) return; //We have no connections - return if (msg.isAck()) { Serial.println("msg is ack!"); if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { @@ -246,95 +275,52 @@ void MyMQTT::SendMQTT(MyMessage &msg) { if (msg.sender==newNodeID) { saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); } - if (mGetCommand(msg)==C_INTERNAL && msg.type==I_CONFIG) { // CONFIG - // As for now there is only one 'config' request. - // We force SI! Resistance is futile! - // - // Todo : Support for more config types, Maybe just read from own EEPROM? - // Use internal EEPROM_CONTROLLER_CONFIG_ADDRESS and special MQTT address to write to - // EEPROM_CONTROLLER_CONFIG_ADDRESS & EEPROM_LOCAL_CONFIG_ADDRESS - msg.destination = msg.sender; //NodeID - msg.sender = GATEWAY_ADDRESS; - msg.sensor = 255; //SensorID - msg.type = I_CONFIG; //msgType - mSetCommand(msg,C_INTERNAL);//Subtype - mSetRequestAck(msg,false); - mSetAck(msg,false); - msg.set("M"); - mSetVersion(msg, PROTOCOL_VERSION); - txBlink(1); - if (!sendRoute(msg)) errBlink(1); - } else if (mGetCommand(msg)==C_PRESENTATION && (msg.type==S_ARDUINO_NODE || msg.type==S_ARDUINO_REPEATER_NODE)) { - //Doesnt work to check new sensorID here, this message does not always arrive.. See above. - } else if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) { - unsigned char newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; - if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID; - if (newNodeID >= MQTT_LAST_SENSORID) return; // Sorry no more id's left :( - msg.destination = msg.sender; //NodeID - msg.sender = GATEWAY_ADDRESS; - msg.sensor = 255; //SensorID - msg.type = I_ID_RESPONSE; //MsgType - mSetCommand(msg,C_INTERNAL); //Subtype - mSetRequestAck(msg,false); //Request ack doesn't work, node/gateway gets stuck in enless loop. - mSetAck(msg,false); - msg.set((uint8_t)newNodeID); //Payload - mSetVersion(msg, PROTOCOL_VERSION); - txBlink(1); - if (!sendRoute(msg)) errBlink(1); - // if (sendRoute(msg)) saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID); // If send OK save to eeprom. DOES NOT ALWAYS RETURN 'OK'!? + if (mGetCommand(msg)==C_INTERNAL) { + if (msg.type==I_CONFIG) { + txBlink(1); + if (!sendRoute(build(msg, GATEWAY_ADDRESS, msg.sender, 255, C_INTERNAL, I_CONFIG, 0).set("M"))) errBlink(1); + } else if (msg.type==I_ID_REQUEST && msg.sender==255) { + uint8_t newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1; + if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID; + if (newNodeID >= MQTT_LAST_SENSORID) return; // Sorry no more id's left :( + txBlink(1); + if (!sendRoute(build(msg, GATEWAY_ADDRESS, msg.sender, 255, C_INTERNAL, I_ID_RESPONSE, 0).set(newNodeID))) errBlink(1); + } } else if (mGetCommand(msg)!=0) { - if (mGetCommand(msg)==3) msg.type=msg.type+38; - buffer[buffsize++] = MQTTPUBLISH << 4; // 0: - buffer[buffsize++] = 0x09; // 1: Remaining length with no payload, we'll set this later to correct value, buffsize -2 - buffer[buffsize++] = 0x00; // 2: Length MSB (Remaing length can never exceed ff,so MSB must be 0!) - buffer[buffsize++] = 0x08; // 3: Length LSB (ADDR), We'll set this later + if (mGetCommand(msg)==3) msg.type=msg.type+(S_FIRSTCUSTOM-10); //Special message + + buffer[buffsize++] = MQTTPUBLISH << 4; // 0: + buffer[buffsize++] = 0x09; // 1: Remaining length with no payload, we'll set this later to correct value, buffsize -2 + buffer[buffsize++] = 0x00; // 2: Length MSB (Remaing length can never exceed ff,so MSB must be 0!) + buffer[buffsize++] = 0x08; // 3: Length LSB (ADDR), We'll set this later + if (msg.type > V_TOTAL) msg.type=V_TOTAL;// If type > defined types set to unknown. strcpy_P(buffer+4, broker); buffsize+=strlen_P(broker); - buffsize+=sprintf(&buffer[buffsize],"/%i/%i/",msg.sender,msg.sensor); - buffsize+=strncpysType_retL(buffer,msg.type,buffsize); - buffer[3]=buffsize-4; // Set correct address length on byte 4. + buffsize+=sprintf(&buffer[buffsize],"/%i/%i/V_%s",msg.sender,msg.sensor,getType(convBuf, &vType[msg.type])); + buffer[3]=buffsize-4; // Set correct address length on byte 4. #ifdef DEBUG Serial.println((char*)&buffer[4]); #endif msg.getString(convBuf); - for (unsigned int a=0; a>"); - char buf[4]; - for (int a=0; a0) otherwise the code might do strange things. -/////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// #define EEPROM_LATEST_NODE_ADDRESS ((uint8_t)EEPROM_LOCAL_CONFIG_ADDRESS) #define MQTT_MAX_PACKET_SIZE 100 @@ -56,29 +55,27 @@ version 2 as published by the Free Software Foundation. class MyMQTT : public MySensor { + public: MyMQTT(uint8_t _cepin=5, uint8_t _cspin=6); - void begin(rf24_pa_dbm_e paLevel=RF24_PA_LEVEL_GW, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE, void (*dataCallback)(const char *, int *)=NULL, uint8_t _rx=6, uint8_t _tx=5, uint8_t _er=4 ); + void begin(rf24_pa_dbm_e paLevel=RF24_PA_LEVEL_GW, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE, void (*dataCallback) + (const char *, uint8_t *)=NULL, uint8_t _rx=6, uint8_t _tx=5, uint8_t _er=4 ); void processRadioMessage(); - void processMQTTMessage(char *inputString, int inputPos); + void processMQTTMessage(char *inputString, uint8_t inputPos); private: - bool MQTTClientConnected; - char buffer[MQTT_MAX_PACKET_SIZE]; - int buffsize; - char convBuf[MAX_PAYLOAD*2+1]; - boolean useWriteCallback; - void (*dataCallback)(const char *, int *); + void (*dataCallback)(const char *, uint8_t *); void SendMQTT(MyMessage &msg); - char strncpysType_retL(char *str, unsigned char index, char start); - - void ledTimers(); void rxBlink(uint8_t cnt); void txBlink(uint8_t cnt); void errBlink(uint8_t cnt); + + bool MQTTClientConnected; + char buffer[MQTT_MAX_PACKET_SIZE]; + char convBuf[MAX_PAYLOAD*2+1]; + uint8_t buffsize; }; extern void ledTimersInterrupt(); - #endif diff --git a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino old mode 100644 new mode 100755 index 3cb651a..29f6551 --- a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino +++ b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino @@ -31,18 +31,19 @@ This will listen on your selected TCP_IP:TCP_PORT below, Please change TCP_MAC y message has any length or not. Example: if (msg.type==V_LIGHT && strlen(msg.getString())>0) otherwise the code might do strange things. -* Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/[SensorType] +* Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/V_[SensorType] NodeID and SensorID is uint8 (0-255) number. Last segment is translation of the sensor type, look inside MyMQTT.cpp for the definitions. User can change this to their needs. We have also left some space for custom types. Special: (sensor 255 reserved for special commands) -You can receive a node sketch name with MyMQTT/20/255/Sketch_name (or version with _version) +You can receive a node sketch name with MyMQTT/20/255/V_Sketch_name (or version with _version) To-do: Special commands : clear or set EEPROM Values, Send REBOOT and Receive reboot for MyMQTT itself. Be able to send ACK so client returns the data being sent. ... Please come with ideas! +What to do with publish messages. Test in more MQTT clients, So far tested in openhab and MyMQTT for Android (Not my creation) - http://www.openhab.org/ @@ -50,51 +51,16 @@ Test in more MQTT clients, So far tested in openhab and MyMQTT for Android (Not ... Please notify me if you use this broker with other software. -* Example Openhab configuration: -openhab.cfg ---- -mqtt:mysensor.url=tcp://192.168.0.234:1883 -mqtt:mysensor.clientId=MQTT ---- - -items/test.items ---- -Group test -Number Temp_test "Temp [%.1f °C]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Temperature:state:default]"} -Number Hum_test "Hum [%.1f %%]" (test) {mqtt="<[mysensor:MyMQTT/20/#/Humidity:state:default]"} -Number test_test "test [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/3/#:state:default]"} -Switch sw1 "sw 1" (test) {mqtt="<[mysensor:MyMQTT/21/1/Light:command:MAP(1on0off.map)]"} -Switch sw2 "sw2 recieve example" (test) {mqtt=">[mysensor:MyMQTT/21/2/Light:command:ON:1], - >[mysensor:MyMQTT/21/2/Light:command:OFF:0]"} -Switch sw3 "sw2 send + recieve example" (test) {mqtt=">[mysensor:MyMQTT/21/2/Light:command:ON:1], - >[mysensor:MyMQTT/21/2/Light:command:OFF:0],<[mysensor:MyMQTT/21/2/Light:command:MAP(1on0off.map)]"} -String sketch20 "Node 20 Sketch name [%s]" (test) {mqtt="<[mysensor:MyMQTT/20/255/Sketch_name:state:default]"} -String sketch21 "Node 21 Sketch name [%s]" (test) {mqtt="<[mysensor:MyMQTT/21/255/Sketch_name:state:default]"} ---- (Note; # = Wildcard character) - -sitemap/test.site ---- -sitemap demo label="Menu" -Frame label="Openhab" { - Group item=test label="Test group" -} ---- - -transform/1on0off.map ---- -1=ON -0=OFF ---- +* How to set-up Openhab and MQTTGateway: +http://forum.mysensors.org/topic/303/mqtt-broker-gateway */ - #include #include #include - -// Use this for IBOARD modded to use standard MISO/MOSI/SCK, see note *1 above! +// * Use this for IBOARD modded to use standard MISO/MOSI/SCK, see note *1 above! /* #define RADIO_CE_PIN 3 // radio chip enable #define RADIO_SPI_SS_PIN 8 // radio SPI serial select @@ -102,7 +68,7 @@ transform/1on0off.map #define RADIO_RX_LED_PIN A1 // Receive led pin #define RADIO_TX_LED_PIN A0 // the PCB, on board LED*/ -//Use this for default configured pro mini / nano etc : +// * Use this for default configured pro mini / nano etc : ///* #define RADIO_CE_PIN 5 // radio chip enable #define RADIO_SPI_SS_PIN 6 // radio SPI serial select @@ -110,19 +76,18 @@ transform/1on0off.map #define RADIO_RX_LED_PIN 8 // Receive led pin #define RADIO_TX_LED_PIN 9 // the PCB, on board LED*/ -#define TCP_PORT 1883 // Set your MQTT Broker Listening port. -IPAddress TCP_IP ( 192, 168, 0, 234 ); // Configure your static ip-address here -byte TCP_MAC[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - You should change this! see note *2 above! +#define TCP_PORT 1883 // Set your MQTT Broker Listening port. +IPAddress TCP_IP ( 192, 168, 0, 234 ); // Configure your static ip-address here +uint8_t TCP_MAC[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - You should change this! see note *2 above! + +////////////////////////////////////////////////////////////////// EthernetServer server = EthernetServer(TCP_PORT); MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN); -// -- Uncomment this constructor if you have leds and include button attached to your gateway -//MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); - void processEthernetMessages() { char inputString[MQTT_MAX_PACKET_SIZE] = ""; - int inputSize = 0; + uint8_t inputSize = 0; EthernetClient client = server.available(); if (client) { while (client.available()) { @@ -130,23 +95,33 @@ void processEthernetMessages() { inputString[inputSize] = inChar; inputSize++; } +#ifdef TCPDUMP + Serial.print("<<"); + char buf[4]; + for (uint8_t a=0; a>"); + char buf[4]; + for (uint8_t a=0; a<*writeSize; a++) { sprintf(buf,"%02X ",(uint8_t)writeBuffer[a]); Serial.print(buf); } Serial.println(""); +#endif + server.write((const uint8_t *)writeBuffer, *writeSize); } + int main(void) { init(); Ethernet.begin(TCP_MAC, TCP_IP); delay(1000); // Wait for Ethernet to get configured. - gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet); + gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); server.begin(); while (1) { processEthernetMessages(); gw.processRadioMessage(); } } - From fdcbbbac95167233ea810a52e5a0d56cebf9cff2 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 27 Aug 2014 21:24:00 +0200 Subject: [PATCH 324/326] Need to include MySensor.h for gateway examples to work in codebender. --- README.md | 2 +- libraries/MySensors/Version.h | 2 +- .../MySensors/examples/EthernetGateway/EthernetGateway.ino | 5 +++-- libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino | 1 + libraries/MySensors/examples/SerialGateway/SerialGateway.ino | 1 + 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4df7c40..ebb4f31 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Arduino ======= -MySensors Arduino Library +MySensors Arduino Library v1.4 Please visit www.mysensors.org for more information diff --git a/libraries/MySensors/Version.h b/libraries/MySensors/Version.h index bc5edc4..026d736 100644 --- a/libraries/MySensors/Version.h +++ b/libraries/MySensors/Version.h @@ -6,6 +6,6 @@ #ifndef Version_h #define Version_h -#define LIBRARY_VERSION "1.4b1 (18848a2)" +#define LIBRARY_VERSION "1.4" #endif diff --git a/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino b/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino index 5932de6..df3b935 100644 --- a/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino +++ b/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino @@ -45,14 +45,15 @@ */ #include +#include #include #include // Use this if you have attached a Ethernet ENC28J60 shields -#include +//#include // Use this fo WizNET module and Arduino Ethernet Shield -//#include +#include #define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled diff --git a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino index 3cb651a..016d3d0 100644 --- a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino +++ b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino @@ -90,6 +90,7 @@ transform/1on0off.map #include +#include #include #include diff --git a/libraries/MySensors/examples/SerialGateway/SerialGateway.ino b/libraries/MySensors/examples/SerialGateway/SerialGateway.ino index a59de08..8c3a7dd 100644 --- a/libraries/MySensors/examples/SerialGateway/SerialGateway.ino +++ b/libraries/MySensors/examples/SerialGateway/SerialGateway.ino @@ -22,6 +22,7 @@ */ #include +#include #include #include From 1290c68c4a472d63dd37a6a6495c051c0dcb4e72 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 27 Aug 2014 21:53:57 +0200 Subject: [PATCH 325/326] Classify method. --- libraries/MySensors/MyMQTT.cpp | 2 +- libraries/MySensors/MyMQTT.h | 2 ++ libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) mode change 100755 => 100644 libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp index 2ab42b5..f080790 100755 --- a/libraries/MySensors/MyMQTT.cpp +++ b/libraries/MySensors/MyMQTT.cpp @@ -119,7 +119,7 @@ inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, ui return msg; } -char *getType(char *b, const char **index) { +char *MyMQTT::getType(char *b, const char **index) { char *q = b; char *p = (char *)pgm_read_ptr(index); while (*q++ = pgm_read_byte(p++)); diff --git a/libraries/MySensors/MyMQTT.h b/libraries/MySensors/MyMQTT.h index 76d0830..e5d5447 100755 --- a/libraries/MySensors/MyMQTT.h +++ b/libraries/MySensors/MyMQTT.h @@ -14,6 +14,7 @@ version 2 as published by the Free Software Foundation. #include "MySensor.h" + ////////////////////////////////////////////////////////////////// #ifdef DEBUG @@ -74,6 +75,7 @@ private: char buffer[MQTT_MAX_PACKET_SIZE]; char convBuf[MAX_PAYLOAD*2+1]; uint8_t buffsize; + char *getType(char *b, const char **index); }; extern void ledTimersInterrupt(); diff --git a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino old mode 100755 new mode 100644 index 5050b59..bbd44dd --- a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino +++ b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino @@ -61,6 +61,7 @@ http://forum.mysensors.org/topic/303/mqtt-broker-gateway #include #include + // * Use this for IBOARD modded to use standard MISO/MOSI/SCK, see note *1 above! /* #define RADIO_CE_PIN 3 // radio chip enable From 9d42ac4c77da6dc184ac539a0725cd6c87afe267 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Wed, 27 Aug 2014 22:55:52 +0200 Subject: [PATCH 326/326] Fix compiler error. --- libraries/MySensors/MyMQTT.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/MyMQTT.cpp b/libraries/MySensors/MyMQTT.cpp index f080790..610414e 100755 --- a/libraries/MySensors/MyMQTT.cpp +++ b/libraries/MySensors/MyMQTT.cpp @@ -121,7 +121,7 @@ inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, ui char *MyMQTT::getType(char *b, const char **index) { char *q = b; - char *p = (char *)pgm_read_ptr(index); + char *p = (char *)pgm_read_word(index); while (*q++ = pgm_read_byte(p++)); *q=0; return b;