├── README.md ├── SoftwareSerial.cpp ├── SoftwareSerial.h ├── examples └── swsertest │ └── swsertest.ino ├── keywords.txt ├── library.json └── library.properties /README.md: -------------------------------------------------------------------------------- 1 | # EspSoftwareSerial 2 | 3 | Implementation of the Arduino software serial library for the ESP32 4 | 5 | Based on the Arduino software serial library for the ESP8266 found here https://github.com/plerup/espsoftwareserial 6 | 7 | -------------------------------------------------------------------------------- /SoftwareSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266. 4 | Copyright (c) 2015-2016 Peter Lerup. All rights reserved. 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | */ 21 | 22 | #include 23 | 24 | // The Arduino standard GPIO routines are not enough, 25 | // must use some from the Espressif SDK as well 26 | extern "C" { 27 | #include "esp32-hal-gpio.h" 28 | } 29 | 30 | #include 31 | 32 | #define MAX_PIN 35 33 | #define DEFAULT_BUAD_RATE 9600 34 | 35 | // As the Arduino attachInterrupt has no parameter, lists of objects 36 | // and callbacks corresponding to each possible GPIO pins have to be defined 37 | SoftwareSerial *ObjList[MAX_PIN+1]; 38 | 39 | void IRAM_ATTR sws_isr_0() { ObjList[0]->rxRead(); }; 40 | void IRAM_ATTR sws_isr_1() { ObjList[1]->rxRead(); }; 41 | void IRAM_ATTR sws_isr_2() { ObjList[2]->rxRead(); }; 42 | void IRAM_ATTR sws_isr_3() { ObjList[3]->rxRead(); }; 43 | void IRAM_ATTR sws_isr_4() { ObjList[4]->rxRead(); }; 44 | void IRAM_ATTR sws_isr_5() { ObjList[5]->rxRead(); }; 45 | // Pin 6 to 11 can not be used 46 | void IRAM_ATTR sws_isr_12() { ObjList[12]->rxRead(); }; 47 | void IRAM_ATTR sws_isr_13() { ObjList[13]->rxRead(); }; 48 | void IRAM_ATTR sws_isr_14() { ObjList[14]->rxRead(); }; 49 | void IRAM_ATTR sws_isr_15() { ObjList[15]->rxRead(); }; 50 | void IRAM_ATTR sws_isr_16() { ObjList[16]->rxRead(); }; 51 | void IRAM_ATTR sws_isr_17() { ObjList[17]->rxRead(); }; 52 | void IRAM_ATTR sws_isr_18() { ObjList[18]->rxRead(); }; 53 | void IRAM_ATTR sws_isr_19() { ObjList[19]->rxRead(); }; 54 | void IRAM_ATTR sws_isr_20() { ObjList[20]->rxRead(); }; 55 | void IRAM_ATTR sws_isr_21() { ObjList[21]->rxRead(); }; 56 | void IRAM_ATTR sws_isr_22() { ObjList[22]->rxRead(); }; 57 | void IRAM_ATTR sws_isr_23() { ObjList[23]->rxRead(); }; 58 | void IRAM_ATTR sws_isr_24() { ObjList[24]->rxRead(); }; 59 | void IRAM_ATTR sws_isr_25() { ObjList[25]->rxRead(); }; 60 | void IRAM_ATTR sws_isr_26() { ObjList[26]->rxRead(); }; 61 | void IRAM_ATTR sws_isr_27() { ObjList[27]->rxRead(); }; 62 | void IRAM_ATTR sws_isr_28() { ObjList[28]->rxRead(); }; 63 | void IRAM_ATTR sws_isr_29() { ObjList[29]->rxRead(); }; 64 | void IRAM_ATTR sws_isr_30() { ObjList[30]->rxRead(); }; 65 | void IRAM_ATTR sws_isr_31() { ObjList[31]->rxRead(); }; 66 | void IRAM_ATTR sws_isr_32() { ObjList[32]->rxRead(); }; 67 | void IRAM_ATTR sws_isr_33() { ObjList[33]->rxRead(); }; 68 | void IRAM_ATTR sws_isr_34() { ObjList[34]->rxRead(); }; 69 | void IRAM_ATTR sws_isr_35() { ObjList[35]->rxRead(); }; 70 | 71 | static void (*ISRList[MAX_PIN+1])() = { 72 | sws_isr_0, 73 | sws_isr_1, 74 | sws_isr_2, 75 | sws_isr_3, 76 | sws_isr_4, 77 | sws_isr_5, 78 | NULL, 79 | NULL, 80 | NULL, 81 | NULL, 82 | NULL, 83 | NULL, 84 | sws_isr_12, 85 | sws_isr_13, 86 | sws_isr_14, 87 | sws_isr_15, 88 | sws_isr_16, 89 | sws_isr_17, 90 | sws_isr_18, 91 | sws_isr_19, 92 | sws_isr_20, 93 | sws_isr_21, 94 | sws_isr_22, 95 | sws_isr_23, 96 | sws_isr_24, 97 | sws_isr_25, 98 | sws_isr_26, 99 | sws_isr_27, 100 | sws_isr_28, 101 | sws_isr_29, 102 | sws_isr_30, 103 | sws_isr_31, 104 | sws_isr_32, 105 | sws_isr_33, 106 | sws_isr_34, 107 | sws_isr_35 108 | }; 109 | 110 | SoftwareSerial::SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic, unsigned int buffSize) { 111 | m_rxValid = m_txValid = m_txEnableValid = false; 112 | m_buffer = NULL; 113 | m_invert = inverse_logic; 114 | m_overflow = false; 115 | m_rxEnabled = false; 116 | if (isValidGPIOpin(receivePin)) { 117 | m_rxPin = receivePin; 118 | m_buffSize = buffSize; 119 | m_buffer = (uint8_t*)malloc(m_buffSize); 120 | if (m_buffer != NULL) { 121 | m_rxValid = true; 122 | m_inPos = m_outPos = 0; 123 | pinMode(m_rxPin, INPUT); 124 | ObjList[m_rxPin] = this; 125 | enableRx(true); 126 | } 127 | } 128 | if (isValidGPIOpin(transmitPin)) { 129 | m_txValid = true; 130 | m_txPin = transmitPin; 131 | pinMode(m_txPin, OUTPUT); 132 | digitalWrite(m_txPin, !m_invert); 133 | } 134 | // Default speed 135 | //begin(DEFAULT_BUAD_RATE); 136 | } 137 | 138 | SoftwareSerial::~SoftwareSerial() { 139 | enableRx(false); 140 | if (m_rxValid) 141 | ObjList[m_rxPin] = NULL; 142 | if (m_buffer) 143 | free(m_buffer); 144 | } 145 | 146 | bool SoftwareSerial::isValidGPIOpin(int pin) { 147 | return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= MAX_PIN); 148 | } 149 | 150 | void SoftwareSerial::begin(long speed) { 151 | // Use getCycleCount() loop to get as exact timing as possible 152 | m_bitTime = ESP.getCpuFreqMHz()*1000000/speed; 153 | 154 | if (!m_rxEnabled) 155 | enableRx(true); 156 | } 157 | 158 | long SoftwareSerial::baudRate() { 159 | return ESP.getCpuFreqMHz()*1000000/m_bitTime; 160 | } 161 | 162 | void SoftwareSerial::setTransmitEnablePin(int transmitEnablePin) { 163 | if (isValidGPIOpin(transmitEnablePin)) { 164 | m_txEnableValid = true; 165 | m_txEnablePin = transmitEnablePin; 166 | pinMode(m_txEnablePin, OUTPUT); 167 | digitalWrite(m_txEnablePin, LOW); 168 | } else { 169 | m_txEnableValid = false; 170 | } 171 | } 172 | 173 | void SoftwareSerial::enableRx(bool on) { 174 | if (m_rxValid) { 175 | if (on) 176 | attachInterrupt(m_rxPin, ISRList[m_rxPin], m_invert ? RISING : FALLING); 177 | else 178 | detachInterrupt(m_rxPin); 179 | m_rxEnabled = on; 180 | } 181 | } 182 | 183 | int SoftwareSerial::read() { 184 | if (!m_rxValid || (m_inPos == m_outPos)) return -1; 185 | uint8_t ch = m_buffer[m_outPos]; 186 | m_outPos = (m_outPos+1) % m_buffSize; 187 | return ch; 188 | } 189 | 190 | int SoftwareSerial::available() { 191 | if (!m_rxValid) return 0; 192 | int avail = m_inPos - m_outPos; 193 | if (avail < 0) avail += m_buffSize; 194 | return avail; 195 | } 196 | 197 | #define WAIT { while (ESP.getCycleCount()-start < wait); wait += m_bitTime; } 198 | 199 | size_t SoftwareSerial::write(uint8_t b) { 200 | if (!m_txValid) return 0; 201 | 202 | if (m_invert) b = ~b; 203 | // Disable interrupts in order to get a clean transmit 204 | cli(); 205 | if (m_txEnableValid) digitalWrite(m_txEnablePin, HIGH); 206 | unsigned long wait = m_bitTime; 207 | digitalWrite(m_txPin, HIGH); 208 | unsigned long start = ESP.getCycleCount(); 209 | // Start bit; 210 | digitalWrite(m_txPin, LOW); 211 | WAIT; 212 | for (int i = 0; i < 8; i++) { 213 | digitalWrite(m_txPin, (b & 1) ? HIGH : LOW); 214 | WAIT; 215 | b >>= 1; 216 | } 217 | // Stop bit 218 | digitalWrite(m_txPin, HIGH); 219 | WAIT; 220 | if (m_txEnableValid) digitalWrite(m_txEnablePin, LOW); 221 | sei(); 222 | return 1; 223 | } 224 | 225 | void SoftwareSerial::flush() { 226 | m_inPos = m_outPos = 0; 227 | } 228 | 229 | bool SoftwareSerial::overflow() { 230 | bool res = m_overflow; 231 | m_overflow = false; 232 | return res; 233 | } 234 | 235 | int SoftwareSerial::peek() { 236 | if (!m_rxValid || (m_inPos == m_outPos)) return -1; 237 | return m_buffer[m_outPos]; 238 | } 239 | 240 | void IRAM_ATTR SoftwareSerial::rxRead() { 241 | // Advance the starting point for the samples but compensate for the 242 | // initial delay which occurs before the interrupt is delivered 243 | unsigned long wait = m_bitTime + m_bitTime/3 - 500; 244 | unsigned long start = ESP.getCycleCount(); 245 | uint8_t rec = 0; 246 | for (int i = 0; i < 8; i++) { 247 | WAIT; 248 | rec >>= 1; 249 | if (digitalRead(m_rxPin)) 250 | rec |= 0x80; 251 | } 252 | if (m_invert) rec = ~rec; 253 | // Stop bit 254 | WAIT; 255 | // Store the received value in the buffer unless we have an overflow 256 | int next = (m_inPos+1) % m_buffSize; 257 | if (next != m_outPos) { 258 | m_buffer[m_inPos] = rec; 259 | m_inPos = next; 260 | } else { 261 | m_overflow = true; 262 | } 263 | // Must clear this bit in the interrupt register, 264 | // it gets set even when interrupts are disabled 265 | GPIO_REG_WRITE(GPIO.status_w1tc, 1 << m_rxPin); 266 | } 267 | -------------------------------------------------------------------------------- /SoftwareSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | SoftwareSerial.h 3 | 4 | SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266. 5 | Copyright (c) 2015-2016 Peter Lerup. All rights reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | */ 22 | 23 | #ifndef SoftwareSerial_h 24 | #define SoftwareSerial_h 25 | 26 | #include 27 | #include 28 | 29 | 30 | // This class is compatible with the corresponding AVR one, 31 | // the constructor however has an optional rx buffer size. 32 | // Speed up to 115200 can be used. 33 | 34 | 35 | class SoftwareSerial : public Stream 36 | { 37 | public: 38 | SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic = false, unsigned int buffSize = 64); 39 | ~SoftwareSerial(); 40 | 41 | void begin(long speed); 42 | long baudRate(); 43 | void setTransmitEnablePin(int transmitEnablePin); 44 | 45 | bool overflow(); 46 | int peek(); 47 | 48 | virtual size_t write(uint8_t byte); 49 | virtual int read(); 50 | virtual int available(); 51 | virtual void flush(); 52 | operator bool() {return m_rxValid || m_txValid;} 53 | 54 | // Disable or enable interrupts on the rx pin 55 | void enableRx(bool on); 56 | 57 | void rxRead(); 58 | 59 | // AVR compatibility methods 60 | bool listen() { enableRx(true); return true; } 61 | void end() { stopListening(); } 62 | bool isListening() { return m_rxEnabled; } 63 | bool stopListening() { enableRx(false); return true; } 64 | 65 | using Print::write; 66 | 67 | private: 68 | bool isValidGPIOpin(int pin); 69 | 70 | // Member variables 71 | int m_rxPin, m_txPin, m_txEnablePin; 72 | bool m_rxValid, m_rxEnabled; 73 | bool m_txValid, m_txEnableValid; 74 | bool m_invert; 75 | bool m_overflow; 76 | unsigned long m_bitTime; 77 | unsigned int m_inPos, m_outPos; 78 | int m_buffSize; 79 | uint8_t *m_buffer; 80 | 81 | }; 82 | 83 | // If only one tx or rx wanted then use this as parameter for the unused pin 84 | #define SW_SERIAL_UNUSED_PIN -1 85 | 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /examples/swsertest/swsertest.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | SoftwareSerial swSer(14, 12, false, 256); 4 | 5 | void setup() { 6 | Serial.begin(115200); 7 | swSer.begin(115200); 8 | 9 | Serial.println("\nSoftware serial test started"); 10 | 11 | for (char ch = ' '; ch <= 'z'; ch++) { 12 | swSer.write(ch); 13 | } 14 | swSer.println(""); 15 | 16 | } 17 | 18 | void loop() { 19 | while (swSer.available() > 0) { 20 | Serial.write(swSer.read()); 21 | } 22 | while (Serial.available() > 0) { 23 | swSer.write(Serial.read()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for SoftwareSerial 3 | # (esp8266) 4 | ####################################### 5 | 6 | ####################################### 7 | # Datatypes (KEYWORD1) 8 | ####################################### 9 | 10 | SoftwareSerial KEYWORD1 11 | 12 | ####################################### 13 | # Methods and Functions (KEYWORD2) 14 | ####################################### 15 | 16 | begin KEYWORD2 17 | read KEYWORD2 18 | write KEYWORD2 19 | available KEYWORD2 20 | flush KEYWORD2 21 | overflow KEYWORD2 22 | peek KEYWORD2 23 | listen KEYWORD2 24 | end KEYWORD2 25 | isListening KEYWORD2 26 | stopListening KEYWORD2 27 | 28 | ####################################### 29 | # Constants (LITERAL1) 30 | ####################################### 31 | 32 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SoftwareSerialEsp32", 3 | "keywords": [ 4 | "serial", "io", "softwareserial" 5 | ], 6 | "description": "Implementation of the Arduino software serial for ESP32.", 7 | "repository": 8 | { 9 | "type": "git", 10 | "url": "https://github.com/jdollar/espsoftwareserial.git" 11 | }, 12 | "frameworks": "arduino", 13 | "platforms": "espressif" 14 | } 15 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SoftwareSerialEsp32 2 | version=1.0 3 | author=jdollar 4 | maintainer=jdollar 5 | sentence=Implementation of the Arduino software serial for ESP32. 6 | paragraph=Based off the implementation for the Arduino software serial for the ESP8266 https://github.com/plerup/espsoftwareserial 7 | category=Signal Input/Output 8 | url= 9 | architectures=esp32 10 | --------------------------------------------------------------------------------