├── README.md ├── SendOnlySoftwareSerial.cpp ├── SendOnlySoftwareSerial.h ├── examples └── simple │ └── simple.ino ├── keywords.txt └── library.properties /README.md: -------------------------------------------------------------------------------- 1 | ## Send-only SoftwareSerial 2 | 3 | This library is an adapation of the SoftwareSerial library, with the receiving code omitted. 4 | 5 | It is intended for situations where you need to do software sending (not use the hardware serial) but don't need to receive anything. This lets you avoid tying up the pin-change interrupts for the receiving code. 6 | 7 | Example of use: 8 | 9 | ```c++ 10 | #include 11 | 12 | SendOnlySoftwareSerial mySerial (3); // Tx pin 13 | 14 | void setup () 15 | { 16 | mySerial.begin(115200); 17 | } 18 | 19 | int i; 20 | 21 | void loop () 22 | { 23 | mySerial.print ("test: "); 24 | mySerial.println (i++); 25 | delay (100); 26 | } 27 | ``` 28 | 29 | --- 30 | 31 | ## How to install 32 | 33 | Make a folder "SendOnlySoftwareSerial" inside the "libraries" folder inside your sketchbook folder. Place the files from this repository in it, in particular SendOnlySoftwareSerial.cpp and SendOnlySoftwareSerial.h. 34 | 35 | 36 | -------------------------------------------------------------------------------- /SendOnlySoftwareSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SendOnlySoftwareSerial - adapted from SendOnlySoftwareSerial by Nick Gammon 30th December 2016 4 | 5 | SoftwareSerial.cpp (formerly NewSoftSerial.cpp) - 6 | Multi-instance software serial library for Arduino/Wiring 7 | -- Interrupt-driven receive and other improvements by ladyada 8 | (http://ladyada.net) 9 | -- Tuning, circular buffer, derivation from class Print/Stream, 10 | multi-instance support, porting to 8MHz processors, 11 | various optimizations, PROGMEM delay tables, inverse logic and 12 | direct port writing by Mikal Hart (http://www.arduiniana.org) 13 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 14 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 15 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 16 | 17 | This library is free software; you can redistribute it and/or 18 | modify it under the terms of the GNU Lesser General Public 19 | License as published by the Free Software Foundation; either 20 | version 2.1 of the License, or (at your option) any later version. 21 | 22 | This library is distributed in the hope that it will be useful, 23 | but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 | Lesser General Public License for more details. 26 | 27 | You should have received a copy of the GNU Lesser General Public 28 | License along with this library; if not, write to the Free Software 29 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 30 | 31 | The latest version of this library can always be found at 32 | http://arduiniana.org. 33 | */ 34 | 35 | // When set, _DEBUG co-opts pins 11 and 13 for debugging with an 36 | // oscilloscope or logic analyzer. Beware: it also slightly modifies 37 | // the bit times, so don't rely on it too much at high baud rates 38 | #define _DEBUG 0 39 | #define _DEBUG_PIN1 11 40 | #define _DEBUG_PIN2 13 41 | // 42 | // Includes 43 | // 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | // 51 | // Debugging 52 | // 53 | // This function generates a brief pulse 54 | // for debugging or measuring on an oscilloscope. 55 | #if _DEBUG 56 | inline void DebugPulse(uint8_t pin, uint8_t count) 57 | { 58 | volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); 59 | 60 | uint8_t val = *pport; 61 | while (count--) 62 | { 63 | *pport = val | digitalPinToBitMask(pin); 64 | *pport = val; 65 | } 66 | } 67 | #else 68 | inline void DebugPulse(uint8_t, uint8_t) {} 69 | #endif 70 | 71 | // 72 | // Private methods 73 | // 74 | 75 | /* static */ 76 | inline void SendOnlySoftwareSerial::tunedDelay(uint16_t delay) { 77 | _delay_loop_2(delay); 78 | } 79 | 80 | // 81 | // Constructor 82 | // 83 | SendOnlySoftwareSerial::SendOnlySoftwareSerial(uint8_t transmitPin, bool inverse_logic /* = false */) : 84 | _tx_delay(0), 85 | _inverse_logic(inverse_logic) 86 | { 87 | setTX(transmitPin); 88 | } 89 | 90 | // 91 | // Destructor 92 | // 93 | SendOnlySoftwareSerial::~SendOnlySoftwareSerial() 94 | { 95 | end(); 96 | } 97 | 98 | void SendOnlySoftwareSerial::setTX(uint8_t tx) 99 | { 100 | // First write, then set output. If we do this the other way around, 101 | // the pin would be output low for a short while before switching to 102 | // output high. Now, it is input with pullup for a short while, which 103 | // is fine. With inverse logic, either order is fine. 104 | digitalWrite(tx, _inverse_logic ? LOW : HIGH); 105 | pinMode(tx, OUTPUT); 106 | _transmitBitMask = digitalPinToBitMask(tx); 107 | uint8_t port = digitalPinToPort(tx); 108 | _transmitPortRegister = portOutputRegister(port); 109 | } 110 | 111 | uint16_t SendOnlySoftwareSerial::subtract_cap(uint16_t num, uint16_t sub) { 112 | if (num > sub) 113 | return num - sub; 114 | else 115 | return 1; 116 | } 117 | 118 | // 119 | // Public methods 120 | // 121 | 122 | void SendOnlySoftwareSerial::begin(long speed) 123 | { 124 | _tx_delay = 0; 125 | 126 | // Precalculate the various delays, in number of 4-cycle delays 127 | uint16_t bit_delay = (F_CPU / speed) / 4; 128 | 129 | // 12 (gcc 4.8.2) or 13 (gcc 4.3.2) cycles from start bit to first bit, 130 | // 15 (gcc 4.8.2) or 16 (gcc 4.3.2) cycles between bits, 131 | // 12 (gcc 4.8.2) or 14 (gcc 4.3.2) cycles from last bit to stop bit 132 | // These are all close enough to just use 15 cycles, since the inter-bit 133 | // timings are the most critical (deviations stack 8 times) 134 | _tx_delay = subtract_cap(bit_delay, 15 / 4); 135 | 136 | #if _DEBUG 137 | pinMode(_DEBUG_PIN1, OUTPUT); 138 | pinMode(_DEBUG_PIN2, OUTPUT); 139 | #endif 140 | 141 | } 142 | 143 | void SendOnlySoftwareSerial::end() 144 | { 145 | } 146 | 147 | size_t SendOnlySoftwareSerial::write(uint8_t b) 148 | { 149 | if (_tx_delay == 0) { 150 | setWriteError(); 151 | return 0; 152 | } 153 | 154 | // By declaring these as local variables, the compiler will put them 155 | // in registers _before_ disabling interrupts and entering the 156 | // critical timing sections below, which makes it a lot easier to 157 | // verify the cycle timings 158 | volatile uint8_t *reg = _transmitPortRegister; 159 | uint8_t reg_mask = _transmitBitMask; 160 | uint8_t inv_mask = ~_transmitBitMask; 161 | uint8_t oldSREG = SREG; 162 | bool inv = _inverse_logic; 163 | uint16_t delay = _tx_delay; 164 | 165 | if (inv) 166 | b = ~b; 167 | 168 | cli(); // turn off interrupts for a clean txmit 169 | 170 | // Write the start bit 171 | if (inv) 172 | *reg |= reg_mask; 173 | else 174 | *reg &= inv_mask; 175 | 176 | tunedDelay(delay); 177 | 178 | // Write each of the 8 bits 179 | for (uint8_t i = 8; i > 0; --i) 180 | { 181 | if (b & 1) // choose bit 182 | *reg |= reg_mask; // send 1 183 | else 184 | *reg &= inv_mask; // send 0 185 | 186 | tunedDelay(delay); 187 | b >>= 1; 188 | } 189 | 190 | // restore pin to natural state 191 | if (inv) 192 | *reg &= inv_mask; 193 | else 194 | *reg |= reg_mask; 195 | 196 | SREG = oldSREG; // turn interrupts back on 197 | tunedDelay(_tx_delay); 198 | 199 | return 1; 200 | } 201 | 202 | void SendOnlySoftwareSerial::flush() 203 | { 204 | // There is no tx buffering, simply return 205 | } 206 | 207 | // Read data from buffer 208 | int SendOnlySoftwareSerial::read() 209 | { 210 | return -1; 211 | } 212 | 213 | int SendOnlySoftwareSerial::available() 214 | { 215 | return 0; 216 | } 217 | 218 | int SendOnlySoftwareSerial::peek() 219 | { 220 | return -1; 221 | } 222 | -------------------------------------------------------------------------------- /SendOnlySoftwareSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | SoftwareSerial.h (formerly NewSoftSerial.h) - 3 | Multi-instance software serial library for Arduino/Wiring 4 | -- Interrupt-driven receive and other improvements by ladyada 5 | (http://ladyada.net) 6 | -- Tuning, circular buffer, derivation from class Print/Stream, 7 | multi-instance support, porting to 8MHz processors, 8 | various optimizations, PROGMEM delay tables, inverse logic and 9 | direct port writing by Mikal Hart (http://www.arduiniana.org) 10 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 11 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 12 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 13 | 14 | This library is free software; you can redistribute it and/or 15 | modify it under the terms of the GNU Lesser General Public 16 | License as published by the Free Software Foundation; either 17 | version 2.1 of the License, or (at your option) any later version. 18 | 19 | This library is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 | Lesser General Public License for more details. 23 | 24 | You should have received a copy of the GNU Lesser General Public 25 | License along with this library; if not, write to the Free Software 26 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 | 28 | The latest version of this library can always be found at 29 | http://arduiniana.org. 30 | */ 31 | 32 | #ifndef SendOnlySoftwareSerial_h 33 | #define SendOnlySoftwareSerial_h 34 | 35 | #include 36 | #include 37 | 38 | /****************************************************************************** 39 | * Definitions 40 | ******************************************************************************/ 41 | 42 | #ifndef GCC_VERSION 43 | #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 44 | #endif 45 | 46 | class SendOnlySoftwareSerial : public Stream 47 | { 48 | private: 49 | uint8_t _transmitBitMask; 50 | volatile uint8_t *_transmitPortRegister; 51 | volatile uint8_t *_pcint_maskreg; 52 | uint8_t _pcint_maskvalue; 53 | 54 | // Expressed as 4-cycle delays (must never be 0!) 55 | uint16_t _tx_delay; 56 | 57 | uint16_t _buffer_overflow:1; 58 | uint16_t _inverse_logic:1; 59 | 60 | // private methods 61 | void setTX(uint8_t transmitPin); 62 | 63 | // Return num - sub, or 1 if the result would be < 1 64 | static uint16_t subtract_cap(uint16_t num, uint16_t sub); 65 | 66 | // private static method for timing 67 | static inline void tunedDelay(uint16_t delay); 68 | 69 | public: 70 | // public methods 71 | SendOnlySoftwareSerial(uint8_t transmitPin, bool inverse_logic = false); 72 | ~SendOnlySoftwareSerial(); 73 | void begin(long speed); 74 | void end(); 75 | bool overflow() { bool ret = _buffer_overflow; if (ret) _buffer_overflow = false; return ret; } 76 | int peek(); 77 | 78 | virtual size_t write(uint8_t byte); 79 | virtual int read(); 80 | virtual int available(); 81 | virtual void flush(); 82 | operator bool() { return true; } 83 | 84 | using Print::write; 85 | 86 | }; 87 | 88 | // Arduino 0012 workaround 89 | #undef int 90 | #undef char 91 | #undef long 92 | #undef byte 93 | #undef float 94 | #undef abs 95 | #undef round 96 | 97 | #endif // SendOnlySoftwareSerial_h 98 | -------------------------------------------------------------------------------- /examples/simple/simple.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | SendOnlySoftwareSerial mySerial(3); // Tx pin 4 | 5 | void setup() 6 | { 7 | mySerial.begin(115200); 8 | } 9 | 10 | int i; 11 | 12 | void loop() 13 | { 14 | mySerial.print ("test: "); 15 | mySerial.println (i++); 16 | delay (100); 17 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for SendOnlySoftwareSerial 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | SendOnlySoftwareSerial KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | begin KEYWORD2 16 | end KEYWORD2 17 | read KEYWORD2 18 | available KEYWORD2 19 | flush KEYWORD2 20 | 21 | ####################################### 22 | # Constants (LITERAL1) 23 | ####################################### 24 | 25 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SendOnlySoftwareSerial 2 | version=1.0 3 | author=Nick Gammon 4 | maintainer=Nick Gammon 5 | sentence=Arduino library for sending (only) of serial data 6 | paragraph=Arduino library for sending (only) of serial data 7 | category=Communication 8 | url=https://github.com/nickgammon/SendOnlySoftwareSerial 9 | architectures=avr --------------------------------------------------------------------------------