mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-26 22:22:16 +03:00
add lib EspSoftwareSerial for ESP32
This commit is contained in:
71
lib/EspSoftwareSerial/examples/bitpattern/bitpattern.ino
Normal file
71
lib/EspSoftwareSerial/examples/bitpattern/bitpattern.ino
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "SoftwareSerial.h"
|
||||
|
||||
#ifndef D5
|
||||
#if defined(ESP8266)
|
||||
#define D8 (15)
|
||||
#define D5 (14)
|
||||
#define D7 (13)
|
||||
#define D6 (12)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#elif defined(ESP32)
|
||||
#define D8 (5)
|
||||
#define D5 (18)
|
||||
#define D7 (23)
|
||||
#define D6 (19)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
EspSoftwareSerial::UART swSer;
|
||||
#ifdef ESP8266
|
||||
auto logSer = EspSoftwareSerial::UART(-1, TX);
|
||||
auto hwSer = Serial;
|
||||
#else
|
||||
auto logSer = Serial;
|
||||
auto hwSer = Serial1;
|
||||
#endif
|
||||
|
||||
constexpr uint32_t TESTBPS = 115200;
|
||||
|
||||
void setup() {
|
||||
delay(2000);
|
||||
#ifdef ESP8266
|
||||
hwSer.begin(TESTBPS, ::SERIAL_8N1);
|
||||
hwSer.swap();
|
||||
#else
|
||||
hwSer.begin(TESTBPS, ::SERIAL_8N1, D6, D5);
|
||||
#endif
|
||||
logSer.begin(115200);
|
||||
logSer.println(PSTR("\nOne Wire Half Duplex Bitpattern and Datarate Test"));
|
||||
swSer.begin(TESTBPS, EspSoftwareSerial::SWSERIAL_8N1, D6, D5);
|
||||
swSer.enableIntTx(true);
|
||||
logSer.println(PSTR("Tx on swSer"));
|
||||
}
|
||||
|
||||
uint8_t val = 0xff;
|
||||
|
||||
void loop() {
|
||||
swSer.write((uint8_t)0x00);
|
||||
swSer.write(val);
|
||||
swSer.write(val);
|
||||
auto start = ESP.getCycleCount();
|
||||
int rxCnt = 0;
|
||||
while (ESP.getCycleCount() - start < ESP.getCpuFreqMHz() * 1000000 / 10) {
|
||||
if (hwSer.available()) {
|
||||
auto rxVal = hwSer.read();
|
||||
if ((!rxCnt && rxVal) || (rxCnt && rxVal != val)) {
|
||||
logSer.printf(PSTR("Rx bit error: tx = 0x%02x, rx = 0x%02x\n"), val, rxVal);
|
||||
}
|
||||
++rxCnt;
|
||||
}
|
||||
}
|
||||
if (rxCnt != 3) {
|
||||
logSer.printf(PSTR("Rx cnt error, tx = 0x%02x\n"), val);
|
||||
}
|
||||
++val;
|
||||
if (!val) {
|
||||
logSer.println("Starting over");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// circular_mp_test.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include "circular_queue/circular_queue_mp.h"
|
||||
|
||||
struct qitem
|
||||
{
|
||||
// produer id
|
||||
int id;
|
||||
// monotonic increasing value
|
||||
int val = 0;
|
||||
};
|
||||
|
||||
constexpr int TOTALMESSAGESTARGET = 60000000;
|
||||
// reserve one thread as consumer
|
||||
const auto THREADS = std::thread::hardware_concurrency() / 2 - 1;
|
||||
const int MESSAGES = TOTALMESSAGESTARGET / THREADS;
|
||||
circular_queue<std::thread> threads(THREADS);
|
||||
circular_queue_mp<qitem> queue(threads.capacity()* MESSAGES / 10);
|
||||
std::vector<int> checks(threads.capacity());
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::cerr << "Utilizing " << THREADS << " producer threads" << std::endl;
|
||||
for (int i = 0; i < threads.capacity(); ++i)
|
||||
{
|
||||
threads.push(std::thread([i]() {
|
||||
for (int c = 0; c < MESSAGES;)
|
||||
{
|
||||
// simulate some load
|
||||
auto start = std::chrono::system_clock::now();
|
||||
while (std::chrono::system_clock::now() - start < 1us);
|
||||
if (queue.push({ i, c }))
|
||||
{
|
||||
++c;
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cerr << "queue full" << std::endl;
|
||||
//std::this_thread::sleep_for(10us);
|
||||
}
|
||||
//if (0 == c % 10000) std::this_thread::sleep_for(10us);
|
||||
}
|
||||
}));
|
||||
}
|
||||
for (int o = 0; o < threads.available() * MESSAGES; ++o)
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
while (!queue.available())
|
||||
{
|
||||
auto starvedFor = std::chrono::system_clock::now() - now;
|
||||
if (starvedFor > 20s) std::cerr << "queue starved for > 20s" << std::endl;
|
||||
//std::this_thread::sleep_for(20ms);
|
||||
}
|
||||
auto item = queue.pop();
|
||||
if (checks[item.id] != item.val)
|
||||
{
|
||||
std::cerr << "item mismatch" << std::endl;
|
||||
}
|
||||
checks[item.id] = item.val + 1;
|
||||
if (0 == item.val % 1000) std::this_thread::sleep_for(100us);
|
||||
}
|
||||
while (threads.available())
|
||||
{
|
||||
auto thread = threads.pop();
|
||||
thread.join();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
279
lib/EspSoftwareSerial/examples/loopback/loopback.ino
Normal file
279
lib/EspSoftwareSerial/examples/loopback/loopback.ino
Normal file
@@ -0,0 +1,279 @@
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
// On ESP8266:
|
||||
// Local EspSoftwareSerial loopback, connect D5 (rx) and D6 (tx).
|
||||
// For local hardware loopback, connect D5 to D8 (tx), D6 to D7 (rx).
|
||||
// For hardware send/sink, connect D7 (rx) and D8 (tx).
|
||||
// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking
|
||||
// interrupts severely impacts the ability of the EspSoftwareSerial devices to operate concurrently
|
||||
// and/or in duplex mode.
|
||||
// Operating in software serial full duplex mode, runs at 19200bps and few errors (~2.5%).
|
||||
// Operating in software serial half duplex mode (both loopback and repeater),
|
||||
// runs at 57600bps with nearly no errors.
|
||||
// Operating loopback in full duplex, and repeater in half duplex, runs at 38400bps with nearly no errors.
|
||||
// On ESP32:
|
||||
// For EspSoftwareSerial or hardware send/sink, connect D5 (rx) and D6 (tx).
|
||||
// Hardware Serial2 defaults to D4 (rx), D3 (tx).
|
||||
// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx).
|
||||
|
||||
#ifndef D5
|
||||
#if defined(ESP8266)
|
||||
#define D8 (15)
|
||||
#define D5 (14)
|
||||
#define D7 (13)
|
||||
#define D6 (12)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#elif defined(ESP32)
|
||||
#define D8 (5)
|
||||
#define D5 (18)
|
||||
#define D7 (23)
|
||||
#define D6 (19)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Pick only one of HWLOOPBACK, HWSOURCESWSINK, or HWSOURCESINK
|
||||
//#define HWLOOPBACK 1
|
||||
//#define HWSOURCESWSINK 1
|
||||
//#define HWSOURCESINK 1
|
||||
#define HALFDUPLEX 1
|
||||
|
||||
#ifdef ESP32
|
||||
constexpr int IUTBITRATE = 19200;
|
||||
#else
|
||||
constexpr int IUTBITRATE = 19200;
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1;
|
||||
constexpr SerialConfig hwSerialConfig = ::SERIAL_8E1;
|
||||
#elif defined(ESP32)
|
||||
constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1;
|
||||
constexpr uint32_t hwSerialConfig = ::SERIAL_8E1;
|
||||
#else
|
||||
constexpr unsigned swSerialConfig = 3;
|
||||
#endif
|
||||
constexpr bool invert = false;
|
||||
|
||||
constexpr int BLOCKSIZE = 16; // use fractions of 256
|
||||
|
||||
unsigned long start;
|
||||
const char effTxTxt[] PROGMEM = "eff. tx: ";
|
||||
const char effRxTxt[] PROGMEM = "eff. rx: ";
|
||||
int txCount;
|
||||
int rxCount;
|
||||
int expected;
|
||||
int rxErrors;
|
||||
int rxParityErrors;
|
||||
constexpr int ReportInterval = IUTBITRATE / 8;
|
||||
|
||||
#if defined(ESP8266)
|
||||
#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK)
|
||||
HardwareSerial& hwSerial(Serial);
|
||||
EspSoftwareSerial::UART serialIUT;
|
||||
EspSoftwareSerial::UART logger;
|
||||
#elif defined(HWSOURCESINK)
|
||||
HardwareSerial& serialIUT(Serial);
|
||||
EspSoftwareSerial::UART logger;
|
||||
#else
|
||||
EspSoftwareSerial::UART serialIUT;
|
||||
HardwareSerial& logger(Serial);
|
||||
#endif
|
||||
#elif defined(ESP32)
|
||||
#if defined(HWLOOPBACK) || defined (HWSOURCESWSINK)
|
||||
HardwareSerial& hwSerial(Serial2);
|
||||
EspSoftwareSerial::UART serialIUT;
|
||||
#elif defined(HWSOURCESINK)
|
||||
HardwareSerial& serialIUT(Serial2);
|
||||
#else
|
||||
EspSoftwareSerial::UART serialIUT;
|
||||
#endif
|
||||
HardwareSerial& logger(Serial);
|
||||
#else
|
||||
EspSoftwareSerial::UART serialIUT(14, 12);
|
||||
HardwareSerial& logger(Serial);
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
#if defined(ESP8266)
|
||||
#if defined(HWLOOPBACK) || defined(HWSOURCESINK) || defined(HWSOURCESWSINK)
|
||||
Serial.begin(IUTBITRATE, hwSerialConfig, ::SERIAL_FULL, 1, invert);
|
||||
Serial.swap();
|
||||
Serial.setRxBufferSize(2 * BLOCKSIZE);
|
||||
logger.begin(9600, EspSoftwareSerial::SWSERIAL_8N1, -1, TX);
|
||||
#else
|
||||
logger.begin(9600);
|
||||
#endif
|
||||
#if !defined(HWSOURCESINK)
|
||||
serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE);
|
||||
#ifdef HALFDUPLEX
|
||||
serialIUT.enableIntTx(false);
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(ESP32)
|
||||
#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK)
|
||||
Serial2.begin(IUTBITRATE, hwSerialConfig, D4, D3, invert);
|
||||
Serial2.setRxBufferSize(2 * BLOCKSIZE);
|
||||
#elif defined(HWSOURCESINK)
|
||||
serialIUT.begin(IUTBITRATE, hwSerialConfig, D5, D6, invert);
|
||||
serialIUT.setRxBufferSize(2 * BLOCKSIZE);
|
||||
#endif
|
||||
#if !defined(HWSOURCESINK)
|
||||
serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE);
|
||||
#ifdef HALFDUPLEX
|
||||
serialIUT.enableIntTx(false);
|
||||
#endif
|
||||
#endif
|
||||
logger.begin(9600);
|
||||
#else
|
||||
#if !defined(HWSOURCESINK)
|
||||
serialIUT.begin(IUTBITRATE);
|
||||
#endif
|
||||
logger.begin(9600);
|
||||
#endif
|
||||
|
||||
logger.println(PSTR("Loopback example for EspEspSoftwareSerial"));
|
||||
|
||||
start = micros();
|
||||
txCount = 0;
|
||||
rxCount = 0;
|
||||
rxErrors = 0;
|
||||
rxParityErrors = 0;
|
||||
expected = -1;
|
||||
}
|
||||
|
||||
unsigned char c = 0;
|
||||
|
||||
void loop() {
|
||||
#ifdef HALFDUPLEX
|
||||
char block[BLOCKSIZE];
|
||||
#endif
|
||||
char inBuf[BLOCKSIZE];
|
||||
for (int i = 0; i < BLOCKSIZE; ++i) {
|
||||
#ifndef HALFDUPLEX
|
||||
#ifdef HWSOURCESWSINK
|
||||
hwSerial.write(c);
|
||||
#else
|
||||
serialIUT.write(c);
|
||||
#endif
|
||||
#ifdef HWLOOPBACK
|
||||
int avail = hwSerial.available();
|
||||
while ((0 == (i % 8)) && avail > 0) {
|
||||
int inCnt = hwSerial.read(inBuf, min(avail, min(BLOCKSIZE, hwSerial.availableForWrite())));
|
||||
hwSerial.write(inBuf, inCnt);
|
||||
avail -= inCnt;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
block[i] = c;
|
||||
#endif
|
||||
c = (c + 1) % 256;
|
||||
++txCount;
|
||||
}
|
||||
#ifdef HALFDUPLEX
|
||||
#ifdef HWSOURCESWSINK
|
||||
hwSerial.write(block, BLOCKSIZE);
|
||||
#else
|
||||
serialIUT.write(block, BLOCKSIZE);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HWSOURCESINK
|
||||
#if defined(ESP8266)
|
||||
if (serialIUT.hasOverrun()) { logger.println(PSTR("serialIUT.overrun")); }
|
||||
#endif
|
||||
#else
|
||||
if (serialIUT.overflow()) { logger.println(PSTR("serialIUT.overflow")); }
|
||||
#endif
|
||||
|
||||
int inCnt;
|
||||
uint32_t deadlineStart;
|
||||
|
||||
#ifdef HWLOOPBACK
|
||||
// starting deadline for the first bytes to become readable
|
||||
deadlineStart = ESP.getCycleCount();
|
||||
inCnt = 0;
|
||||
while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) {
|
||||
int avail = hwSerial.available();
|
||||
inCnt += hwSerial.read(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite())));
|
||||
if (inCnt >= BLOCKSIZE) { break; }
|
||||
// wait for more outstanding bytes to trickle in
|
||||
if (avail) deadlineStart = ESP.getCycleCount();
|
||||
}
|
||||
hwSerial.write(inBuf, inCnt);
|
||||
#endif
|
||||
|
||||
// starting deadline for the first bytes to come in
|
||||
deadlineStart = ESP.getCycleCount();
|
||||
inCnt = 0;
|
||||
while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) {
|
||||
int avail;
|
||||
if (0 != (swSerialConfig & 070))
|
||||
avail = serialIUT.available();
|
||||
else
|
||||
avail = serialIUT.read(inBuf, BLOCKSIZE);
|
||||
for (int i = 0; i < avail; ++i)
|
||||
{
|
||||
unsigned char r;
|
||||
if (0 != (swSerialConfig & 070))
|
||||
r = serialIUT.read();
|
||||
else
|
||||
r = inBuf[i];
|
||||
if (expected == -1) { expected = r; }
|
||||
else {
|
||||
expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4));
|
||||
}
|
||||
if (r != expected) {
|
||||
++rxErrors;
|
||||
expected = -1;
|
||||
}
|
||||
#ifndef HWSOURCESINK
|
||||
if (serialIUT.readParity() != (static_cast<bool>(swSerialConfig & 010) ? serialIUT.parityOdd(r) : serialIUT.parityEven(r)))
|
||||
{
|
||||
++rxParityErrors;
|
||||
}
|
||||
#elif defined(ESP8266)
|
||||
// current ESP8266 API does not flag parity errors separately
|
||||
if (serialIUT.hasRxError())
|
||||
{
|
||||
++rxParityErrors;
|
||||
}
|
||||
#endif
|
||||
++rxCount;
|
||||
++inCnt;
|
||||
}
|
||||
|
||||
if (inCnt >= BLOCKSIZE) { break; }
|
||||
// wait for more outstanding bytes to trickle in
|
||||
if (avail) deadlineStart = ESP.getCycleCount();
|
||||
}
|
||||
|
||||
const uint32_t interval = micros() - start;
|
||||
if (txCount >= ReportInterval && interval) {
|
||||
uint8_t wordBits = (5 + swSerialConfig % 4) + static_cast<bool>(swSerialConfig & 070) + 1 + ((swSerialConfig & 0300) ? 1 : 0);
|
||||
logger.println(String(PSTR("tx/rx: ")) + txCount + PSTR("/") + rxCount);
|
||||
const long txCps = txCount * (1000000.0 / interval);
|
||||
const long rxCps = rxCount * (1000000.0 / interval);
|
||||
logger.print(String(FPSTR(effTxTxt)) + wordBits * txCps + PSTR("bps, ")
|
||||
+ effRxTxt + wordBits * rxCps + PSTR("bps, ")
|
||||
+ rxErrors + PSTR(" errors (") + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + PSTR("%)"));
|
||||
if (0 != (swSerialConfig & 070))
|
||||
{
|
||||
logger.print(PSTR(" (")); logger.print(rxParityErrors); logger.println(PSTR(" parity errors)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.println();
|
||||
}
|
||||
txCount = 0;
|
||||
rxCount = 0;
|
||||
rxErrors = 0;
|
||||
rxParityErrors = 0;
|
||||
expected = -1;
|
||||
// resync
|
||||
delay(1000UL * 12 * BLOCKSIZE / IUTBITRATE * 16);
|
||||
serialIUT.flush();
|
||||
start = micros();
|
||||
}
|
||||
}
|
||||
59
lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino
Normal file
59
lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "SoftwareSerial.h"
|
||||
|
||||
#ifndef D5
|
||||
#if defined(ESP8266)
|
||||
#define D5 (14)
|
||||
#define D6 (12)
|
||||
#elif defined(ESP32)
|
||||
#define D5 (18)
|
||||
#define D6 (19)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
EspSoftwareSerial::UART swSer1;
|
||||
EspSoftwareSerial::UART swSer2;
|
||||
|
||||
void checkSwSerial(EspSoftwareSerial::UART* ss) {
|
||||
byte ch;
|
||||
while (!Serial.available());
|
||||
ss->enableTx(true);
|
||||
while (Serial.available()) {
|
||||
ch = Serial.read();
|
||||
ss->write(ch);
|
||||
}
|
||||
ss->enableTx(false);
|
||||
// wait 1 second for the reply from EspSoftwareSerial if any
|
||||
delay(1000);
|
||||
if (ss->available()) {
|
||||
Serial.print(PSTR("\nResult:"));
|
||||
while (ss->available()) {
|
||||
ch = (byte)ss->read();
|
||||
Serial.print(ch < 0x10 ? PSTR(" 0") : PSTR(" "));
|
||||
Serial.print(ch, HEX);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(2000);
|
||||
Serial.begin(115200);
|
||||
Serial.println(PSTR("\nOne Wire Half Duplex Serial Tester"));
|
||||
swSer1.begin(115200, EspSoftwareSerial::SWSERIAL_8N1, D6, D6, false, 256);
|
||||
// high speed half duplex, turn off interrupts during tx
|
||||
swSer1.enableIntTx(false);
|
||||
swSer2.begin(115200, EspSoftwareSerial::SWSERIAL_8N1, D5, D5, false, 256);
|
||||
// high speed half duplex, turn off interrupts during tx
|
||||
swSer2.enableIntTx(false);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println(PSTR("\n\nTesting on swSer1"));
|
||||
Serial.print(PSTR("Enter something to send using swSer1."));
|
||||
checkSwSerial(&swSer1);
|
||||
|
||||
Serial.println(PSTR("\n\nTesting on swSer2"));
|
||||
Serial.print(PSTR("Enter something to send using swSer2."));
|
||||
checkSwSerial(&swSer2);
|
||||
|
||||
}
|
||||
80
lib/EspSoftwareSerial/examples/onreceive/onreceive.ino
Normal file
80
lib/EspSoftwareSerial/examples/onreceive/onreceive.ino
Normal file
@@ -0,0 +1,80 @@
|
||||
// On ESP8266:
|
||||
// Runs up to 115200bps at 80MHz, 250000bps at 160MHz, with nearly zero errors.
|
||||
// This example is currently not ported to ESP32, which is based on FreeRTOS.
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
#ifndef D5
|
||||
#define D8 (15)
|
||||
#define D5 (14)
|
||||
#define D7 (13)
|
||||
#define D6 (12)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#endif
|
||||
|
||||
#define BAUD_RATE 115200
|
||||
#define MAX_FRAMEBITS (1 + 8 + 1 + 2)
|
||||
|
||||
EspSoftwareSerial::UART testSerial;
|
||||
|
||||
// Becomes set from ISR / IRQ callback function.
|
||||
std::atomic<bool> rxPending(false);
|
||||
|
||||
void IRAM_ATTR receiveHandler() {
|
||||
rxPending.store(true);
|
||||
esp_schedule();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(false);
|
||||
Serial.swap();
|
||||
testSerial.begin(BAUD_RATE, EspSoftwareSerial::SWSERIAL_8N1, RX, TX);
|
||||
// Only half duplex this way, but reliable TX timings for high bps
|
||||
testSerial.enableIntTx(false);
|
||||
testSerial.onReceive(receiveHandler);
|
||||
|
||||
testSerial.println(PSTR("\nSoftware serial onReceive() event test started"));
|
||||
|
||||
for (char ch = ' '; ch <= 'z'; ch++) {
|
||||
testSerial.write(ch);
|
||||
}
|
||||
testSerial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#ifdef ESP8266
|
||||
bool isRxPending = rxPending.load();
|
||||
if (isRxPending) {
|
||||
rxPending.store(false);
|
||||
}
|
||||
#else
|
||||
bool isRxPending = m_isrOverflow.exchange(false);
|
||||
#endif
|
||||
auto avail = testSerial.available();
|
||||
if (isRxPending && !avail) {
|
||||
// event fired on start bit, wait until first stop bit of longest frame
|
||||
delayMicroseconds(1 + MAX_FRAMEBITS * 1000000 / BAUD_RATE);
|
||||
avail = testSerial.available();
|
||||
}
|
||||
if (!avail) {
|
||||
// On development board, idle power draw at USB:
|
||||
// with yield() 77mA, 385mW (160MHz: 82mA, 410mW)
|
||||
// with esp_suspend() 20mA, 100mW (at 160MHz, too)
|
||||
//yield();
|
||||
esp_suspend();
|
||||
return;
|
||||
}
|
||||
// try to force to half-duplex
|
||||
decltype(avail) prev_avail;
|
||||
do {
|
||||
delayMicroseconds(1 + MAX_FRAMEBITS * 1000000 / BAUD_RATE);
|
||||
prev_avail = avail;
|
||||
} while (prev_avail != (avail = testSerial.available()));
|
||||
while (avail > 0) {
|
||||
testSerial.write(testSerial.read());
|
||||
avail = testSerial.available();
|
||||
}
|
||||
testSerial.println();
|
||||
}
|
||||
199
lib/EspSoftwareSerial/examples/repeater/repeater.ino
Normal file
199
lib/EspSoftwareSerial/examples/repeater/repeater.ino
Normal file
@@ -0,0 +1,199 @@
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
// On ESP8266:
|
||||
// EspSoftwareSerial loopback for remote source (loopback.ino), or hardware loopback.
|
||||
// Connect source D5 (rx) to local D8 (tx), source D6 (tx) to local D7 (rx).
|
||||
// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking
|
||||
// interrupts severely impacts the ability of the EspSoftwareSerial devices to operate concurrently
|
||||
// and/or in duplex mode.
|
||||
// On ESP32:
|
||||
// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx).
|
||||
|
||||
#ifndef D5
|
||||
#if defined(ESP8266)
|
||||
#define D8 (15)
|
||||
#define D5 (14)
|
||||
#define D7 (13)
|
||||
#define D6 (12)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#elif defined(ESP32)
|
||||
#define D8 (5)
|
||||
#define D5 (18)
|
||||
#define D7 (23)
|
||||
#define D6 (19)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define HWLOOPBACK 1
|
||||
#define HALFDUPLEX 1
|
||||
|
||||
#ifdef ESP32
|
||||
constexpr int IUTBITRATE = 19200;
|
||||
#else
|
||||
constexpr int IUTBITRATE = 19200;
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1;
|
||||
constexpr SerialConfig hwSerialConfig = ::SERIAL_8E1;
|
||||
#elif defined(ESP32)
|
||||
constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1;
|
||||
constexpr uint32_t hwSerialConfig = ::SERIAL_8E1;
|
||||
#else
|
||||
constexpr unsigned swSerialConfig = 3;
|
||||
#endif
|
||||
constexpr bool invert = false;
|
||||
|
||||
constexpr int BLOCKSIZE = 16; // use fractions of 256
|
||||
|
||||
unsigned long start;
|
||||
const char bitRateTxt[] PROGMEM = "Effective data rate: ";
|
||||
int rxCount;
|
||||
int seqErrors;
|
||||
int parityErrors;
|
||||
int expected;
|
||||
constexpr int ReportInterval = IUTBITRATE / 8;
|
||||
|
||||
#if defined(ESP8266)
|
||||
#if defined(HWLOOPBACK)
|
||||
HardwareSerial& repeater(Serial);
|
||||
EspSoftwareSerial::UART logger;
|
||||
#else
|
||||
EspSoftwareSerial::UART repeater;
|
||||
HardwareSerial& logger(Serial);
|
||||
#endif
|
||||
#elif defined(ESP32)
|
||||
#if defined(HWLOOPBACK)
|
||||
HardwareSerial& repeater(Serial2);
|
||||
#else
|
||||
EspSoftwareSerial::UART repeater;
|
||||
#endif
|
||||
HardwareSerial& logger(Serial);
|
||||
#else
|
||||
EspSoftwareSerial::UART repeater(14, 12);
|
||||
HardwareSerial& logger(Serial);
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
#if defined(ESP8266)
|
||||
#if defined(HWLOOPBACK)
|
||||
repeater.begin(IUTBITRATE, hwSerialConfig, ::SERIAL_FULL, 1, invert);
|
||||
repeater.swap();
|
||||
repeater.setRxBufferSize(2 * BLOCKSIZE);
|
||||
logger.begin(9600, EspSoftwareSerial::SWSERIAL_8N1, -1, TX);
|
||||
#else
|
||||
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE);
|
||||
#ifdef HALFDUPLEX
|
||||
repeater.enableIntTx(false);
|
||||
#endif
|
||||
logger.begin(9600);
|
||||
#endif
|
||||
#elif defined(ESP32)
|
||||
#if defined(HWLOOPBACK)
|
||||
repeater.begin(IUTBITRATE, hwSerialConfig, D7, D8, invert);
|
||||
repeater.setRxBufferSize(2 * BLOCKSIZE);
|
||||
#else
|
||||
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE);
|
||||
#ifdef HALFDUPLEX
|
||||
repeater.enableIntTx(false);
|
||||
#endif
|
||||
#endif
|
||||
logger.begin(9600);
|
||||
#else
|
||||
repeater.begin(IUTBITRATE);
|
||||
logger.begin(9600);
|
||||
#endif
|
||||
|
||||
logger.println(PSTR("Repeater example for EspEspSoftwareSerial"));
|
||||
start = micros();
|
||||
rxCount = 0;
|
||||
seqErrors = 0;
|
||||
parityErrors = 0;
|
||||
expected = -1;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#ifdef HWLOOPBACK
|
||||
#if defined(ESP8266)
|
||||
if (repeater.hasOverrun()) { logger.println(PSTR("repeater.overrun")); }
|
||||
#endif
|
||||
#else
|
||||
if (repeater.overflow()) { logger.println(PSTR("repeater.overflow")); }
|
||||
#endif
|
||||
|
||||
#ifdef HALFDUPLEX
|
||||
char block[BLOCKSIZE];
|
||||
#endif
|
||||
// starting deadline for the first bytes to come in
|
||||
uint32_t deadlineStart = ESP.getCycleCount();
|
||||
int inCnt = 0;
|
||||
while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) {
|
||||
int avail = repeater.available();
|
||||
for (int i = 0; i < avail; ++i)
|
||||
{
|
||||
int r = repeater.read();
|
||||
if (r == -1) { logger.println(PSTR("read() == -1")); }
|
||||
if (expected == -1) { expected = r; }
|
||||
else {
|
||||
expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4));
|
||||
}
|
||||
if (r != expected) {
|
||||
++seqErrors;
|
||||
expected = -1;
|
||||
}
|
||||
#ifndef HWLOOPBACK
|
||||
if (repeater.readParity() != (static_cast<bool>(swSerialConfig & 010) ? repeater.parityOdd(r) : repeater.parityEven(r)))
|
||||
{
|
||||
++parityErrors;
|
||||
}
|
||||
#elif defined(ESP8266)
|
||||
// current ESP8266 API does not flag parity errors separately
|
||||
if (repeater.hasRxError())
|
||||
{
|
||||
++parityErrors;
|
||||
}
|
||||
#endif
|
||||
++rxCount;
|
||||
#ifdef HALFDUPLEX
|
||||
block[inCnt] = r;
|
||||
#else
|
||||
repeater.write(r);
|
||||
#endif
|
||||
if (++inCnt >= BLOCKSIZE) { break; }
|
||||
}
|
||||
if (inCnt >= BLOCKSIZE) { break; }
|
||||
// wait for more outstanding bytes to trickle in
|
||||
if (avail) deadlineStart = ESP.getCycleCount();
|
||||
}
|
||||
|
||||
#ifdef HALFDUPLEX
|
||||
repeater.write(block, inCnt);
|
||||
#endif
|
||||
|
||||
if (rxCount >= ReportInterval) {
|
||||
auto end = micros();
|
||||
unsigned long interval = end - start;
|
||||
long cps = rxCount * (1000000.0 / interval);
|
||||
long seqErrorsps = seqErrors * (1000000.0 / interval);
|
||||
logger.print(String(FPSTR(bitRateTxt)) + 10 * cps + PSTR("bps, ")
|
||||
+ seqErrorsps + PSTR("cps seq. errors (") + 100.0 * seqErrors / rxCount + PSTR("%)"));
|
||||
#ifndef HWLOOPBACK
|
||||
if (0 != (swSerialConfig & 070))
|
||||
{
|
||||
logger.print(PSTR(" (")); logger.print(parityErrors); logger.println(PSTR(" parity errors)"));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
logger.println();
|
||||
}
|
||||
start = end;
|
||||
rxCount = 0;
|
||||
seqErrors = 0;
|
||||
parityErrors = 0;
|
||||
expected = -1;
|
||||
}
|
||||
}
|
||||
79
lib/EspSoftwareSerial/examples/swsertest/swsertest.ino
Normal file
79
lib/EspSoftwareSerial/examples/swsertest/swsertest.ino
Normal file
@@ -0,0 +1,79 @@
|
||||
// On ESP8266:
|
||||
// At 80MHz runs up 57600bps, and at 160MHz CPU frequency up to 115200bps with only negligible errors.
|
||||
// Connect pin 13 to 15.
|
||||
// For verification and as a example for how to use SW serial on the USB to PC connection,
|
||||
// which allows the use of HW Serial on GPIO13 and GPIO15 instead, #define SWAPSERIAL below.
|
||||
// Notice how the bitrates are also swapped then between RX/TX and GPIO13/GPIO15.
|
||||
// Builtin debug output etc. must be stopped on HW Serial in this case, as it would interfere with the
|
||||
// external communication on GPIO13/GPIO15.
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
#ifndef D5
|
||||
#if defined(ESP8266)
|
||||
#define D8 (15)
|
||||
#define D5 (14)
|
||||
#define D7 (13)
|
||||
#define D6 (12)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#elif defined(ESP32)
|
||||
#define D8 (5)
|
||||
#define D5 (18)
|
||||
#define D7 (23)
|
||||
#define D6 (19)
|
||||
#define RX (3)
|
||||
#define TX (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
#define BAUD_RATE 57600
|
||||
#else
|
||||
#define BAUD_RATE 57600
|
||||
#endif
|
||||
|
||||
#undef SWAPSERIAL
|
||||
|
||||
#ifndef SWAPSERIAL
|
||||
auto& usbSerial = Serial;
|
||||
EspSoftwareSerial::UART testSerial;
|
||||
#else
|
||||
EspSoftwareSerial::UART usbSerial;
|
||||
auto& testSerial = Serial;
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
#ifndef SWAPSERIAL
|
||||
usbSerial.begin(115200);
|
||||
// Important: the buffer size optimizations here, in particular the isrBufSize (11) that is only sufficiently
|
||||
// large to hold a single word (up to start - 8 data - parity - stop), are on the basis that any char written
|
||||
// to the loopback EspSoftwareSerial adapter gets read before another write is performed.
|
||||
// Block writes with a size greater than 1 would usually fail. Do not copy this into your own project without
|
||||
// reading the documentation.
|
||||
testSerial.begin(BAUD_RATE, EspSoftwareSerial::SWSERIAL_8N1, D7, D8, false, 95, 11);
|
||||
#else
|
||||
testSerial.begin(115200);
|
||||
testSerial.setDebugOutput(false);
|
||||
testSerial.swap();
|
||||
usbSerial.begin(BAUD_RATE, EspSoftwareSerial::SWSERIAL_8N1, RX, TX, false, 95);
|
||||
#endif
|
||||
|
||||
usbSerial.println(PSTR("\nSoftware serial test started"));
|
||||
|
||||
for (char ch = ' '; ch <= 'z'; ch++) {
|
||||
testSerial.write(ch);
|
||||
}
|
||||
testSerial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
while (testSerial.available() > 0) {
|
||||
usbSerial.write(testSerial.read());
|
||||
yield();
|
||||
}
|
||||
while (usbSerial.available() > 0) {
|
||||
testSerial.write(usbSerial.read());
|
||||
yield();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user