├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── REPOSITORY_NOTES.md └── SECURITY.md ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── docs ├── BACKGROUND.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── Doxyfile ├── GETTING_STARTED.md ├── SUPPORT.md ├── TODO.md └── TROUBLESHOOTING.md ├── examples ├── PacketSerialReverseEcho │ └── PacketSerialReverseEcho.ino ├── PacketSerialReverseEchoAdvanced │ ├── .due.test.skip │ ├── .esp32.test.skip │ ├── .m4.test.skip │ ├── .zero.test.skip │ ├── PacketSerialReverseEchoAdvanced.ino │ └── README.md ├── PacketSerialReverseEchoClass │ └── PacketSerialReverseEchoClass.ino └── PacketSerialReverseEchoSLIP │ └── PacketSerialReverseEchoSLIP.ino ├── keywords.txt ├── library.properties └── src ├── Encoding ├── COBS.h └── SLIP.h └── PacketSerial.h /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See more here https://help.github.com/en/articles/about-code-owners 2 | 3 | * @bakercp 4 | 5 | src/Encoding/SLIP.h @avilleret -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bakercp 2 | custom: https://paypal.me/bakercp 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # New Issue Guidelines 2 | 3 | _Your issue may already be reported! Please search on the [Issue Tracker](../) before creating one._ 4 | 5 | ## Expected Behavior 6 | 7 | 8 | 9 | ## Current Behavior 10 | 11 | 12 | 13 | ## Possible Solution 14 | 15 | 16 | 17 | ## Steps to Reproduce (for bugs) 18 | 19 | 20 | 21 | 1. Step 1. 22 | 2. Step 2. 23 | 3. Step 3. 24 | 4. etc. 25 | 26 | ## Context 27 | 28 | 29 | 30 | ## Your Environment 31 | 32 | * Version used: 33 | * IDE Name and version: 34 | * Operating System and version (desktop or mobile): 35 | * Link to your project: 36 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # New Pull Request Guidelines 2 | 3 | _A similar PR may already be submitted! Please search among the [Pull Requests](../) before creating one._ 4 | 5 | ## This Pull-Request Fixes the Following Issue(s) 6 | 7 | - Fixes # _the issue number here_ 8 | - Fixes # _etc_ 9 | 10 | ## Proposed Changes 11 | 12 | - _Change 1._ 13 | - _Change 2._ 14 | - _Change 3._ 15 | - _etc._ 16 | 17 | ## Testing Procedure 18 | 19 | _How do we test it?_ 20 | 21 | - _Test 1._ 22 | - _Test 2._ 23 | - _Test 3._ 24 | - _etc._ 25 | -------------------------------------------------------------------------------- /.github/REPOSITORY_NOTES.md: -------------------------------------------------------------------------------- 1 | 2 | # Github Documentation Help 3 | 4 | - [README.md](https://help.github.com/en/articles/about-readmes) 5 | - [CODE_OF_CONDUCT.md](https://help.github.com/en/articles/adding-a-code-of-conduct-to-your-project) 6 | - [CODEONWERS](https://help.github.com/en/articles/about-code-owners) 7 | - [CONTRIBUTING.md](https://help.github.com/en/articles/setting-guidelines-for-repository-contributors) 8 | - [ISSUE_TEMPLATE.md](https://help.github.com/en/articles/about-issue-and-pull-request-templates) 9 | - [PULL_REQUEST_TEMPLATE.md](https://help.github.com/en/articles/about-issue-and-pull-request-templates) 10 | - [FUNDING.yml](https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository) 11 | - Enable in settings. 12 | - [SECURITY.md](https://help.github.com/en/articles/adding-a-security-policy-to-your-repository) 13 | - [SUPPORT.md](https://help.github.com/en/articles/adding-support-resources-to-your-project) 14 | - [LICENSE.md](https://help.github.com/en/articles/adding-a-license-to-a-repository) 15 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | If an exploitable security issue is discovered, please contact the repository owner at [info@christopherbaker.net](mailto:info@christopherbaker.net) before creating an issue. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | 3 | docs/html 4 | docs/tagfile.xml 5 | 6 | # xcode 7 | *.pbxuser 8 | *.perspectivev3 9 | xcuserdata 10 | build 11 | *.app 12 | #*.a 13 | 14 | # OSX 15 | .DS_Store 16 | 17 | # codeblocks 18 | *.layout 19 | example/obj 20 | 21 | # Win 22 | *.exe 23 | *.dll 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: false 3 | cache: 4 | directories: 5 | - ~/arduino_ide 6 | - ~/.arduino15/packages/ 7 | before_install: 8 | - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh) 9 | install: 10 | - arduino --install-library "ArduinoUnit" 11 | script: 12 | - build_main_platforms 13 | notifications: 14 | email: 15 | on_success: change 16 | on_failure: change 17 | 18 | jobs: 19 | include: 20 | - stage: Deploy Documentation 21 | script: 22 | - source <(curl -SLs https://raw.githubusercontent.com/bakercp/ci/master/deploy_documentation.sh) 23 | deploy: 24 | provider: pages 25 | skip_cleanup: true 26 | github_token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable 27 | keep_history: false 28 | local_dir: docs/html 29 | on: 30 | branch: master 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Christopher Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PacketSerial 2 | 3 | [![Build Status](https://travis-ci.org/bakercp/PacketSerial.svg?branch=master)](https://travis-ci.org/bakercp/PacketSerial) 4 | 5 | An Arduino Library that facilitates packet-based serial communication using COBS or SLIP encoding. 6 | 7 | ## Features 8 | 9 | _PacketSerial_ is an small, efficient, library that allows [Arduinos](http://www.arduino.cc/) to send and receive serial data packets (with COBS, SLIP or a user-defined encoding) that include bytes of any value (0 - 255). 10 | A _packet_ is simply an array of bytes. 11 | 12 | ## Documentation 13 | 14 | If you're asking _Why do I need this?_, read the [background introduction](docs/BACKGROUND.md) page. If you're ready to get started, check out the [getting started](docs/GETTING_STARTED.md) page. You can also learn a lot by reading through the [examples](./examples/). Finally, if you're interested in learning more about how the code works, read the comments in the [source code](./src/) and check out the [API documentation](https://bakercp.github.io/PacketSerial/). 15 | 16 | ## Support 17 | 18 | If you're looking for help, read the [support](docs/SUPPORT.md) page. 19 | 20 | ## Compatibility 21 | 22 | ### Requrements 23 | 24 | - [Arduino IDE](https://www.arduino.cc/en/main/software) (version 1.5+) 25 | 26 | ### Other Platforms 27 | 28 | This project has been used successfully with [openFrameworks](https://openframeworks.cc/) using the [ofxSerial](https://github.com/bakercp/ofxSerial) addon. In particular, see the `ofx::IO::PacketSerial` object. Additionally this project has been used with Python using the [PySerial](https://pythonhosted.org/pyserial/index.html) package. In particular, check out the [COBS](https://pythonhosted.org/cobs/) (see [this discussion](https://github.com/bakercp/PacketSerial/issues/10)) and [SLIP](https://pypi.python.org/pypi/sliplib/0.0.1) packages. 29 | 30 | Ultimately, any library that correctly implements a COBS or SLIP encoding scheme should be compatible with this project. 31 | 32 | ### Continuous Integration 33 | 34 | Continuous integration tests are carried out on a variety of common Arduino platforms. See [this script](https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh) for a list. 35 | 36 | ## Licensing 37 | 38 | This project is licensed under the [MIT License](LICENSE.md). 39 | 40 | ## Project Management 41 | 42 | ### Repository 43 | 44 | [https://github.com/bakercp/PacketSerial](https://github.com/bakercp/PacketSerial) 45 | 46 | ### Contributing 47 | 48 | If you'd like to contribute to this project, please check out the [Code of Conduct](docs/CODE_OF_CONDUCT.md) and the [contributing](docs/CONTRIBUTING.md) guide. 49 | 50 | ### Versioning 51 | 52 | This project uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html). You can check out recent changes in the [changelog](CHANGELOG.md). 53 | -------------------------------------------------------------------------------- /docs/BACKGROUND.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | _Why do I need this?_ you may ask. The truth is that you may not need it if you are converting your values to ASCII strings and separating them with a known character (like a carriage return `\r` and a line feed `\n`) before sending them. This is what happens if you call and `Serial.println();`. For instance, if you just want to send a byte with the value of 255 and follow it with a new line character (i.e. `Serial.println(255);`) the Arduino automatically converts the number to the equivalent printable ASCII characters, sending 5 bytes total. As a result the receiver won't just receive a byte for the number and two bytes for the carriage return and new line character. Instead it will receive a stream of 5 bytes: 4 | 5 | ```console 6 | 50 // ASCII '2' 7 | 53 // ASCII '5' 8 | 53 // ASCII '5' 9 | 13 // ASCII '\r' 10 | 10 // ASCII '\n' 11 | ``` 12 | 13 | The receiver must then collect the 3 ASCII characters { '2', '5', '5' }, combine them and convert them back into a single byte with a value of `255`. This process can get complicated when the user wants to send large quantities of structured data between the Arduino and a receiver. 14 | 15 | One way to send a _packet_ of data without this library is to send each byte separated by a comma or space and terminate the sequence with a new line character. Thus, to send the value `255` and the value `10`, one might call: 16 | 17 | ```cpp 18 | Serial.print(255); 19 | Serial.print(','); 20 | Serial.print(10); 21 | Serial.print('\n'); 22 | ``` 23 | 24 | The receiver will actually see a stream of 7 bytes: 25 | 26 | ```console 27 | 50 // ASCII '2' 28 | 53 // ASCII '5' 29 | 53 // ASCII '5' 30 | 44 // ASCII ',' 31 | 49 // ASCII '1' 32 | 48 // ASCII '0' 33 | 10 // ASCII '\n' 34 | ``` 35 | 36 | In this case, the receiver must then collect the ASCII characters, combine them, skip the delimiter (the comma in this case) and then process the packet when a new line is encountered. While effective, this method doesn't scale particularly well. Bytes with values larger than 9 are encoded as 2 bytes and bytes with values larger than 99 are encoded as 3 bytes, etc. If the user would like to send the number 4,294,967,295 (the maximum value of a 4 byte `unsigned long`), it would be encoded as 10 bytes. This means that there is an overhead of 6 extra bytes to transmit a 4 byte `unsigned long`. 37 | 38 | An alternative to ASCII encoding is to write the bytes directly to using the `Serial.write()` methods. These methods do not convert the byte values to ASCII. So if the user wants to send a single byte with the value of `255` and follow it with a new line character: 39 | 40 | ```cpp 41 | Serial.write(255); 42 | Serial.write('\n'); 43 | ``` 44 | 45 | the receiver will see a stream of 2 bytes: 46 | 47 | ```console 48 | 255 // The value transmitted. 49 | 10 // The new line character (\n). 50 | ``` 51 | 52 | This is much more compact but can create problems when the user wants to send a _packet_ of data. If the user wants to send a packet consisting of two values such as 255 and 10, we run into problems if we also use the new line (`\n` ASCII 10) character as a packet boundary. This essentially means that the receiver will incorrectly think that a new packet is beginning when it receives the _value_ of 10. Thus, to use this more compact form of sending bytes while reserving one value for a packet boundary marker. Several unambiguous packet boundary marking encodings exist, but one with a small predictable overhead is called [Consistent Overhead Byte Stuffing](http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing). For a raw packet of length `SIZE`, the maximum encoded buffer size will only be `SIZE + SIZE / 254 + 1`. This is significantly less than ASCII encoding and the encoding / decoding algorithm is simple and fast. In its default mode, the COBS encoding process simply removes all _zeros_ from the packet, allowing the sender and receiver to use the value of _zero_ as a packet boundary marker. 53 | 54 | Another encoding available in `PacketSerial` is [Serial Line Internet Protocol](https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol) which is often used to send OSC over serial or TCP connections. To use SLIP encoding instead of COBS, use `SLIPPacketSerial` instead of `PacketSerial`. You can find an openFrameworks example of sending OSC data over serial in the [ofxSerial](https://github.com/bakercp/ofxSerial) repository. 55 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | - None 13 | 14 | ### Changed 15 | 16 | - None 17 | 18 | ### Removed 19 | 20 | - None 21 | 22 | ### Fixed 23 | 24 | - None 25 | 26 | ### Security 27 | 28 | - None 29 | 30 | ## [1.4.0] 2019-07-26 31 | 32 | ### Added 33 | 34 | - Added `const Stream* getStream() const` and `Stream* getStream()` methods. Thanks @orgicus. 35 | 36 | ### Changed 37 | 38 | - Updated README. 39 | 40 | ## [1.3.0] 2019-07-25 41 | 42 | ### Removed 43 | 44 | - Remove the `void begin(unsigned long speed, size_t port)` function. 45 | - Remove the `void begin(Stream* stream)` function. 46 | - `while(!SerialX)` line when `CORE_TEENSY` is defined. This was leading to unexpected behavior where programs would not start until a serial connection was opened. 47 | 48 | ### Added 49 | 50 | - Lambda function packetHandler examples and documentation. 51 | - Add `bool overflow() const` to check for a receive buffer overflow. 52 | - Added API documentation @ [http://bakercp.github.io/PacketSerial/](http://bakercp.github.io/PacketSerial/) 53 | 54 | ### Changed 55 | 56 | - Updated README.md, fixed spelling error, added links, docs. 57 | - Rewrote SLIP enum in terms of decimal notation for consistency. 58 | - Updated documentation folder, .github structure. 59 | - Updated CI testing. 60 | 61 | ## [1.2.0] 2017-11-09 62 | 63 | ### Added 64 | 65 | - An additional PacketHandler pointer type that includes the sender's pointer e.g. `void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size)`. Either functions can be set. Calling `setPacketHandler()` will always remove all previous function pointers. 66 | 67 | ### Removed 68 | 69 | - Deprecated all but one basic `void begin(Stream* stream)` function. Use `void setStream(Stream* stream)` instead. 70 | - Reverted void `PacketSerial_::begin(unsigned long speed, uint8_t config, size_t port)`. 71 | 72 | ### Changed 73 | 74 | - Updated README.md, fixed errors, spelling and updated examples. 75 | 76 | ## [1.1.0] 2017-11-09 77 | 78 | ### Added 79 | 80 | - Additional inline documentation. 81 | - Added doxygen configuration file. 82 | - Added CHANGELOG.md file. 83 | - Added the `void PacketSerial_::begin(unsigned long speed, uint8_t config, size_t port)` method to avoid confusion with the standard `Serial.begin(unsigned long speed, uint8_t config)`. 84 | 85 | ### Changed 86 | 87 | - Updated README.md, fixed errors, spelling, byte counts, etc. 88 | - Updated documentation / comments in documentation for clarity. 89 | 90 | ### Removed 91 | 92 | - Deprecated the `void begin(unsigned long speed, size_t port)` method because it could be confused with the standard `Serial.begin(unsigned long speed, uint8_t config)` method. 93 | 94 | ### Fixed 95 | 96 | - Fixed Duplicated SLIP END Packet #[11](https://github.com/bakercp/PacketSerial/issues/11) 97 | - Fix types to remove warnings in examples. 98 | - Add `const` qualifier to the `send()` method. 99 | 100 | ### Security 101 | 102 | - None 103 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [info@christopherbaker.net](mailto:info@christopherbaker.net?subject=[GitHub]%20Code%20Of%20Conduct). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][[homepage](https://www.contributor-covenant.org)], version 1.4, 71 | available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html). 72 | 73 | For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). 74 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Code of Conduct 4 | 5 | First, please see the [Code of Conduct](CODE_OF_CONDUCT.md). 6 | 7 | ## How to Help 8 | 9 | Check out the [Todo](TODO.md) list to see if there are any planned items. 10 | 11 | Check out the [Help Wanted](../../../issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) tag in the issues section for specific ideas or propose your own new ideas. 12 | 13 | ## Making Pull Requests 14 | 15 | Pull Requests are always welcome, so if you make any improvements please feel free to float them back upstream :) 16 | 17 | 1. Fork this repository. 18 | 2. Create your feature branch (`git checkout -b my-new-feature`). 19 | 3. Commit your changes (`git commit -am 'Add some feature'`). 20 | 4. Push to the branch (`git push origin my-new-feature`). 21 | 5. Create new Pull Request. 22 | 23 | New features should include tests (if possible) to confirm functionality. 24 | 25 | All code should be documented inline using [Doxygen](http://www.doxygen.nl/manual/docblocks.html) documentation. See the project's source for examples of the preferred style. 26 | -------------------------------------------------------------------------------- /docs/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | - Christopher Baker [@bakercp](https://github.com/bakercp) - Primary author. 4 | - Antoine Villeret [@avilleret](https://github.com/avilleret) - SLIP Encoding. 5 | - Anton Viktorov [@latonita](https://github.com/latonita) - Bugfixes. 6 | - [@per1234](https://github.com/per1234) - Metadata. 7 | - George Profenza [@orgicus](https://github.com/orgicus) - API Suggestions. 8 | 9 | _For and up-to-date list of contributors, see [contributors](../graphs/contributors)._ 10 | -------------------------------------------------------------------------------- /docs/Doxyfile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = PacketSerial 2 | PROJECT_BRIEF = "An Arduino Library that facilitates packet-based serial communication using COBS or SLIP encoding." 3 | INPUT = ../src \ 4 | . 5 | FILE_PATTERNS = *.c \ 6 | *.cpp \ 7 | *.h \ 8 | *.hpp \ 9 | *.md \ 10 | *.ino \ 11 | *.pde 12 | EXCLUDE = ./html 13 | EXCLUDE_PATTERNS = *.bak 14 | USE_MDFILE_AS_MAINPAGE = README.md 15 | HTML_OUTPUT = html 16 | GENERATE_TREEVIEW = YES 17 | GENERATE_TAGFILE = PacketSerial.xml 18 | GENERATE_LATEX = NO 19 | -------------------------------------------------------------------------------- /docs/GETTING_STARTED.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | First, it might be worth it to read the [background introduction](BACKGROUND.md). 4 | 5 | ## Installation 6 | 7 | This project can be installed using the Arduino IDE. Navigate to the _Tools > Manage Libraries ..._ menu item and search for `PacketSerial`. 8 | 9 | Alternatively, this library can be downloaded or cloned from github and to your Arduino `libraries` folder. 10 | 11 | ## Use 12 | 13 | `PacketSerial` class wraps the Arduino `Stream` class to automatically encode and decode byte packets that are sent and received. Typically serial communication uses the default `Serial` object, which implements the `Stream` class. In most cases, `PacketSerial` should be given exclusive access to the serial `Stream` (e.g. for a default setup using `Serial`, users should avoid calling functions like `Serial.print()`, `Serial.write()`, etc directly). Data should be sent via the `send(const uint8_t* buffer, size_t size) const` method and received in a `PacketSerial` callback function (see below). 14 | 15 | ## Setup 16 | 17 | ### Basic 18 | 19 | To use the default `Serial` object and the default communication settings (usually `SERIAL_8N1`), set up `PacketSerial` like this: 20 | 21 | ```cpp 22 | PacketSerial myPacketSerial; 23 | 24 | void setup() 25 | { 26 | myPacketSerial.begin(9600); 27 | myPacketSerial.setPacketHandler(&onPacketReceived); 28 | } 29 | ``` 30 | 31 | ### Advanced 32 | 33 | For a non-default Serial connection, a class implementing the `Stream` interface should be configured and then set for the `PacketSerial` instance. 34 | 35 | #### Using A Non-Standard Serial Configuration 36 | 37 | ```cpp 38 | PacketSerial myPacketSerial; 39 | 40 | void setup() 41 | { 42 | Serial.begin(300, SERIAL_7N1); 43 | myPacketSerial.setStream(&Serial); 44 | myPacketSerial.setPacketHandler(&onPacketReceived); 45 | } 46 | ``` 47 | 48 | #### Using Secondary Serial Ports (e.g. Serial1, Serial2, etc) 49 | 50 | ```cpp 51 | PacketSerial myPacketSerial; 52 | 53 | void setup() 54 | { 55 | Serial1.begin(9600); 56 | myPacketSerial.setStream(&Serial1); 57 | myPacketSerial.setPacketHandler(&onPacketReceived); 58 | } 59 | ``` 60 | 61 | #### Using SoftwareSerial 62 | 63 | ```cpp 64 | PacketSerial myPacketSerial; 65 | SoftwareSerial mySoftwareSerial(10, 11); 66 | 67 | void setup() 68 | { 69 | mySoftwareSerial.begin(38400); 70 | myPacketSerial.setStream(&mySoftwareSerial); 71 | myPacketSerial.setPacketHandler(&onPacketReceived); 72 | } 73 | ``` 74 | 75 | #### Other Streams 76 | 77 | Any class that correctly implements the `Stream` interface should work, which includes some network communication objects. 78 | 79 | ### Loop 80 | 81 | In order to processing incoming serial packets, the user must call the `update()` method at the end of the `loop()` method. 82 | 83 | ```cpp 84 | void loop() 85 | { 86 | // Your program here. 87 | 88 | 89 | // Call update to receive, decode and process incoming packets. 90 | myPacketSerial.update(); 91 | } 92 | 93 | ``` 94 | 95 | ### Receiving Packets 96 | 97 | All packets are received via handler functions. A typical handler function would be registered in the `void setup()` function like: 98 | 99 | ```cpp 100 | PacketSerial myPacketSerial; 101 | 102 | void setup() 103 | { 104 | myPacketSerial.begin(9600); 105 | myPacketSerial.setPacketHandler(&onPacketReceived); 106 | } 107 | ``` 108 | 109 | The `onPacketReceived` function can take two forms. The simplest looks like this: 110 | 111 | ```cpp 112 | void onPacketReceived(const uint8_t* buffer, size_t size) 113 | { 114 | // Process your decoded incoming packet here. 115 | } 116 | ``` 117 | 118 | For more advanced programs with multiple PacketSerial instances and a shared handler, it may be useful to know which PacketSerial instance received the packet. In this case you could define a callback like this: 119 | 120 | ```cpp 121 | void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size) 122 | { 123 | if (sender == &myPacketSerial) 124 | { 125 | // Do something with the packet from myPacketSerial. 126 | } 127 | else if (sender == &myOtherPacketSerial) 128 | { 129 | // Do something with the packet from myOtherPacketSerial. 130 | } 131 | } 132 | ``` 133 | 134 | Finally, it is also possible to set arbitrary packet handlers that point to member functions of a given class instance using lambda functions. For example: 135 | 136 | ```cpp 137 | // Instances of this class can receive data packets when registered. 138 | class MyClass 139 | { 140 | public: 141 | void processPacketFromSender(const PacketSerial& sender, const uint8_t* buffer, size_t size) 142 | { 143 | // Just send the buffer back to the sender. 144 | sender.send(buffer, size); 145 | } 146 | }; 147 | 148 | 149 | MyClass myClassInstance; 150 | PacketSerial myPacketSerial; 151 | 152 | void setup() 153 | { 154 | myPacketSerial.begin(115200); 155 | myPacketSerial.setPacketHandler([](const uint8_t* buffer, size_t size) { 156 | myClassInstance.processPacketFromSender(myPacketSerial, buffer, size); 157 | }); 158 | } 159 | ``` 160 | 161 | ### Sending Packets 162 | 163 | To send packets call the `send()` method. The send method will take a packet (an array of bytes), encode it, transmit it and send the packet boundary marker. To send the values `255` and `10`, one might do the following: 164 | 165 | ```cpp 166 | 167 | // Make an array. 168 | uint8_t myPacket[2] = { 255, 10 }; 169 | 170 | // Send the array. 171 | myPacketSerial.send(myPacket, 2); 172 | ``` 173 | 174 | ### Multiple Streams 175 | 176 | On boards with multiple serial ports, this strategy can also be used to set up two Serial streams, one for packets and one for debug ASCII (see [this discussion](https://github.com/bakercp/PacketSerial/issues/10) for more). 177 | 178 | ### Checking for Receive Buffer Overflows 179 | 180 | In some cases the receive buffer may not be large enough for an incoming encoded packet. 181 | 182 | To check for overflows, call the `receiveBufferOverflowed()` method after calling `update()`. 183 | 184 | For example: 185 | 186 | ```cpp 187 | void loop() 188 | { 189 | // Other program code. 190 | myPacketSerial.update(); 191 | 192 | // Check for a receive buffer overflow. 193 | if (myPacketSerial.overflow()) 194 | { 195 | // Send an alert via a pin (e.g. make an overflow LED) or return a 196 | // user-defined packet to the sender. 197 | // 198 | // Ultimately you may need to just increase your recieve buffer via the 199 | // template parameters. 200 | } 201 | } 202 | ``` 203 | 204 | The state of the overflow flag is reset every time a new packet marker is detected, NOT when the `overflow()` method is called. 205 | 206 | ### Customizing the PacketSerial Class 207 | 208 | The `PacketSerial_` class is a templated class that allows us to statically set the encoder type, packet marker and buffer size at compile time. 209 | 210 | The the template parameters are as follows: 211 | 212 | ```cpp 213 | template 214 | class PacketSerial_ 215 | 216 | (...) 217 | ``` 218 | 219 | The `PacketMarker` has a default of `0` while the `BufferSize` has a default of `256` bytes. 220 | 221 | Thus, if you define your class as: 222 | 223 | ```cpp 224 | PacketSerial_ myPacketSerial; 225 | ``` 226 | 227 | You will use the `COBS` encoder type and a default PacketMarker of `0` and buffer size of `256`. 228 | 229 | Currently there are three default `PacketSerial_` types defined via `typedef` for convenience: 230 | 231 | ```cpp 232 | /// \brief A typedef for the default COBS PacketSerial class. 233 | typedef PacketSerial_ PacketSerial; 234 | 235 | /// \brief A typedef for a PacketSerial type with COBS encoding. 236 | typedef PacketSerial_ COBSPacketSerial; 237 | 238 | /// \brief A typedef for a PacketSerial type with SLIP encoding. 239 | typedef PacketSerial_ SLIPPacketSerial; 240 | ``` 241 | 242 | ### Changing the EncoderType Type 243 | 244 | To use a custom encoding type, the `EncoderType` class must implement the following functions: 245 | 246 | ```cpp 247 | static size_t encode(const uint8_t* buffer, size_t size, uint8_t* encodedBuffer); 248 | static size_t decode(const uint8_t* encodedBuffer, size_t size, uint8_t* decodedBuffer); 249 | static size_t getEncodedBufferSize(size_t unencodedBufferSize); 250 | ``` 251 | 252 | See the `Encoding/COBS.h` and `Encoding/SLIP.h` for examples and further documentation. 253 | 254 | ### Changing the Packet Marker Byte and Receive Buffer Size 255 | 256 | For example, to increase the buffer size for a standard `COBS` encoder to 512, one can defined the templated class like this: 257 | 258 | ```cpp 259 | PacketSerial_ myPacketSerial; 260 | ``` 261 | 262 | This uses the COBS encoder type, a PacketMarker of 0 and a buffer size of 512. 263 | 264 | Likewise, a custom `SLIP` encoder with a buffer size of 512 bytes would be defined like this: 265 | 266 | ```cpp 267 | PacketSerial_ myPacketSerial; 268 | ``` -------------------------------------------------------------------------------- /docs/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | First, read the [Getting Started](GETTING_STARTED.md) guide and see the [Troubleshooting](TROUBLESHOOTING.md) guide. You might also check and see if there are any [existing issues](../../../issues?utf8=✓&q=is%3Aissue) that are helpful to you. If those don't help and you, create a new [issue](../../../issues). 4 | 5 | Please do not email the author directly with questions. 6 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | First check out the [Help Wanted](../../../issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) tag in the issues section for specific ideas. 4 | 5 | ## Future Plans 6 | 7 | Right now the encoder / decoder works by encoding or decoding a full buffer. SLIP (and COBS?) encoding, for instance, can be encoded / decoded on the fly, allowing for a smaller buffer allocation. https://github.com/CNMAT/OSC uses an "on-the-fly" approach, rather than a large buffer approach. It would be interesting to investigate this. 8 | -------------------------------------------------------------------------------- /docs/TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | There are no additional _Troubleshooting_ notes at steps time. 4 | -------------------------------------------------------------------------------- /examples/PacketSerialReverseEcho/PacketSerialReverseEcho.ino: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2012 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include 9 | 10 | 11 | // By default, PacketSerial automatically wraps the built-in `Serial` object. 12 | // While it is still possible to use the Serial object directly, it is 13 | // recommended that the user let the PacketSerial object manage all serial 14 | // communication. Thus the user should not call Serial.write(), Serial.print(), 15 | // etc. Additionally the user should not use the serialEvent() framework. 16 | // 17 | // By default, PacketSerial uses COBS encoding and has a 256 byte receive 18 | // buffer. This can be adjusted by the user by replacing `PacketSerial` with 19 | // a variation of the `PacketSerial_` template found in 20 | // PacketSerial.h. 21 | PacketSerial myPacketSerial; 22 | 23 | 24 | void setup() 25 | { 26 | // We begin communication with our PacketSerial object by setting the 27 | // communication speed in bits / second (baud). 28 | myPacketSerial.begin(115200); 29 | 30 | // If we want to receive packets, we must specify a packet handler function. 31 | // The packet handler is a custom function with a signature like the 32 | // onPacketReceived function below. 33 | myPacketSerial.setPacketHandler(&onPacketReceived); 34 | } 35 | 36 | 37 | void loop() 38 | { 39 | // Do your program-specific loop() work here as usual. 40 | 41 | // The PacketSerial::update() method attempts to read in any incoming serial 42 | // data and emits received and decoded packets via the packet handler 43 | // function specified by the user in the void setup() function. 44 | // 45 | // The PacketSerial::update() method should be called once per loop(). Failure 46 | // to call the PacketSerial::update() frequently enough may result in buffer 47 | // serial overflows. 48 | myPacketSerial.update(); 49 | 50 | // Check for a receive buffer overflow (optional). 51 | if (myPacketSerial.overflow()) 52 | { 53 | // Send an alert via a pin (e.g. make an overflow LED) or return a 54 | // user-defined packet to the sender. 55 | // 56 | // Ultimately you may need to just increase your recieve buffer via the 57 | // template parameters (see the README.md). 58 | } 59 | } 60 | 61 | // This is our handler callback function. 62 | // When an encoded packet is received and decoded, it will be delivered here. 63 | // The `buffer` is a pointer to the decoded byte array. `size` is the number of 64 | // bytes in the `buffer`. 65 | void onPacketReceived(const uint8_t* buffer, size_t size) 66 | { 67 | // In this example, we will simply reverse the contents of the array and send 68 | // it back to the sender. 69 | 70 | // Make a temporary buffer. 71 | uint8_t tempBuffer[size]; 72 | 73 | // Copy the packet into our temporary buffer. 74 | memcpy(tempBuffer, buffer, size); 75 | 76 | // Reverse our temporaray buffer. 77 | reverse(tempBuffer, size); 78 | 79 | // Send the reversed buffer back to the sender. The send() method will encode 80 | // the whole buffer as as single packet, set packet markers, etc. 81 | // The `tempBuffer` is a pointer to the `tempBuffer` array and `size` is the 82 | // number of bytes to send in the `tempBuffer`. 83 | myPacketSerial.send(tempBuffer, size); 84 | } 85 | 86 | // This function takes a byte buffer and reverses it. 87 | void reverse(uint8_t* buffer, size_t size) 88 | { 89 | uint8_t tmp; 90 | 91 | for (size_t i = 0; i < size / 2; i++) 92 | { 93 | tmp = buffer[i]; 94 | buffer[i] = buffer[size - i - 1]; 95 | buffer[size - i - 1] = tmp; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoAdvanced/.due.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakercp/PacketSerial/92c4bae0bd9340cb0b265789885bf8873402ed8f/examples/PacketSerialReverseEchoAdvanced/.due.test.skip -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoAdvanced/.esp32.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakercp/PacketSerial/92c4bae0bd9340cb0b265789885bf8873402ed8f/examples/PacketSerialReverseEchoAdvanced/.esp32.test.skip -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoAdvanced/.m4.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakercp/PacketSerial/92c4bae0bd9340cb0b265789885bf8873402ed8f/examples/PacketSerialReverseEchoAdvanced/.m4.test.skip -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoAdvanced/.zero.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakercp/PacketSerial/92c4bae0bd9340cb0b265789885bf8873402ed8f/examples/PacketSerialReverseEchoAdvanced/.zero.test.skip -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoAdvanced/PacketSerialReverseEchoAdvanced.ino: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2012 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include 9 | #include 10 | 11 | 12 | // Instances of this class can recieve data packets when registered. 13 | class MyClass 14 | { 15 | public: 16 | void processPacketFromSender(const PacketSerial& sender, const uint8_t* buffer, size_t size) 17 | { 18 | // Just send the buffer back to the sender. 19 | sender.send(buffer, size); 20 | } 21 | }; 22 | 23 | // By default, PacketSerial automatically wraps the built-in `Serial` object. 24 | // While it is still possible to use the Serial object directly, it is 25 | // recommended that the user let the PacketSerial object manage all serial 26 | // communication. Thus the user should not call Serial.write(), Serial.print(), 27 | // etc. Additionally the user should not use the serialEvent() framework. 28 | // 29 | // By default, PacketSerial uses COBS encoding and has a 256 byte receive 30 | // buffer. This can be adjusted by the user by replacing `PacketSerial` with 31 | // a variation of the `PacketSerial_` template found in 32 | // PacketSerial.h. 33 | PacketSerial myPacketSerial; 34 | 35 | 36 | // Note that SoftwareSerial is not compatible with SAMD_ZERO 37 | 38 | // An additional PacketSerial instance. 39 | SoftwareSerial mySoftwareSerial(10, 11); 40 | PacketSerial myOtherPacketSerial; 41 | 42 | // An instance of our custom class. 43 | MyClass myClassInstance; 44 | 45 | void setup() 46 | { 47 | // We begin communication with our PacketSerial object by setting the 48 | // communication speed in bits / second (baud). 49 | myPacketSerial.begin(115200); 50 | 51 | // If we want to receive packets, we must specify a packet handler function. 52 | // The packet handler is a custom function with a signature like the 53 | // onPacketReceived function below. 54 | myPacketSerial.setPacketHandler(&onPacketReceived); 55 | 56 | // Set up a scond custom Serial connection on Serial1. 57 | mySoftwareSerial.begin(9600); 58 | myOtherPacketSerial.setStream(&mySoftwareSerial); 59 | 60 | // Here we set the packet handler to be a member of the given instance of 61 | // MyClass using a lambda function. Static variables (e.g. myClassInstance) 62 | // don't need to be captured. Additionally the member function of MyClass 63 | // that processes the packet isn't required to match the packet handler 64 | // function signature. 65 | myOtherPacketSerial.setPacketHandler([](const uint8_t* buffer, size_t size) { 66 | myClassInstance.processPacketFromSender(myOtherPacketSerial, buffer, size); 67 | }); 68 | } 69 | 70 | 71 | void loop() 72 | { 73 | // Do your program-specific loop() work here as usual. 74 | 75 | // The PacketSerial::update() method attempts to read in any incoming serial 76 | // data and emits received and decoded packets via the packet handler 77 | // function specified by the user in the void setup() function. 78 | // 79 | // The PacketSerial::update() method should be called once per loop(). Failure 80 | // to call the PacketSerial::update() frequently enough may result in buffer 81 | // serial overflows. 82 | myPacketSerial.update(); 83 | 84 | // Check for a receive buffer overflow (optional). 85 | if (myPacketSerial.overflow()) 86 | { 87 | // Send an alert via a pin (e.g. make an overflow LED) or return a 88 | // user-defined packet to the sender. 89 | // 90 | // Ultimately you may need to just increase your recieve buffer via the 91 | // template parameters (see the README.md). 92 | } 93 | } 94 | 95 | // This is our handler callback function. 96 | // When an encoded packet is received and decoded, it will be delivered here. 97 | // The sender is a pointer to the sending PacketSerial instance. The `buffer` is 98 | // a pointer to the decoded byte array. `size` is the number of bytes in the 99 | // `buffer`. 100 | void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size) 101 | { 102 | if (sender == &myPacketSerial) 103 | { 104 | // In this example, we will simply reverse the contents of the array and send 105 | // it back to the sender. 106 | // Make a temporary buffer. 107 | uint8_t tempBuffer[size]; 108 | 109 | // Copy the packet into our temporary buffer. 110 | memcpy(tempBuffer, buffer, size); 111 | 112 | // Reverse our temporaray buffer. 113 | reverse(tempBuffer, size); 114 | 115 | // Send the reversed buffer back to the sender. The send() method will encode 116 | // the whole buffer as as single packet, set packet markers, etc. 117 | // The `tempBuffer` is a pointer to the `tempBuffer` array and `size` is the 118 | // number of bytes to send in the `tempBuffer`. 119 | myPacketSerial.send(tempBuffer, size); 120 | } 121 | else if (sender == &myOtherPacketSerial) 122 | { 123 | // Just send it back without reversing it. 124 | myOtherPacketSerial.send(buffer, size); 125 | } 126 | } 127 | 128 | // This function takes a byte buffer and reverses it. 129 | void reverse(uint8_t* buffer, size_t size) 130 | { 131 | uint8_t tmp; 132 | 133 | for (size_t i = 0; i < size / 2; i++) 134 | { 135 | tmp = buffer[i]; 136 | buffer[i] = buffer[size - i - 1]; 137 | buffer[size - i - 1] = tmp; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoAdvanced/README.md: -------------------------------------------------------------------------------- 1 | # Note 2 | 3 | This example will not compile on boards that don't support `SoftwareSerial`. This includes DUE, Zero, etc. 4 | 5 | That said, the concept of setting a custom `Stream` output will apply to any `Stream` supported by those boards. 6 | -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoClass/PacketSerialReverseEchoClass.ino: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2012 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | // This example is PacketSerialReverseEcho modified to use PacketSerial from within a class 8 | 9 | #include 10 | 11 | // This function takes a byte buffer and reverses it. 12 | void reverse(uint8_t* buffer, size_t size) 13 | { 14 | uint8_t tmp; 15 | 16 | for (size_t i = 0; i < size / 2; i++) 17 | { 18 | tmp = buffer[i]; 19 | buffer[i] = buffer[size - i - 1]; 20 | buffer[size - i - 1] = tmp; 21 | } 22 | } 23 | 24 | class EchoClass { 25 | public: 26 | void begin(unsigned long speed) { 27 | // If we want to receive packets, we must specify a packet handler function. 28 | // The packet handler is a custom function with a signature like the 29 | // onPacketReceived function below. 30 | myPacketSerial.setPacketHandler(&onPacketReceived, this); 31 | 32 | myPacketSerial.begin(speed); 33 | } 34 | 35 | void loop() { 36 | // The PacketSerial::update() method attempts to read in any incoming serial 37 | // data and emits received and decoded packets via the packet handler 38 | // function specified by the user in the void setup() function. 39 | // 40 | // The PacketSerial::update() method should be called once per loop(). Failure 41 | // to call the PacketSerial::update() frequently enough may result in buffer 42 | // serial overflows. 43 | myPacketSerial.update(); 44 | 45 | // Check for a receive buffer overflow (optional). 46 | if (myPacketSerial.overflow()) 47 | { 48 | // Send an alert via a pin (e.g. make an overflow LED) or return a 49 | // user-defined packet to the sender. 50 | // 51 | // Ultimately you may need to just increase your recieve buffer via the 52 | // template parameters (see the README.md). 53 | } 54 | } 55 | 56 | private: 57 | // C-style callbacks can't use non-static methods, so we use a static method that receives "this" as the sender argument: https://wiki.c2.com/?VirtualStaticIdiom 58 | static void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size) { 59 | ((EchoClass*)sender)->onPacketReceived(buffer, size); 60 | } 61 | 62 | // This is our handler callback function. 63 | // When an encoded packet is received and decoded, it will be delivered here. 64 | // The `buffer` is a pointer to the decoded byte array. `size` is the number of 65 | // bytes in the `buffer`. 66 | void onPacketReceived(const uint8_t* buffer, size_t size) { 67 | // In this example, we will simply reverse the contents of the array and send 68 | // it back to the sender. 69 | 70 | // Make a temporary buffer. 71 | uint8_t tempBuffer[size]; 72 | 73 | // Copy the packet into our temporary buffer. 74 | memcpy(tempBuffer, buffer, size); 75 | 76 | // Reverse our temporaray buffer. 77 | reverse(tempBuffer, size); 78 | 79 | // Send the reversed buffer back to the sender. The send() method will encode 80 | // the whole buffer as as single packet, set packet markers, etc. 81 | // The `tempBuffer` is a pointer to the `tempBuffer` array and `size` is the 82 | // number of bytes to send in the `tempBuffer`. 83 | myPacketSerial.send(tempBuffer, size); 84 | } 85 | 86 | PacketSerial myPacketSerial; 87 | }; 88 | 89 | // By default, PacketSerial automatically wraps the built-in `Serial` object. 90 | // While it is still possible to use the Serial object directly, it is 91 | // recommended that the user let the PacketSerial object manage all serial 92 | // communication. Thus the user should not call Serial.write(), Serial.print(), 93 | // etc. Additionally the user should not use the serialEvent() framework. 94 | // 95 | // By default, PacketSerial uses COBS encoding and has a 256 byte receive 96 | // buffer. This can be adjusted by the user by replacing `PacketSerial` with 97 | // a variation of the `PacketSerial_` template found in 98 | // PacketSerial.h. 99 | 100 | EchoClass myEchoClass; 101 | 102 | void setup() 103 | { 104 | // We begin communication with our PacketSerial object by setting the 105 | // communication speed in bits / second (baud). 106 | myEchoClass.begin(115200); 107 | } 108 | 109 | 110 | void loop() 111 | { 112 | // Do your program-specific loop() work here as usual. 113 | 114 | myEchoClass.loop(); 115 | } 116 | -------------------------------------------------------------------------------- /examples/PacketSerialReverseEchoSLIP/PacketSerialReverseEchoSLIP.ino: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2012 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include 9 | 10 | 11 | // By default, PacketSerial automatically wraps the built-in `Serial` object. 12 | // While it is still possible to use the Serial object directly, it is 13 | // recommended that the user let the PacketSerial object manage all serial 14 | // communication. Thus the user should not call Serial.write(), Serial.print(), 15 | // etc. Additionally the user should not use the serialEvent() framework. 16 | // 17 | // By default, SLIPPacketSerial uses SLIP encoding and has a 256 byte receive 18 | // buffer. This can be adjusted by the user by replacing `SLIPPacketSerial` 19 | // with a variation of the `PacketSerial_` template 20 | // found in PacketSerial.h. 21 | SLIPPacketSerial myPacketSerial; 22 | 23 | 24 | void setup() 25 | { 26 | // We begin communication with our PacketSerial object by setting the 27 | // communication speed in bits / second (baud). 28 | myPacketSerial.begin(115200); 29 | 30 | // If we want to receive packets, we must specify a packet handler function. 31 | // The packet handler is a custom function with a signature like the 32 | // onPacketReceived function below. 33 | myPacketSerial.setPacketHandler(&onPacketReceived); 34 | } 35 | 36 | 37 | void loop() 38 | { 39 | // Do your program-specific loop() work here as usual. 40 | 41 | // The PacketSerial::update() method attempts to read in any incoming serial 42 | // data and emits received and decoded packets via the packet handler 43 | // function specified by the user in the void setup() function. 44 | // 45 | // The PacketSerial::update() method should be called once per loop(). Failure 46 | // to call the PacketSerial::update() frequently enough may result in buffer 47 | // serial overflows. 48 | myPacketSerial.update(); 49 | 50 | // Check for a receive buffer overflow (optional). 51 | if (myPacketSerial.overflow()) 52 | { 53 | // Send an alert via a pin (e.g. make an overflow LED) or return a 54 | // user-defined packet to the sender. 55 | // 56 | // Ultimately you may need to just increase your recieve buffer via the 57 | // template parameters (see the README.md). 58 | } 59 | } 60 | 61 | // This is our handler callback function. 62 | // When an encoded packet is received and decoded, it will be delivered here. 63 | // The `buffer` is a pointer to the decoded byte array. `size` is the number of 64 | // bytes in the `buffer`. 65 | void onPacketReceived(const uint8_t* buffer, size_t size) 66 | { 67 | // In this example, we will simply reverse the contents of the array and send 68 | // it back to the sender. 69 | 70 | // Make a temporary buffer. 71 | uint8_t tempBuffer[size]; 72 | 73 | // Copy the packet into our temporary buffer. 74 | memcpy(tempBuffer, buffer, size); 75 | 76 | // Reverse our temporaray buffer. 77 | reverse(tempBuffer, size); 78 | 79 | // Send the reversed buffer back to the sender. The send() method will encode 80 | // the whole buffer as as single packet, set packet markers, etc. 81 | // The `tempBuffer` is a pointer to the `tempBuffer` array and `size` is the 82 | // number of bytes to send in the `tempBuffer`. 83 | myPacketSerial.send(tempBuffer, size); 84 | } 85 | 86 | // This function takes a byte buffer and reverses it. 87 | void reverse(uint8_t* buffer, size_t size) 88 | { 89 | uint8_t tmp; 90 | 91 | for (size_t i = 0; i < size / 2; i++) 92 | { 93 | tmp = buffer[i]; 94 | buffer[i] = buffer[size - i - 1]; 95 | buffer[size - i - 1] = tmp; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Arduration 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | PacketSerial_ KEYWORD1 9 | PacketSerial KEYWORD1 10 | COBSPacketSerial KEYWORD1 11 | SLIPPacketSerial KEYWORD1 12 | 13 | SLIP KEYWORD1 14 | COBS KEYWORD1 15 | 16 | ####################################### 17 | # Methods and Functions (KEYWORD2) 18 | ####################################### 19 | 20 | encode KEYWORD2 21 | decode KEYWORD2 22 | getEncodedBufferSize KEYWORD2 23 | send KEYWORD2 24 | setPacketHandler KEYWORD2 25 | update KEYWORD2 26 | begin KEYWORD2 27 | setStream KEYWORD2 28 | onPacketReceived KEYWORD2 29 | 30 | ####################################### 31 | # Constants (LITERAL1) 32 | ####################################### 33 | 34 | END LITERAL1 35 | ESC LITERAL1 36 | ESC_END LITERAL1 37 | ESC_ESC LITERAL1 38 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PacketSerial 2 | version=1.4.0 3 | author=Christopher Baker 4 | maintainer=Christopher Baker 5 | sentence=An Arduino Library that facilitates packet-based serial communication using COBS or SLIP encoding. 6 | paragraph=PacketSerial is an small, efficient, library that allows Arduinos to send and receive serial data packets (with COBS, SLIP or a user-defined encoding) that include bytes of any value (0 - 255). A packet is simply an array of bytes. 7 | category=Communication 8 | url=https://github.com/bakercp/PacketSerial 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/Encoding/COBS.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011 Christopher Baker 3 | // Copyright (c) 2011 Jacques Fortier 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #pragma once 10 | 11 | 12 | #include "Arduino.h" 13 | 14 | 15 | /// \brief A Consistent Overhead Byte Stuffing (COBS) Encoder. 16 | /// 17 | /// Consistent Overhead Byte Stuffing (COBS) is an encoding that removes all 0 18 | /// bytes from arbitrary binary data. The encoded data consists only of bytes 19 | /// with values from 0x01 to 0xFF. This is useful for preparing data for 20 | /// transmission over a serial link (RS-232 or RS-485 for example), as the 0 21 | /// byte can be used to unambiguously indicate packet boundaries. COBS also has 22 | /// the advantage of adding very little overhead (at least 1 byte, plus up to an 23 | /// additional byte per 254 bytes of data). For messages smaller than 254 bytes, 24 | /// the overhead is constant. 25 | /// 26 | /// \sa http://conferences.sigcomm.org/sigcomm/1997/papers/p062.pdf 27 | /// \sa http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing 28 | /// \sa https://github.com/jacquesf/COBS-Consistent-Overhead-Byte-Stuffing 29 | /// \sa http://www.jacquesf.com/2011/03/consistent-overhead-byte-stuffing 30 | class COBS 31 | { 32 | public: 33 | /// \brief Encode a byte buffer with the COBS encoder. 34 | /// \param buffer A pointer to the unencoded buffer to encode. 35 | /// \param size The number of bytes in the \p buffer. 36 | /// \param encodedBuffer The buffer for the encoded bytes. 37 | /// \returns The number of bytes written to the \p encodedBuffer. 38 | /// \warning The encodedBuffer must have at least getEncodedBufferSize() 39 | /// allocated. 40 | static size_t encode(const uint8_t* buffer, 41 | size_t size, 42 | uint8_t* encodedBuffer) 43 | { 44 | size_t read_index = 0; 45 | size_t write_index = 1; 46 | size_t code_index = 0; 47 | uint8_t code = 1; 48 | 49 | while (read_index < size) 50 | { 51 | if (buffer[read_index] == 0) 52 | { 53 | encodedBuffer[code_index] = code; 54 | code = 1; 55 | code_index = write_index++; 56 | read_index++; 57 | } 58 | else 59 | { 60 | encodedBuffer[write_index++] = buffer[read_index++]; 61 | code++; 62 | 63 | if (code == 0xFF) 64 | { 65 | encodedBuffer[code_index] = code; 66 | code = 1; 67 | code_index = write_index++; 68 | } 69 | } 70 | } 71 | 72 | encodedBuffer[code_index] = code; 73 | 74 | return write_index; 75 | } 76 | 77 | 78 | /// \brief Decode a COBS-encoded buffer. 79 | /// \param encodedBuffer A pointer to the \p encodedBuffer to decode. 80 | /// \param size The number of bytes in the \p encodedBuffer. 81 | /// \param decodedBuffer The target buffer for the decoded bytes. 82 | /// \returns The number of bytes written to the \p decodedBuffer. 83 | /// \warning decodedBuffer must have a minimum capacity of size. 84 | static size_t decode(const uint8_t* encodedBuffer, 85 | size_t size, 86 | uint8_t* decodedBuffer) 87 | { 88 | if (size == 0) 89 | return 0; 90 | 91 | size_t read_index = 0; 92 | size_t write_index = 0; 93 | uint8_t code = 0; 94 | uint8_t i = 0; 95 | 96 | while (read_index < size) 97 | { 98 | code = encodedBuffer[read_index]; 99 | 100 | if (read_index + code > size && code != 1) 101 | { 102 | return 0; 103 | } 104 | 105 | read_index++; 106 | 107 | for (i = 1; i < code; i++) 108 | { 109 | decodedBuffer[write_index++] = encodedBuffer[read_index++]; 110 | } 111 | 112 | if (code != 0xFF && read_index != size) 113 | { 114 | decodedBuffer[write_index++] = '\0'; 115 | } 116 | } 117 | 118 | return write_index; 119 | } 120 | 121 | /// \brief Get the maximum encoded buffer size for an unencoded buffer size. 122 | /// \param unencodedBufferSize The size of the buffer to be encoded. 123 | /// \returns the maximum size of the required encoded buffer. 124 | static size_t getEncodedBufferSize(size_t unencodedBufferSize) 125 | { 126 | return unencodedBufferSize + unencodedBufferSize / 254 + 1; 127 | } 128 | 129 | }; 130 | -------------------------------------------------------------------------------- /src/Encoding/SLIP.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2010 Christopher Baker 3 | // Copyright (c) 2016 Antoine Villeret 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #pragma once 10 | 11 | 12 | #include "Arduino.h" 13 | 14 | 15 | /// \brief A Serial Line Internet Protocol (SLIP) Encoder. 16 | /// 17 | /// Serial Line Internet Protocol (SLIP) is a packet framing protocol: SLIP 18 | /// defines a sequence of characters that frame IP packets on a serial line and 19 | /// nothing more. It provides no addressing, packet type identification, error 20 | /// detection, correction or compression mechanisms. Because the protocol does 21 | /// so little its implementation is trivial and fast. 22 | /// 23 | /// \sa http://tools.ietf.org/html/rfc1055 24 | class SLIP 25 | { 26 | public: 27 | /// \brief Encode a byte buffer with the SLIP encoder. 28 | /// \param buffer A pointer to the unencoded buffer to encode. 29 | /// \param size The number of bytes in the \p buffer. 30 | /// \param encodedBuffer The buffer for the encoded bytes. 31 | /// \returns The number of bytes written to the \p encodedBuffer. 32 | /// \warning The encodedBuffer must have at least getEncodedBufferSize() 33 | /// allocated. 34 | static size_t encode(const uint8_t* buffer, 35 | size_t size, 36 | uint8_t* encodedBuffer) 37 | { 38 | if (size == 0) 39 | return 0; 40 | 41 | size_t read_index = 0; 42 | size_t write_index = 0; 43 | 44 | // Double-ENDed, flush any data that may have accumulated due to line 45 | // noise. 46 | encodedBuffer[write_index++] = END; 47 | 48 | while (read_index < size) 49 | { 50 | if(buffer[read_index] == END) 51 | { 52 | encodedBuffer[write_index++] = ESC; 53 | encodedBuffer[write_index++] = ESC_END; 54 | read_index++; 55 | } 56 | else if(buffer[read_index] == ESC) 57 | { 58 | encodedBuffer[write_index++] = ESC; 59 | encodedBuffer[write_index++] = ESC_ESC; 60 | read_index++; 61 | } 62 | else 63 | { 64 | encodedBuffer[write_index++] = buffer[read_index++]; 65 | } 66 | } 67 | 68 | return write_index; 69 | } 70 | 71 | /// \brief Decode a SLIP-encoded buffer. 72 | /// \param encodedBuffer A pointer to the \p encodedBuffer to decode. 73 | /// \param size The number of bytes in the \p encodedBuffer. 74 | /// \param decodedBuffer The target buffer for the decoded bytes. 75 | /// \returns The number of bytes written to the \p decodedBuffer. 76 | /// \warning decodedBuffer must have a minimum capacity of size. 77 | static size_t decode(const uint8_t* encodedBuffer, 78 | size_t size, 79 | uint8_t* decodedBuffer) 80 | { 81 | if (size == 0) 82 | return 0; 83 | 84 | size_t read_index = 0; 85 | size_t write_index = 0; 86 | 87 | while (read_index < size) 88 | { 89 | if (encodedBuffer[read_index] == END) 90 | { 91 | // flush or done 92 | read_index++; 93 | } 94 | else if (encodedBuffer[read_index] == ESC) 95 | { 96 | if (encodedBuffer[read_index+1] == ESC_END) 97 | { 98 | decodedBuffer[write_index++] = END; 99 | read_index += 2; 100 | } 101 | else if (encodedBuffer[read_index+1] == ESC_ESC) 102 | { 103 | decodedBuffer[write_index++] = ESC; 104 | read_index += 2; 105 | } 106 | else 107 | { 108 | // This case is considered a protocol violation. 109 | } 110 | } 111 | else 112 | { 113 | decodedBuffer[write_index++] = encodedBuffer[read_index++]; 114 | } 115 | } 116 | 117 | return write_index; 118 | } 119 | 120 | /// \brief Get the maximum encoded buffer size for an unencoded buffer size. 121 | /// 122 | /// SLIP has a start and end markers (192 and 219). Marker value is 123 | /// replaced by 2 bytes in the encoded buffer. So in the worst case of 124 | /// sending a buffer with only '192' or '219', the encoded buffer length 125 | /// will be 2 * buffer.size() + 2. 126 | /// 127 | /// \param unencodedBufferSize The size of the buffer to be encoded. 128 | /// \returns the maximum size of the required encoded buffer. 129 | static size_t getEncodedBufferSize(size_t unencodedBufferSize) 130 | { 131 | return unencodedBufferSize * 2 + 2; 132 | } 133 | 134 | /// \brief Key constants used in the SLIP protocol. 135 | enum 136 | { 137 | /// \brief The decimal END character (octal 0300). 138 | /// 139 | /// Indicates the end of a packet. 140 | END = 192, 141 | 142 | /// \brief The decimal ESC character (octal 0333). 143 | /// 144 | /// Indicates byte stuffing. 145 | ESC = 219, 146 | 147 | /// \brief The decimal ESC_END character (octal 0334). 148 | /// 149 | /// ESC ESC_END means END data byte. 150 | ESC_END = 220, 151 | 152 | /// \brief The decimal ESC_ESC character (ocatal 0335). 153 | /// 154 | /// ESC ESC_ESC means ESC data byte. 155 | ESC_ESC = 221 156 | }; 157 | 158 | }; 159 | -------------------------------------------------------------------------------- /src/PacketSerial.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include 12 | #include "Encoding/COBS.h" 13 | #include "Encoding/SLIP.h" 14 | 15 | 16 | /// \brief A template class enabling packet-based Serial communication. 17 | /// 18 | /// Typically one of the typedefined versions are used, for example, 19 | /// `COBSPacketSerial` or `SLIPPacketSerial`. 20 | /// 21 | /// The template parameters allow the user to define their own packet encoder / 22 | /// decoder, custom packet marker and receive buffer size. 23 | /// 24 | /// \tparam EncoderType The static packet encoder class name. 25 | /// \tparam PacketMarker The byte value used to mark the packet boundary. 26 | /// \tparam BufferSize The number of bytes allocated for the receive buffer. 27 | template 28 | class PacketSerial_ 29 | { 30 | public: 31 | /// \brief A typedef describing the packet handler method. 32 | /// 33 | /// The packet handler method usually has the form: 34 | /// 35 | /// void onPacketReceived(const uint8_t* buffer, size_t size); 36 | /// 37 | /// where buffer is a pointer to the incoming buffer array, and size is the 38 | /// number of bytes in the incoming buffer. 39 | typedef void (*PacketHandlerFunction)(const uint8_t* buffer, size_t size); 40 | 41 | /// \brief A typedef describing the packet handler method. 42 | /// 43 | /// The packet handler method usually has the form: 44 | /// 45 | /// void onPacketReceived(void* sender, const uint8_t* buffer, size_t size); 46 | /// 47 | /// where sender is a pointer to the PacketSerial_ instance that recieved 48 | /// the buffer, buffer is a pointer to the incoming buffer array, and size 49 | /// is the number of bytes in the incoming buffer. 50 | typedef void (*PacketHandlerFunctionWithSender)(const void* sender, const uint8_t* buffer, size_t size); 51 | 52 | /// \brief Construct a default PacketSerial_ device. 53 | PacketSerial_(): 54 | _receiveBufferIndex(0), 55 | _stream(nullptr), 56 | _onPacketFunction(nullptr), 57 | _onPacketFunctionWithSender(nullptr), 58 | _senderPtr(nullptr) 59 | { 60 | } 61 | 62 | /// \brief Destroy the PacketSerial_ device. 63 | ~PacketSerial_() 64 | { 65 | } 66 | 67 | /// \brief Begin a default serial connection with the given speed. 68 | /// 69 | /// The default Serial port `Serial` and default config `SERIAL_8N1` will be 70 | /// used. For example: 71 | /// 72 | /// PacketSerial myPacketSerial; 73 | /// 74 | /// void setup() 75 | /// { 76 | /// myPacketSerial.begin(9600); 77 | /// } 78 | /// 79 | /// This is a convenience method. For more complex Serial port 80 | /// configurations, use the `setStream()` function to set an arbitrary 81 | /// Arduino Stream. 82 | /// 83 | /// \param speed The serial data transmission speed in bits / second (baud). 84 | /// \sa https://www.arduino.cc/en/Serial/Begin 85 | void begin(unsigned long speed) 86 | { 87 | Serial.begin(speed); 88 | #if ARDUINO >= 100 && !defined(CORE_TEENSY) 89 | while (!Serial) {;} 90 | #endif 91 | setStream(&Serial); 92 | } 93 | 94 | /// \brief Deprecated. Use setStream() to configure a non-default port. 95 | /// \param speed The serial data transmission speed in bits / second (baud). 96 | /// \param port The Serial port number (e.g. 0 is Serial, 1 is Serial1). 97 | /// \deprecated Use setStream() to configure a non-default port. 98 | void begin(unsigned long speed, size_t port) __attribute__ ((deprecated)) 99 | { 100 | switch(port) 101 | { 102 | #if defined(UBRR1H) 103 | case 1: 104 | Serial1.begin(speed); 105 | #if ARDUINO >= 100 && !defined(CORE_TEENSY) 106 | while (!Serial1) {;} 107 | #endif 108 | setStream(&Serial1); 109 | break; 110 | #endif 111 | #if defined(UBRR2H) 112 | case 2: 113 | Serial2.begin(speed); 114 | #if ARDUINO >= 100 && !defined(CORE_TEENSY) 115 | while (!Serial1) {;} 116 | #endif 117 | setStream(&Serial2); 118 | break; 119 | #endif 120 | #if defined(UBRR3H) 121 | case 3: 122 | Serial3.begin(speed); 123 | #if ARDUINO >= 100 && !defined(CORE_TEENSY) 124 | while (!Serial3) {;} 125 | #endif 126 | setStream(&Serial3); 127 | break; 128 | #endif 129 | default: 130 | begin(speed); 131 | } 132 | } 133 | 134 | /// \brief Deprecated. Use setStream() to configure a non-default port. 135 | /// \param stream A pointer to an Arduino `Stream`. 136 | /// \deprecated Use setStream() to configure a non-default port. 137 | void begin(Stream* stream) __attribute__ ((deprecated)) 138 | { 139 | _stream = stream; 140 | } 141 | 142 | /// \brief Attach PacketSerial to an existing Arduino `Stream`. 143 | /// 144 | /// This `Stream` could be a standard `Serial` `Stream` with a non-default 145 | /// configuration such as: 146 | /// 147 | /// PacketSerial myPacketSerial; 148 | /// 149 | /// void setup() 150 | /// { 151 | /// Serial.begin(300, SERIAL_7N1); 152 | /// myPacketSerial.setStream(&Serial); 153 | /// } 154 | /// 155 | /// Or it might be a `SoftwareSerial` `Stream` such as: 156 | /// 157 | /// PacketSerial myPacketSerial; 158 | /// SoftwareSerial mySoftwareSerial(10, 11); 159 | /// 160 | /// void setup() 161 | /// { 162 | /// mySoftwareSerial.begin(38400); 163 | /// myPacketSerial.setStream(&mySoftwareSerial); 164 | /// } 165 | /// 166 | /// Any class that implements the `Stream` interface should work, which 167 | /// includes some network objects. 168 | /// 169 | /// \param stream A pointer to an Arduino `Stream`. 170 | void setStream(Stream* stream) 171 | { 172 | _stream = stream; 173 | } 174 | 175 | /// \brief Get a pointer to the current stream. 176 | /// \warning Reading from or writing to the stream managed by PacketSerial_ 177 | /// may break the packet-serial protocol if not done so with care. 178 | /// Access to the stream is allowed because PacketSerial_ never 179 | /// takes ownership of the stream and thus does not have exclusive 180 | /// access to the stream anyway. 181 | /// \returns a non-const pointer to the stream, or nullptr if unset. 182 | Stream* getStream() 183 | { 184 | return _stream; 185 | } 186 | 187 | /// \brief Get a pointer to the current stream. 188 | /// \warning Reading from or writing to the stream managed by PacketSerial_ 189 | /// may break the packet-serial protocol if not done so with care. 190 | /// Access to the stream is allowed because PacketSerial_ never 191 | /// takes ownership of the stream and thus does not have exclusive 192 | /// access to the stream anyway. 193 | /// \returns a const pointer to the stream, or nullptr if unset. 194 | const Stream* getStream() const 195 | { 196 | return _stream; 197 | } 198 | 199 | /// \brief The update function services the serial connection. 200 | /// 201 | /// This must be called often, ideally once per `loop()`, e.g.: 202 | /// 203 | /// void loop() 204 | /// { 205 | /// // Other program code. 206 | /// 207 | /// myPacketSerial.update(); 208 | /// } 209 | /// 210 | void update() 211 | { 212 | if (_stream == nullptr) return; 213 | 214 | while (_stream->available() > 0) 215 | { 216 | uint8_t data = _stream->read(); 217 | 218 | if (data == PacketMarker) 219 | { 220 | if (_onPacketFunction || _onPacketFunctionWithSender) 221 | { 222 | uint8_t _decodeBuffer[_receiveBufferIndex]; 223 | 224 | size_t numDecoded = EncoderType::decode(_receiveBuffer, 225 | _receiveBufferIndex, 226 | _decodeBuffer); 227 | 228 | // clear the index here so that the callback function can call update() if needed and receive more data 229 | _receiveBufferIndex = 0; 230 | _recieveBufferOverflow = false; 231 | 232 | if (_onPacketFunction) 233 | { 234 | _onPacketFunction(_decodeBuffer, numDecoded); 235 | } 236 | else if (_onPacketFunctionWithSender) 237 | { 238 | _onPacketFunctionWithSender(_senderPtr, _decodeBuffer, numDecoded); 239 | } 240 | 241 | } else { 242 | _receiveBufferIndex = 0; 243 | _recieveBufferOverflow = false; 244 | } 245 | } 246 | else 247 | { 248 | if ((_receiveBufferIndex + 1) < ReceiveBufferSize) 249 | { 250 | _receiveBuffer[_receiveBufferIndex++] = data; 251 | } 252 | else 253 | { 254 | // The buffer will be in an overflowed state if we write 255 | // so set a buffer overflowed flag. 256 | _recieveBufferOverflow = true; 257 | } 258 | } 259 | } 260 | } 261 | 262 | /// \brief Set a packet of data. 263 | /// 264 | /// This function will encode and send an arbitrary packet of data. After 265 | /// sending, it will send the specified `PacketMarker` defined in the 266 | /// template parameters. 267 | /// 268 | /// // Make an array. 269 | /// uint8_t myPacket[2] = { 255, 10 }; 270 | /// 271 | /// // Send the array. 272 | /// myPacketSerial.send(myPacket, 2); 273 | /// 274 | /// \param buffer A pointer to a data buffer. 275 | /// \param size The number of bytes in the data buffer. 276 | void send(const uint8_t* buffer, size_t size) const 277 | { 278 | if(_stream == nullptr || buffer == nullptr || size == 0) return; 279 | 280 | uint8_t _encodeBuffer[EncoderType::getEncodedBufferSize(size)]; 281 | 282 | size_t numEncoded = EncoderType::encode(buffer, 283 | size, 284 | _encodeBuffer); 285 | 286 | _stream->write(_encodeBuffer, numEncoded); 287 | _stream->write(PacketMarker); 288 | } 289 | 290 | /// \brief Set the function that will receive decoded packets. 291 | /// 292 | /// This function will be called when data is read from the serial stream 293 | /// connection and a packet is decoded. The decoded packet will be passed 294 | /// to the packet handler. The packet handler must have the form: 295 | /// 296 | /// The packet handler method usually has the form: 297 | /// 298 | /// void onPacketReceived(const uint8_t* buffer, size_t size); 299 | /// 300 | /// The packet handler would then be registered like this: 301 | /// 302 | /// myPacketSerial.setPacketHandler(&onPacketReceived); 303 | /// 304 | /// Setting a packet handler will remove all other packet handlers. 305 | /// 306 | /// \param onPacketFunction A pointer to the packet handler function. 307 | void setPacketHandler(PacketHandlerFunction onPacketFunction) 308 | { 309 | _onPacketFunction = onPacketFunction; 310 | _onPacketFunctionWithSender = nullptr; 311 | _senderPtr = nullptr; 312 | } 313 | 314 | /// \brief Set the function that will receive decoded packets. 315 | /// 316 | /// This function will be called when data is read from the serial stream 317 | /// connection and a packet is decoded. The decoded packet will be passed 318 | /// to the packet handler. The packet handler must have the form: 319 | /// 320 | /// The packet handler method usually has the form: 321 | /// 322 | /// void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size); 323 | /// 324 | /// To determine the sender, compare the pointer to the known possible 325 | /// PacketSerial senders. 326 | /// 327 | /// void onPacketReceived(void* sender, const uint8_t* buffer, size_t size) 328 | /// { 329 | /// if (sender == &myPacketSerial) 330 | /// { 331 | /// // Do something with the packet from myPacketSerial. 332 | /// } 333 | /// else if (sender == &myOtherPacketSerial) 334 | /// { 335 | /// // Do something with the packet from myOtherPacketSerial. 336 | /// } 337 | /// } 338 | /// 339 | /// The packet handler would then be registered like this: 340 | /// 341 | /// myPacketSerial.setPacketHandler(&onPacketReceived); 342 | /// 343 | /// You can also register an arbitrary void* pointer to be passed to your packet handler method. 344 | /// This is most useful when PacketSerial is used inside a class, to pass a pointer to 345 | /// the containing class: 346 | /// 347 | /// class EchoClass { 348 | /// public: 349 | /// void begin(unsigned long speed) { 350 | /// myPacketSerial.setPacketHandler(&onPacketReceived, this); 351 | /// myPacketSerial.begin(speed); 352 | /// } 353 | /// 354 | /// // C-style callbacks can't use non-static methods, 355 | /// // so we use a static method that receives "this" as the sender argument: 356 | /// // https://wiki.c2.com/?VirtualStaticIdiom 357 | /// static void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size) { 358 | /// ((EchoClass*)sender)->onPacketReceived(buffer, size); 359 | /// } 360 | /// 361 | /// void onPacketReceived(const uint8_t* buffer, size_t size) { 362 | /// // we can now use myPacketSerial as needed here 363 | /// } 364 | /// 365 | /// PacketSerial myPacketSerial; 366 | /// }; 367 | /// 368 | /// Setting a packet handler will remove all other packet handlers. 369 | /// 370 | /// \param onPacketFunctionWithSender A pointer to the packet handler function. 371 | /// \param senderPtr Optional pointer to a void* pointer, default argument will pass a pointer to the sending PacketSerial instance to the callback 372 | void setPacketHandler(PacketHandlerFunctionWithSender onPacketFunctionWithSender, void * senderPtr = nullptr) 373 | { 374 | _onPacketFunction = nullptr; 375 | _onPacketFunctionWithSender = onPacketFunctionWithSender; 376 | _senderPtr = senderPtr; 377 | // for backwards compatibility, the default _senderPtr is "this", but you can't use "this" as a default argument 378 | if(!senderPtr) _senderPtr = this; 379 | } 380 | 381 | /// \brief Check to see if the receive buffer overflowed. 382 | /// 383 | /// This must be called often, directly after the `update()` function. 384 | /// 385 | /// void loop() 386 | /// { 387 | /// // Other program code. 388 | /// myPacketSerial.update(); 389 | /// 390 | /// // Check for a receive buffer overflow. 391 | /// if (myPacketSerial.overflow()) 392 | /// { 393 | /// // Send an alert via a pin (e.g. make an overflow LED) or return a 394 | /// // user-defined packet to the sender. 395 | /// // 396 | /// // Ultimately you may need to just increase your recieve buffer via the 397 | /// // template parameters. 398 | /// } 399 | /// } 400 | /// 401 | /// The state is reset every time a new packet marker is received NOT when 402 | /// overflow() method is called. 403 | /// 404 | /// \returns true if the receive buffer overflowed. 405 | bool overflow() const 406 | { 407 | return _recieveBufferOverflow; 408 | } 409 | 410 | private: 411 | PacketSerial_(const PacketSerial_&); 412 | PacketSerial_& operator = (const PacketSerial_&); 413 | 414 | bool _recieveBufferOverflow = false; 415 | 416 | uint8_t _receiveBuffer[ReceiveBufferSize]; 417 | size_t _receiveBufferIndex = 0; 418 | 419 | Stream* _stream = nullptr; 420 | 421 | PacketHandlerFunction _onPacketFunction = nullptr; 422 | PacketHandlerFunctionWithSender _onPacketFunctionWithSender = nullptr; 423 | void* _senderPtr = nullptr; 424 | }; 425 | 426 | 427 | /// \brief A typedef for the default COBS PacketSerial class. 428 | typedef PacketSerial_ PacketSerial; 429 | 430 | /// \brief A typedef for a PacketSerial type with COBS encoding. 431 | typedef PacketSerial_ COBSPacketSerial; 432 | 433 | /// \brief A typedef for a PacketSerial type with SLIP encoding. 434 | typedef PacketSerial_ SLIPPacketSerial; 435 | --------------------------------------------------------------------------------