diff --git a/README.md b/README.md index cdf6db3..f9ea068 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A _packet_ is simply an array of bytes. ## Documentation -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.com/PacketSerial). +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/). ## Support diff --git a/examples/PacketSerialReverseEchoClass/PacketSerialReverseEchoClass.ino b/examples/PacketSerialReverseEchoClass/PacketSerialReverseEchoClass.ino new file mode 100644 index 0000000..59744f3 --- /dev/null +++ b/examples/PacketSerialReverseEchoClass/PacketSerialReverseEchoClass.ino @@ -0,0 +1,115 @@ +// +// Copyright (c) 2012 Christopher Baker +// +// SPDX-License-Identifier: MIT +// + +// This example is PacketSerialReverseEcho modified to use PacketSerial from within a class + +#include + +// This function takes a byte buffer and reverses it. +void reverse(uint8_t* buffer, size_t size) +{ + uint8_t tmp; + + for (size_t i = 0; i < size / 2; i++) + { + tmp = buffer[i]; + buffer[i] = buffer[size - i - 1]; + buffer[size - i - 1] = tmp; + } +} + +class EchoClass { + public: + void begin(unsigned long speed) { + // If we want to receive packets, we must specify a packet handler function. + // The packet handler is a custom function with a signature like the + // onPacketReceived function below. + myPacketSerial.setPacketHandler(&onPacketReceived, this); + + myPacketSerial.begin(speed); + } + + void loop() { + // The PacketSerial::update() method attempts to read in any incoming serial + // data and emits received and decoded packets via the packet handler + // function specified by the user in the void setup() function. + // + // The PacketSerial::update() method should be called once per loop(). Failure + // to call the PacketSerial::update() frequently enough may result in buffer + // serial overflows. + myPacketSerial.update(); + + // Check for a receive buffer overflow (optional). + if (myPacketSerial.overflow()) + { + // Send an alert via a pin (e.g. make an overflow LED) or return a + // user-defined packet to the sender. + // + // Ultimately you may need to just increase your recieve buffer via the + // template parameters (see the README.md). + } + } + + private: + // 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 + static void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size) { + ((EchoClass*)sender)->onPacketReceived(buffer, size); + } + + // This is our handler callback function. + // When an encoded packet is received and decoded, it will be delivered here. + // The `buffer` is a pointer to the decoded byte array. `size` is the number of + // bytes in the `buffer`. + void onPacketReceived(const uint8_t* buffer, size_t size) { + // In this example, we will simply reverse the contents of the array and send + // it back to the sender. + + // Make a temporary buffer. + uint8_t tempBuffer[size]; + + // Copy the packet into our temporary buffer. + memcpy(tempBuffer, buffer, size); + + // Reverse our temporaray buffer. + reverse(tempBuffer, size); + + // Send the reversed buffer back to the sender. The send() method will encode + // the whole buffer as as single packet, set packet markers, etc. + // The `tempBuffer` is a pointer to the `tempBuffer` array and `size` is the + // number of bytes to send in the `tempBuffer`. + myPacketSerial.send(tempBuffer, size); + } + + PacketSerial myPacketSerial; +}; + +// By default, PacketSerial automatically wraps the built-in `Serial` object. +// While it is still possible to use the Serial object directly, it is +// recommended that the user let the PacketSerial object manage all serial +// communication. Thus the user should not call Serial.write(), Serial.print(), +// etc. Additionally the user should not use the serialEvent() framework. +// +// By default, PacketSerial uses COBS encoding and has a 256 byte receive +// buffer. This can be adjusted by the user by replacing `PacketSerial` with +// a variation of the `PacketSerial_` template found in +// PacketSerial.h. + +EchoClass myEchoClass; + +void setup() +{ + // We begin communication with our PacketSerial object by setting the + // communication speed in bits / second (baud). + myEchoClass.begin(115200); +} + + +void loop() +{ + // Do your program-specific loop() work here as usual. + + myEchoClass.loop(); +} diff --git a/src/PacketSerial.h b/src/PacketSerial.h index c2f75c5..67de570 100644 --- a/src/PacketSerial.h +++ b/src/PacketSerial.h @@ -54,7 +54,8 @@ class PacketSerial_ _receiveBufferIndex(0), _stream(nullptr), _onPacketFunction(nullptr), - _onPacketFunctionWithSender(nullptr) + _onPacketFunctionWithSender(nullptr), + _senderPtr(nullptr) { } @@ -224,18 +225,23 @@ class PacketSerial_ _receiveBufferIndex, _decodeBuffer); + // clear the index here so that the callback function can call update() if needed and receive more data + _receiveBufferIndex = 0; + _recieveBufferOverflow = false; + if (_onPacketFunction) { _onPacketFunction(_decodeBuffer, numDecoded); } else if (_onPacketFunctionWithSender) { - _onPacketFunctionWithSender(this, _decodeBuffer, numDecoded); + _onPacketFunctionWithSender(_senderPtr, _decodeBuffer, numDecoded); } - } - _receiveBufferIndex = 0; - _recieveBufferOverflow = false; + } else { + _receiveBufferIndex = 0; + _recieveBufferOverflow = false; + } } else { @@ -302,6 +308,7 @@ class PacketSerial_ { _onPacketFunction = onPacketFunction; _onPacketFunctionWithSender = nullptr; + _senderPtr = nullptr; } /// \brief Set the function that will receive decoded packets. @@ -333,13 +340,42 @@ class PacketSerial_ /// /// myPacketSerial.setPacketHandler(&onPacketReceived); /// + /// You can also register an arbitrary void* pointer to be passed to your packet handler method. + /// This is most useful when PacketSerial is used inside a class, to pass a pointer to + /// the containing class: + /// + /// class EchoClass { + /// public: + /// void begin(unsigned long speed) { + /// myPacketSerial.setPacketHandler(&onPacketReceived, this); + /// myPacketSerial.begin(speed); + /// } + /// + /// // 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 + /// static void onPacketReceived(const void* sender, const uint8_t* buffer, size_t size) { + /// ((EchoClass*)sender)->onPacketReceived(buffer, size); + /// } + /// + /// void onPacketReceived(const uint8_t* buffer, size_t size) { + /// // we can now use myPacketSerial as needed here + /// } + /// + /// PacketSerial myPacketSerial; + /// }; + /// /// Setting a packet handler will remove all other packet handlers. /// /// \param onPacketFunctionWithSender A pointer to the packet handler function. - void setPacketHandler(PacketHandlerFunctionWithSender onPacketFunctionWithSender) + /// \param senderPtr Optional pointer to a void* pointer, default argument will pass a pointer to the sending PacketSerial instance to the callback + void setPacketHandler(PacketHandlerFunctionWithSender onPacketFunctionWithSender, void * senderPtr = nullptr) { _onPacketFunction = nullptr; _onPacketFunctionWithSender = onPacketFunctionWithSender; + _senderPtr = senderPtr; + // for backwards compatibility, the default _senderPtr is "this", but you can't use "this" as a default argument + if(!senderPtr) _senderPtr = this; } /// \brief Check to see if the receive buffer overflowed. @@ -384,6 +420,7 @@ class PacketSerial_ PacketHandlerFunction _onPacketFunction = nullptr; PacketHandlerFunctionWithSender _onPacketFunctionWithSender = nullptr; + void* _senderPtr = nullptr; };