first commit

This commit is contained in:
Jérôme Delacotte
2025-03-06 11:15:32 +01:00
commit 7b30d6e298
5276 changed files with 2108927 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
/*
* Implementation is in ArduinoBLE_UART_Stream.h to avoid making ArduinoBLE a
* build-time dependency for all projects that use the Firmata library.
*/

View File

@@ -0,0 +1,256 @@
/*
ArduinoBLE_UART_Stream.h
Based on BLEStream.h and the HardwareBLESerial library:
https://github.com/Uberi/Arduino-HardwareBLESerial
*/
#ifndef _ARDUINO_BLE_UART_STREAM_H_
#define _ARDUINO_BLE_UART_STREAM_H_
#include <ArduinoBLE.h>
#define BLE_ATTRIBUTE_MAX_VALUE_LENGTH 20
class ArduinoBLE_UART_Stream : public Stream
{
public:
ArduinoBLE_UART_Stream();
void setLocalName(const char *localName);
void setAdvertisingInterval(unsigned short advertisingInterval);
void setConnectionInterval(unsigned short minConnInterval, unsigned short maxConnInterval);
void setFlushInterval(int flushInterval);
void begin();
bool poll();
void end();
// Print overrides
size_t write(uint8_t byte);
using Print::write; // Expose other write variants
// Stream overrides
int available();
int read();
int peek();
void flush();
private:
void dataReceived(const unsigned char *data, size_t size);
static void connectedHandler(BLEDevice central);
static void disconnectedHandler(BLEDevice central);
static void rxWrittenHandler(BLEDevice central, BLECharacteristic characteristic);
static void txSubscribedHandler(BLEDevice central, BLECharacteristic characteristic);
static void txUnsubscribedHandler(BLEDevice central, BLECharacteristic characteristic);
BLEService uartService;
BLECharacteristic rxCharacteristic;
BLECharacteristic txCharacteristic;
String localName;
unsigned short advertisingInterval;
unsigned short minConnInterval;
unsigned short maxConnInterval;
int flushInterval;
bool connected;
unsigned char rxBuffer[256];
size_t rxHead;
size_t rxTail;
bool txSubscribed;
unsigned char txBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH];
size_t txCount;
unsigned long lastFlushTime;
static ArduinoBLE_UART_Stream *instance;
};
ArduinoBLE_UART_Stream::ArduinoBLE_UART_Stream() :
uartService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"),
rxCharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse | BLEWrite, BLE_ATTRIBUTE_MAX_VALUE_LENGTH),
txCharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, BLE_ATTRIBUTE_MAX_VALUE_LENGTH),
advertisingInterval(0),
minConnInterval(0),
maxConnInterval(0),
flushInterval(100), // Default flush interval is 100ms
connected(false),
rxHead(0),
rxTail(0),
txSubscribed(false),
txCount(0),
lastFlushTime(0)
{
instance = this;
}
void ArduinoBLE_UART_Stream::setLocalName(const char *localName)
{
this->localName = localName;
}
void ArduinoBLE_UART_Stream::setAdvertisingInterval(unsigned short advertisingInterval)
{
this->advertisingInterval = advertisingInterval;
}
void ArduinoBLE_UART_Stream::setConnectionInterval(unsigned short minConnInterval, unsigned short maxConnInterval)
{
this->minConnInterval = minConnInterval;
this->maxConnInterval = maxConnInterval;
}
void ArduinoBLE_UART_Stream::setFlushInterval(int flushInterval)
{
// The minimum allowed connection interval is 7.5ms, so don't try to flush
// more frequently than that
this->flushInterval = max(flushInterval, 8);
}
void ArduinoBLE_UART_Stream::begin()
{
BLE.begin();
if (localName.length() > 0) {
BLE.setLocalName(localName.c_str());
}
if (advertisingInterval > 0) {
BLE.setAdvertisingInterval(advertisingInterval);
}
if (minConnInterval > 0 && maxConnInterval > 0) {
BLE.setConnectionInterval(minConnInterval, maxConnInterval);
}
BLE.setEventHandler(BLEConnected, connectedHandler);
BLE.setEventHandler(BLEDisconnected, disconnectedHandler);
rxCharacteristic.setEventHandler(BLEWritten, rxWrittenHandler);
uartService.addCharacteristic(rxCharacteristic);
txCharacteristic.setEventHandler(BLESubscribed, txSubscribedHandler);
txCharacteristic.setEventHandler(BLEUnsubscribed, txUnsubscribedHandler);
uartService.addCharacteristic(txCharacteristic);
BLE.addService(uartService);
BLE.setAdvertisedService(uartService);
BLE.advertise();
}
bool ArduinoBLE_UART_Stream::poll()
{
if (millis() - lastFlushTime > flushInterval) {
flush(); // Always calls BLE.poll()
} else {
BLE.poll();
}
return connected;
}
void ArduinoBLE_UART_Stream::end()
{
flush();
txCharacteristic.setEventHandler(BLEUnsubscribed, NULL);
txCharacteristic.setEventHandler(BLESubscribed, NULL);
txSubscribed = false;
rxCharacteristic.setEventHandler(BLEWritten, NULL);
rxHead = 0;
rxTail = 0;
BLE.setEventHandler(BLEDisconnected, NULL);
BLE.setEventHandler(BLEConnected, NULL);
connected = false;
BLE.end();
}
size_t ArduinoBLE_UART_Stream::write(uint8_t byte)
{
if (!txSubscribed) {
return 0;
}
txBuffer[txCount] = byte;
txCount++;
if (txCount == sizeof(txBuffer)) {
flush();
}
return 1;
}
int ArduinoBLE_UART_Stream::available()
{
return (rxHead - rxTail + sizeof(rxBuffer)) % sizeof(rxBuffer);
}
int ArduinoBLE_UART_Stream::read()
{
if (rxTail == rxHead) {
return -1;
}
uint8_t byte = rxBuffer[rxTail];
rxTail = (rxTail + 1) % sizeof(rxBuffer);
return byte;
}
int ArduinoBLE_UART_Stream::peek()
{
if (rxTail == rxHead) {
return -1;
}
return rxBuffer[rxTail];
}
void ArduinoBLE_UART_Stream::flush()
{
if (txCount > 0) {
txCharacteristic.setValue(txBuffer, txCount);
txCount = 0;
}
lastFlushTime = millis();
BLE.poll();
}
void ArduinoBLE_UART_Stream::dataReceived(const unsigned char *data, size_t size)
{
for (size_t i = 0; i < size; i++) {
rxBuffer[rxHead] = data[i];
rxHead = (rxHead + 1) % sizeof(rxBuffer);
}
}
void ArduinoBLE_UART_Stream::connectedHandler(BLEDevice central)
{
instance->connected = true;
}
void ArduinoBLE_UART_Stream::disconnectedHandler(BLEDevice central)
{
instance->connected = false;
}
void ArduinoBLE_UART_Stream::rxWrittenHandler(BLEDevice central, BLECharacteristic characteristic)
{
instance->dataReceived(characteristic.value(), characteristic.valueLength());
}
void ArduinoBLE_UART_Stream::txSubscribedHandler(BLEDevice central, BLECharacteristic characteristic)
{
instance->txSubscribed = true;
}
void ArduinoBLE_UART_Stream::txUnsubscribedHandler(BLEDevice central, BLECharacteristic characteristic)
{
instance->txSubscribed = false;
}
ArduinoBLE_UART_Stream * ArduinoBLE_UART_Stream::instance = NULL;
#endif // _ARDUINO_BLE_UART_STREAM_H_

View File

@@ -0,0 +1,3 @@
/*
* Implementation is in BLEStream.h to avoid linker issues.
*/

View File

@@ -0,0 +1,243 @@
/*
BLEStream.h
Based on BLESerial.cpp by Voita Molda
https://github.com/sandeepmistry/arduino-BLEPeripheral/blob/master/examples/serial/BLESerial.h
Last updated April 4th, 2016
*/
#ifndef _BLE_STREAM_H_
#define _BLE_STREAM_H_
#include <Arduino.h>
#if defined(_VARIANT_ARDUINO_101_X_)
#include <CurieBLE.h>
#define _MAX_ATTR_DATA_LEN_ BLE_MAX_ATTR_DATA_LEN
#else
#include <BLEPeripheral.h>
#define _MAX_ATTR_DATA_LEN_ BLE_ATTRIBUTE_MAX_VALUE_LENGTH
#endif
#define BLESTREAM_TXBUFFER_FLUSH_INTERVAL 80
#define BLESTREAM_MIN_FLUSH_INTERVAL 8 // minimum interval for flushing the TX buffer
// #define BLE_SERIAL_DEBUG
class BLEStream : public BLEPeripheral, public Stream
{
public:
BLEStream(unsigned char req = 0, unsigned char rdy = 0, unsigned char rst = 0);
void begin(...);
bool poll();
void end();
void setFlushInterval(int);
virtual int available(void);
virtual int peek(void);
virtual int read(void);
virtual void flush(void);
virtual size_t write(uint8_t byte);
using Print::write;
virtual operator bool();
private:
bool _connected;
unsigned long _flushed;
int _flushInterval;
static BLEStream* _instance;
size_t _rxHead;
size_t _rxTail;
size_t _rxCount() const;
unsigned char _rxBuffer[256];
size_t _txCount;
unsigned char _txBuffer[_MAX_ATTR_DATA_LEN_];
BLEService _uartService = BLEService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
BLEDescriptor _uartNameDescriptor = BLEDescriptor("2901", "UART");
BLECharacteristic _rxCharacteristic = BLECharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse, _MAX_ATTR_DATA_LEN_);
BLEDescriptor _rxNameDescriptor = BLEDescriptor("2901", "RX - Receive Data (Write)");
BLECharacteristic _txCharacteristic = BLECharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, _MAX_ATTR_DATA_LEN_);
BLEDescriptor _txNameDescriptor = BLEDescriptor("2901", "TX - Transfer Data (Notify)");
void _received(const unsigned char* data, size_t size);
static void _received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic);
};
/*
* BLEStream.cpp
* Copied here as a hack to avoid having to install the BLEPeripheral libarary even if it's
* not needed.
*/
BLEStream* BLEStream::_instance = NULL;
BLEStream::BLEStream(unsigned char req, unsigned char rdy, unsigned char rst) :
#if defined(_VARIANT_ARDUINO_101_X_)
BLEPeripheral()
#else
BLEPeripheral(req, rdy, rst)
#endif
{
this->_txCount = 0;
this->_rxHead = this->_rxTail = 0;
this->_flushed = 0;
this->_flushInterval = BLESTREAM_TXBUFFER_FLUSH_INTERVAL;
BLEStream::_instance = this;
addAttribute(this->_uartService);
addAttribute(this->_uartNameDescriptor);
setAdvertisedServiceUuid(this->_uartService.uuid());
addAttribute(this->_rxCharacteristic);
addAttribute(this->_rxNameDescriptor);
this->_rxCharacteristic.setEventHandler(BLEWritten, BLEStream::_received);
addAttribute(this->_txCharacteristic);
addAttribute(this->_txNameDescriptor);
}
void BLEStream::begin(...)
{
BLEPeripheral::begin();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLEStream::begin()"));
#endif
}
bool BLEStream::poll()
{
// BLEPeripheral::poll is called each time connected() is called
this->_connected = BLEPeripheral::connected();
if (millis() > this->_flushed + this->_flushInterval) {
flush();
}
return this->_connected;
}
void BLEStream::end()
{
this->_rxCharacteristic.setEventHandler(BLEWritten, (void(*)(BLECentral&, BLECharacteristic&))NULL);
this->_rxHead = this->_rxTail = 0;
flush();
BLEPeripheral::disconnect();
}
int BLEStream::available(void)
{
// BLEPeripheral::poll only calls delay(1) in CurieBLE so skipping it here to avoid the delay
#ifndef _VARIANT_ARDUINO_101_X_
// TODO Need to do more testing to determine if all of these calls to BLEPeripheral::poll are
// actually necessary. Seems to run fine without them, but only minimal testing so far.
BLEPeripheral::poll();
#endif
int retval = (this->_rxHead - this->_rxTail + sizeof(this->_rxBuffer)) % sizeof(this->_rxBuffer);
#ifdef BLE_SERIAL_DEBUG
if (retval > 0) {
Serial.print(F("BLEStream::available() = "));
Serial.println(retval);
}
#endif
return retval;
}
int BLEStream::peek(void)
{
#ifndef _VARIANT_ARDUINO_101_X_
BLEPeripheral::poll();
#endif
if (this->_rxTail == this->_rxHead) return -1;
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::peek() = 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
int BLEStream::read(void)
{
#ifndef _VARIANT_ARDUINO_101_X_
BLEPeripheral::poll();
#endif
if (this->_rxTail == this->_rxHead) return -1;
this->_rxTail = (this->_rxTail + 1) % sizeof(this->_rxBuffer);
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::read() = 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
void BLEStream::flush(void)
{
if (this->_txCount == 0) return;
#ifndef _VARIANT_ARDUINO_101_X_
// ensure there are available packets before sending
while(!this->_txCharacteristic.canNotify()) {
BLEPeripheral::poll();
}
#endif
this->_txCharacteristic.setValue(this->_txBuffer, this->_txCount);
this->_flushed = millis();
this->_txCount = 0;
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLEStream::flush()"));
#endif
}
size_t BLEStream::write(uint8_t byte)
{
#ifndef _VARIANT_ARDUINO_101_X_
BLEPeripheral::poll();
#endif
if (this->_txCharacteristic.subscribed() == false) return 0;
this->_txBuffer[this->_txCount++] = byte;
if (this->_txCount == sizeof(this->_txBuffer)) flush();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::write( 0x"));
Serial.print(byte, HEX);
Serial.println(F(") = 1"));
#endif
return 1;
}
BLEStream::operator bool()
{
bool retval = this->_connected = BLEPeripheral::connected();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::operator bool() = "));
Serial.println(retval);
#endif
return retval;
}
void BLEStream::setFlushInterval(int interval)
{
if (interval > BLESTREAM_MIN_FLUSH_INTERVAL) {
this->_flushInterval = interval;
}
}
void BLEStream::_received(const unsigned char* data, size_t size)
{
for (size_t i = 0; i < size; i++) {
this->_rxHead = (this->_rxHead + 1) % sizeof(this->_rxBuffer);
this->_rxBuffer[this->_rxHead] = data[i];
}
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::received("));
for (int i = 0; i < size; i++) Serial.print(data[i], HEX);
Serial.println(F(")"));
#endif
}
void BLEStream::_received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic)
{
BLEStream::_instance->_received(rxCharacteristic.value(), rxCharacteristic.valueLength());
}
#endif // _BLE_STREAM_H_

View File

@@ -0,0 +1,5 @@
/*
* Implementation is in BluefruitLE_SPI_Stream.h to avoid making
* Adafruit_BluefruitLE_nRF51 a build-time dependency for all projects that use
* the Firmata library.
*/

View File

@@ -0,0 +1,167 @@
/*
BluefruitLE_SPI_Stream.h
Documentation for the various AT commands used below is available at
https://learn.adafruit.com/adafruit-feather-m0-bluefruit-le/at-commands
*/
#ifndef _BLUEFRUIT_LE_SPI_STREAM_H_
#define _BLUEFRUIT_LE_SPI_STREAM_H_
#include <Adafruit_BluefruitLE_SPI.h>
class BluefruitLE_SPI_Stream : public Stream
{
public:
BluefruitLE_SPI_Stream(int8_t csPin, int8_t irqPin, int8_t rstPin);
void setLocalName(const char *localName);
void setAdvertisingInterval(unsigned short advertisingInterval);
void setConnectionInterval(unsigned short minConnInterval, unsigned short maxConnInterval);
void setFlushInterval(int flushInterval);
void begin();
bool poll();
void end();
// Print overrides
size_t write(uint8_t byte);
using Print::write; // Expose other write variants
// Stream overrides
int available();
int read();
int peek();
void flush();
private:
Adafruit_BluefruitLE_SPI ble;
String localName;
unsigned short advertisingInterval;
unsigned short minConnInterval;
unsigned short maxConnInterval;
uint8_t txBuffer[SDEP_MAX_PACKETSIZE];
size_t txCount;
};
BluefruitLE_SPI_Stream::BluefruitLE_SPI_Stream(int8_t csPin, int8_t irqPin, int8_t rstPin) :
ble(csPin, irqPin, rstPin),
advertisingInterval(0),
minConnInterval(0),
maxConnInterval(0),
txCount(0)
{ }
void BluefruitLE_SPI_Stream::setLocalName(const char *localName)
{
this->localName = localName;
}
void BluefruitLE_SPI_Stream::setAdvertisingInterval(unsigned short advertisingInterval)
{
this->advertisingInterval = advertisingInterval;
}
void BluefruitLE_SPI_Stream::setConnectionInterval(unsigned short minConnInterval, unsigned short maxConnInterval)
{
this->minConnInterval = minConnInterval;
this->maxConnInterval = maxConnInterval;
}
void BluefruitLE_SPI_Stream::setFlushInterval(int flushInterval)
{
// Not used
}
void BluefruitLE_SPI_Stream::begin()
{
// Initialize the SPI interface
ble.begin();
// Perform a factory reset to make sure everything is in a known state
ble.factoryReset();
// Disable command echo from Bluefruit
ble.echo(false);
// Change the MODE LED to indicate BLE UART activity
ble.println("AT+HWMODELED=BLEUART");
// Set local name
if (localName.length() > 0) {
ble.print("AT+GAPDEVNAME=");
ble.println(localName);
}
// Set connection and advertising intervals
ble.print("AT+GAPINTERVALS=");
if (minConnInterval > 0) ble.print(minConnInterval);
ble.print(",");
if (maxConnInterval > 0) ble.print(maxConnInterval);
ble.print(",");
if (advertisingInterval > 0) ble.print(advertisingInterval);
ble.print(",,"); // Always omit fast advertising timeout, hence two commas
if (advertisingInterval > 0) ble.print(advertisingInterval);
ble.println();
// Disable real and simulated mode switch (i.e. "+++") command
ble.println("AT+MODESWITCHEN=local,0");
ble.enableModeSwitchCommand(false);
// Switch to data mode
ble.setMode(BLUEFRUIT_MODE_DATA);
}
bool BluefruitLE_SPI_Stream::poll()
{
// If there's outgoing data in the buffer, just send it. The firmware on
// the nRF51822 will decide when to transmit the data in its TX FIFO.
if (txCount) flush();
// In order to check for a connection, we would need to switch from data to
// command mode and back again. However, due to the internal workings of
// Adafruit_BluefruitLE_SPI, this can lead to unread incoming data being
// lost. Therefore, we always return true.
return true;
}
void BluefruitLE_SPI_Stream::end()
{
flush();
ble.end();
}
size_t BluefruitLE_SPI_Stream::write(uint8_t byte)
{
txBuffer[txCount++] = byte;
if (txCount == sizeof(txBuffer)) flush();
return 1;
}
int BluefruitLE_SPI_Stream::available()
{
return ble.available();
}
int BluefruitLE_SPI_Stream::read()
{
return ble.read();
}
int BluefruitLE_SPI_Stream::peek()
{
return ble.peek();
}
void BluefruitLE_SPI_Stream::flush()
{
ble.write(txBuffer, txCount);
txCount = 0;
}
#endif // _BLUEFRUIT_LE_SPI_STREAM_H_

View File

@@ -0,0 +1,3 @@
/*
* Implementation is in EthernetClientStream.h to avoid linker issues.
*/

View File

@@ -0,0 +1,166 @@
/*
EthernetClientStream.h
An Arduino-Stream that wraps an instance of Client reconnecting to
the remote-ip in a transparent way. A disconnected client may be
recognized by the returnvalues -1 from calls to peek or read and
a 0 from calls to write.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated March 10th, 2020
*/
#ifndef ETHERNETCLIENTSTREAM_H
#define ETHERNETCLIENTSTREAM_H
#include <inttypes.h>
#include <Stream.h>
//#define SERIAL_DEBUG
#include "firmataDebug.h"
#define MILLIS_RECONNECT 5000
#define HOST_CONNECTION_DISCONNECTED 0
#define HOST_CONNECTION_CONNECTED 1
extern "C" {
// callback function types
typedef void (*hostConnectionCallbackFunction)(byte);
}
class EthernetClientStream : public Stream
{
public:
EthernetClientStream(Client &client, IPAddress localip, IPAddress ip, const char* host, uint16_t port);
int available();
int read();
int peek();
void flush();
size_t write(uint8_t);
void maintain(IPAddress localip);
void attach(hostConnectionCallbackFunction newFunction);
private:
Client &client;
IPAddress localip;
IPAddress ip;
const char* host;
uint16_t port;
bool connected;
uint32_t time_connect;
hostConnectionCallbackFunction currentHostConnectionCallback;
bool maintain();
void stop();
};
/*
* EthernetClientStream.cpp
* Copied here as a hack to linker issues with 3rd party board packages that don't properly
* implement the Arduino network APIs.
*/
EthernetClientStream::EthernetClientStream(Client &client, IPAddress localip, IPAddress ip, const char* host, uint16_t port)
: client(client),
localip(localip),
ip(ip),
host(host),
port(port),
connected(false)
, currentHostConnectionCallback(nullptr)
{
}
int
EthernetClientStream::available()
{
return maintain() ? client.available() : 0;
}
int
EthernetClientStream::read()
{
return maintain() ? client.read() : -1;
}
int
EthernetClientStream::peek()
{
return maintain() ? client.peek() : -1;
}
void EthernetClientStream::flush()
{
if (maintain())
client.flush();
}
size_t
EthernetClientStream::write(uint8_t c)
{
return maintain() ? client.write(c) : 0;
}
void
EthernetClientStream::maintain(IPAddress localip)
{
// ensure the local IP is updated in the case that it is changed by the DHCP server
if (this->localip != localip) {
this->localip = localip;
if (connected)
stop();
}
}
void
EthernetClientStream::stop()
{
client.stop();
if (currentHostConnectionCallback)
{
(*currentHostConnectionCallback)(HOST_CONNECTION_DISCONNECTED);
}
connected = false;
time_connect = millis();
}
void
EthernetClientStream::attach(hostConnectionCallbackFunction newFunction)
{
currentHostConnectionCallback = newFunction;
}
bool
EthernetClientStream::maintain()
{
if (client && client.connected())
return true;
if (connected) {
stop();
}
// if the client is disconnected, attempt to reconnect every 5 seconds
else if (millis() - time_connect >= MILLIS_RECONNECT) {
connected = host ? client.connect(host, port) : client.connect(ip, port);
if (!connected) {
time_connect = millis();
DEBUG_PRINTLN("Connection failed. Attempting to reconnect...");
} else {
DEBUG_PRINTLN("Connected");
if (currentHostConnectionCallback)
{
(*currentHostConnectionCallback)(HOST_CONNECTION_CONNECTED);
}
}
}
return connected;
}
#endif /* ETHERNETCLIENTSTREAM_H */

View File

@@ -0,0 +1,3 @@
/*
* Implementation is in EthernetServerStream.h to avoid linker issues.
*/

View File

@@ -0,0 +1,147 @@
/*
EthernetServerStream.h
Copyright (C) 2017 Marc Josef Pees. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated July 10th, 2017
*/
#ifndef ETHERNETSERVERSTREAM_H
#define ETHERNETSERVERSTREAM_H
#include <inttypes.h>
#include <Stream.h>
#include <Ethernet.h>
//#define SERIAL_DEBUG
#include "firmataDebug.h"
class EthernetServerStream : public Stream
{
public:
EthernetServerStream(IPAddress localip, uint16_t port);
int available();
int read();
int peek();
void flush();
size_t write(uint8_t);
void maintain(IPAddress localip);
private:
EthernetClient client;
IPAddress localip;
uint16_t port;
bool connected;
bool maintain();
void stop();
protected:
EthernetServer server = EthernetServer(3030);
bool listening = false;
bool connect_client();
};
/*
* EthernetServerStream.cpp
* Copied here as a hack to linker issues with 3rd party board packages that don't properly
* implement the Arduino network APIs.
*/
EthernetServerStream::EthernetServerStream(IPAddress localip, uint16_t port)
: localip(localip),
port(port),
connected(false)
{
}
bool EthernetServerStream::connect_client()
{
if ( connected )
{
if ( client && client.connected() ) return true;
stop();
}
EthernetClient newClient = server.available();
if ( !newClient ) return false;
client = newClient;
connected = true;
DEBUG_PRINTLN("Connected");
return true;
}
int
EthernetServerStream::available()
{
return maintain() ? client.available() : 0;
}
int
EthernetServerStream::read()
{
return maintain() ? client.read() : -1;
}
int
EthernetServerStream::peek()
{
return maintain() ? client.peek() : -1;
}
void EthernetServerStream::flush()
{
if (maintain())
client.flush();
}
size_t
EthernetServerStream::write(uint8_t c)
{
return maintain() ? client.write(c) : 0;
}
void
EthernetServerStream::maintain(IPAddress localip)
{
// ensure the local IP is updated in the case that it is changed by the DHCP server
if (this->localip != localip) {
this->localip = localip;
if (connected)
stop();
}
}
void
EthernetServerStream::stop()
{
if(client)
{
client.stop();
}
connected = false;
}
bool
EthernetServerStream::maintain()
{
if (connect_client()) return true;
stop();
if(!listening)
{
server = EthernetServer(port);
server.begin();
listening = true;
}
return false;
}
#endif /* ETHERNETSERVERSTREAM_H */

View File

@@ -0,0 +1,38 @@
/*
FirmataFeature.h
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
Interface for Firmata feature classes.
This version of FirmataFeature.h differs from the ConfigurableFirmata
version in the following ways:
- Imports Firmata.h rather than ConfigurableFirmata.h
See file LICENSE.txt for further informations on licensing terms.
*/
#ifndef FirmataFeature_h
#define FirmataFeature_h
#include <Firmata.h>
class FirmataFeature
{
public:
virtual void handleCapability(byte pin) = 0;
virtual boolean handlePinMode(byte pin, int mode) = 0;
virtual boolean handleSysex(byte command, byte argc, byte* argv) = 0;
virtual void reset() = 0;
};
#endif

View File

@@ -0,0 +1,394 @@
/*
SerialFirmata.cpp
Copyright (C) 2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
This version of SerialFirmata.cpp differs from the ConfigurableFirmata
version in the following ways:
- handlePinMode calls Firmata::setPinMode
Last updated March 16th, 2020
*/
#include "SerialFirmata.h"
// The RX and TX hardware FIFOs of the ESP8266 hold 128 bytes that can be
// extended using interrupt handlers. The Arduino constants are not available
// for the ESP8266 platform.
#if !defined(SERIAL_RX_BUFFER_SIZE) && defined(UART_TX_FIFO_SIZE)
#define SERIAL_RX_BUFFER_SIZE UART_TX_FIFO_SIZE
#endif
SerialFirmata::SerialFirmata()
{
#if defined(SoftwareSerial_h)
swSerial0 = NULL;
swSerial1 = NULL;
swSerial2 = NULL;
swSerial3 = NULL;
#endif
serialIndex = -1;
#if defined(FIRMATA_SERIAL_RX_DELAY)
for (byte i = 0; i < SERIAL_READ_ARR_LEN; i++) {
maxRxDelay[i] = FIRMATA_SERIAL_RX_DELAY; // @todo provide setter
}
#endif
}
boolean SerialFirmata::handlePinMode(byte pin, int mode)
{
// used for both HW and SW serial
if (mode == PIN_MODE_SERIAL) {
Firmata.setPinMode(pin, PIN_MODE_SERIAL);
return true;
}
return false;
}
void SerialFirmata::handleCapability(byte pin)
{
if (IS_PIN_SERIAL(pin)) {
Firmata.write(PIN_MODE_SERIAL);
Firmata.write(getSerialPinType(pin));
}
}
boolean SerialFirmata::handleSysex(byte command, byte argc, byte *argv)
{
if (command == SERIAL_MESSAGE) {
Stream *serialPort;
byte mode = argv[0] & SERIAL_MODE_MASK;
byte portId = argv[0] & SERIAL_PORT_ID_MASK;
if (portId >= SERIAL_READ_ARR_LEN) return false;
switch (mode) {
case SERIAL_CONFIG:
{
long baud = (long)argv[1] | ((long)argv[2] << 7) | ((long)argv[3] << 14);
serial_pins pins;
#if defined(FIRMATA_SERIAL_RX_DELAY)
lastBytesAvailable[portId] = 0;
lastBytesReceived[portId] = 0;
#endif
if (portId < 8) {
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
pins = getSerialPinNumbers(portId);
if (pins.rx != 0 && pins.tx != 0) {
Firmata.setPinMode(pins.rx, PIN_MODE_SERIAL);
Firmata.setPinMode(pins.tx, PIN_MODE_SERIAL);
// Fixes an issue where some serial devices would not work properly with Arduino Due
// because all Arduino pins are set to OUTPUT by default in StandardFirmata.
pinMode(pins.rx, INPUT);
}
((HardwareSerial*)serialPort)->begin(baud);
}
} else {
#if defined(SoftwareSerial_h)
byte swTxPin, swRxPin;
if (argc > 4) {
swRxPin = argv[4];
swTxPin = argv[5];
} else {
// RX and TX pins must be specified when using SW serial
Firmata.sendString("Specify serial RX and TX pins");
return false;
}
switch (portId) {
case SW_SERIAL0:
if (swSerial0 == NULL) {
swSerial0 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
case SW_SERIAL1:
if (swSerial1 == NULL) {
swSerial1 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
case SW_SERIAL2:
if (swSerial2 == NULL) {
swSerial2 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
case SW_SERIAL3:
if (swSerial3 == NULL) {
swSerial3 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
}
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
Firmata.setPinMode(swRxPin, PIN_MODE_SERIAL);
Firmata.setPinMode(swTxPin, PIN_MODE_SERIAL);
((SoftwareSerial*)serialPort)->begin(baud);
}
#endif
}
break; // SERIAL_CONFIG
}
case SERIAL_WRITE:
{
byte data;
serialPort = getPortFromId(portId);
if (serialPort == NULL) {
break;
}
for (byte i = 1; i < argc; i += 2) {
data = argv[i] + (argv[i + 1] << 7);
serialPort->write(data);
}
break; // SERIAL_WRITE
}
case SERIAL_READ:
if (argv[1] == SERIAL_READ_CONTINUOUSLY) {
if (serialIndex + 1 >= MAX_SERIAL_PORTS) {
break;
}
if (argc > 2) {
// maximum number of bytes to read from buffer per iteration of loop()
serialBytesToRead[portId] = (int)argv[2] | ((int)argv[3] << 7);
} else {
// read all available bytes per iteration of loop()
serialBytesToRead[portId] = 0;
}
serialIndex++;
reportSerial[serialIndex] = portId;
} else if (argv[1] == SERIAL_STOP_READING) {
byte serialIndexToSkip = 0;
if (serialIndex <= 0) {
serialIndex = -1;
} else {
for (byte i = 0; i < serialIndex + 1; i++) {
if (reportSerial[i] == portId) {
serialIndexToSkip = i;
break;
}
}
// shift elements over to fill space left by removed element
for (byte i = serialIndexToSkip; i < serialIndex + 1; i++) {
if (i < MAX_SERIAL_PORTS) {
reportSerial[i] = reportSerial[i + 1];
}
}
serialIndex--;
}
}
break; // SERIAL_READ
case SERIAL_CLOSE:
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
if (portId < 8) {
((HardwareSerial*)serialPort)->end();
} else {
#if defined(SoftwareSerial_h)
((SoftwareSerial*)serialPort)->end();
if (serialPort != NULL) {
free(serialPort);
serialPort = NULL;
}
#endif
}
}
break; // SERIAL_CLOSE
case SERIAL_FLUSH:
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
getPortFromId(portId)->flush();
}
break; // SERIAL_FLUSH
#if defined(SoftwareSerial_h)
case SERIAL_LISTEN:
// can only call listen() on software serial ports
if (portId > 7) {
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
((SoftwareSerial*)serialPort)->listen();
}
}
break; // SERIAL_LISTEN
#endif
} // end switch
return true;
}
return false;
}
void SerialFirmata::update()
{
checkSerial();
}
void SerialFirmata::reset()
{
#if defined(SoftwareSerial_h)
Stream *serialPort;
// free memory allocated for SoftwareSerial ports
for (byte i = SW_SERIAL0; i < SW_SERIAL3 + 1; i++) {
serialPort = getPortFromId(i);
if (serialPort != NULL) {
free(serialPort);
serialPort = NULL;
}
}
#endif
serialIndex = -1;
for (byte i = 0; i < SERIAL_READ_ARR_LEN; i++) {
serialBytesToRead[i] = 0;
#if defined(FIRMATA_SERIAL_RX_DELAY)
lastBytesAvailable[i] = 0;
lastBytesReceived[i] = 0;
#endif
}
}
// get a pointer to the serial port associated with the specified port id
Stream* SerialFirmata::getPortFromId(byte portId)
{
switch (portId) {
case HW_SERIAL0:
// block use of Serial (typically pins 0 and 1) until ability to reclaim Serial is implemented
//return &Serial;
return NULL;
#if defined(PIN_SERIAL1_RX)
case HW_SERIAL1:
return &Serial1;
#endif
#if defined(PIN_SERIAL2_RX)
case HW_SERIAL2:
return &Serial2;
#endif
#if defined(PIN_SERIAL3_RX)
case HW_SERIAL3:
return &Serial3;
#endif
#if defined(PIN_SERIAL4_RX)
case HW_SERIAL4:
return &Serial4;
#endif
#if defined(PIN_SERIAL5_RX)
case HW_SERIAL5:
return &Serial5;
#endif
#if defined(PIN_SERIAL6_RX)
case HW_SERIAL6:
return &Serial6;
#endif
#if defined(SoftwareSerial_h)
case SW_SERIAL0:
if (swSerial0 != NULL) {
// instances of SoftwareSerial are already pointers so simply return the instance
return swSerial0;
}
break;
case SW_SERIAL1:
if (swSerial1 != NULL) {
return swSerial1;
}
break;
case SW_SERIAL2:
if (swSerial2 != NULL) {
return swSerial2;
}
break;
case SW_SERIAL3:
if (swSerial3 != NULL) {
return swSerial3;
}
break;
#endif
}
return NULL;
}
// Check serial ports that have READ_CONTINUOUS mode set and relay any data
// for each port to the device attached to that port.
void SerialFirmata::checkSerial()
{
byte portId, serialData;
int bytesToRead = 0;
int numBytesToRead = 0;
Stream* serialPort;
if (serialIndex > -1) {
#if defined(FIRMATA_SERIAL_RX_DELAY)
unsigned long currentMillis = millis();
#endif
// loop through all reporting (READ_CONTINUOUS) serial ports
for (byte i = 0; i < serialIndex + 1; i++) {
portId = reportSerial[i];
bytesToRead = serialBytesToRead[portId];
serialPort = getPortFromId(portId);
if (serialPort == NULL) {
continue;
}
#if defined(SoftwareSerial_h)
// only the SoftwareSerial port that is "listening" can read data
if (portId > 7 && !((SoftwareSerial*)serialPort)->isListening()) {
continue;
}
#endif
int bytesAvailable = serialPort->available();
if (bytesAvailable > 0) {
#if defined(FIRMATA_SERIAL_RX_DELAY)
if (bytesAvailable > lastBytesAvailable[portId]) {
lastBytesReceived[portId] = currentMillis;
}
lastBytesAvailable[portId] = bytesAvailable;
#endif
if (bytesToRead <= 0 || (bytesAvailable <= bytesToRead)) {
numBytesToRead = bytesAvailable;
} else {
numBytesToRead = bytesToRead;
}
#if defined(FIRMATA_SERIAL_RX_DELAY)
if (maxRxDelay[portId] >= 0 && numBytesToRead > 0) {
// read and send immediately only if
// - expected bytes are unknown and the receive buffer has reached 50 %
// - expected bytes are available
// - maxRxDelay has expired since last receive (or time counter wrap)
if (!((bytesToRead <= 0 && bytesAvailable >= SERIAL_RX_BUFFER_SIZE/2)
|| (bytesToRead > 0 && bytesAvailable >= bytesToRead)
|| (maxRxDelay[portId] > 0 && (currentMillis < lastBytesReceived[portId] || (currentMillis - lastBytesReceived[portId]) >= maxRxDelay[portId])))) {
// delay
numBytesToRead = 0;
}
}
#endif
// relay serial data to the serial device
if (numBytesToRead > 0) {
#if defined(FIRMATA_SERIAL_RX_DELAY)
lastBytesAvailable[portId] -= numBytesToRead;
#endif
Firmata.write(START_SYSEX);
Firmata.write(SERIAL_MESSAGE);
Firmata.write(SERIAL_REPLY | portId);
// relay serial data to the serial device
while (numBytesToRead > 0) {
serialData = serialPort->read();
Firmata.write(serialData & 0x7F);
Firmata.write((serialData >> 7) & 0x7F);
numBytesToRead--;
}
Firmata.write(END_SYSEX);
}
}
}
}
}

View File

@@ -0,0 +1,263 @@
/*
SerialFirmata.h
Copyright (C) 2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
This version of SerialFirmata.h differs from the ConfigurableFirmata
version in the following ways:
- Defines FIRMATA_SERIAL_FEATURE (could add to Configurable version as well)
- Imports Firmata.h rather than ConfigurableFirmata.h
Last updated March 11th, 2020
*/
#ifndef SerialFirmata_h
#define SerialFirmata_h
#include <Firmata.h>
#include "FirmataFeature.h"
// SoftwareSerial is currently only supported for AVR-based boards and the Arduino 101.
// Limited to Arduino 1.6.6 or higher because Arduino builder cannot find SoftwareSerial
// prior to this release.
#if (ARDUINO > 10605) && (defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ARC32) || defined(ESP8266))
#include <SoftwareSerial.h>
#endif
// If defined and set to a value between 0 and 255 milliseconds the received bytes
// will be not be read until until one of the following conditions are met:
// 1) the expected number of bytes have been received
// 2) the serial receive buffer is filled to 50 % (default size is 64 bytes)
// 3) the delay since the last received byte exceeds the configured FIRMATA_SERIAL_RX_DELAY
// hints: 5 bytes at 9600 baud take 5 ms, human perception of a delay starts at 50 ms
// This feature can significantly reduce the load on the transport layer when
// the byte receive rate is equal or lower than the average Firmata main loop execution
// duration by preventing single byte transmits if the underlying Firmata stream supports
// transmit buffering (currently only available with EthernetClientStream). The effect
// can be increased with higher values of FIRMATA_SERIAL_RX_DELAY.
// Notes
// 1) Enabling this feature will delay the received data and may concatenate
// bytes into one transmit that would otherwise be transmitted separately.
// 2) The usefulness and configuration of this feature depends on the baud rate and the serial message type:
// a) continuous streaming at higher baud rates: enable but set to 0 (receive buffer store & forward)
// b) messages: set to a value below min. inter message delay (message store & forward)
// c) continuous streaming at lower baud rates or random characters: undefine or set to -1 (disable)
// 3) Smaller delays may not have the desired effect, especially with less powerful CPUs,
// if set to a value near or below the average Firmata main loop duration.
// 4) The Firmata stream write buffer size must be equal or greater than the max.
// serial buffer/message size and the Firmata frame size (4 bytes) to prevent fragmentation
// on the transport layer.
//#define FIRMATA_SERIAL_RX_DELAY 50 // [ms]
#define FIRMATA_SERIAL_FEATURE
// Serial port Ids
#define HW_SERIAL0 0x00
#define HW_SERIAL1 0x01
#define HW_SERIAL2 0x02
#define HW_SERIAL3 0x03
#define HW_SERIAL4 0x04
#define HW_SERIAL5 0x05
#define HW_SERIAL6 0x06
#define HW_SERIAL7 0x07
#define SW_SERIAL0 0x08
#define SW_SERIAL1 0x09
#define SW_SERIAL2 0x0A
#define SW_SERIAL3 0x0B
// extensible up to 0x0F
#define SERIAL_PORT_ID_MASK 0x0F
#define MAX_SERIAL_PORTS 8
#define SERIAL_READ_ARR_LEN 12
// map configuration query response resolution value to serial pin type
#define RES_RX0 0x00
#define RES_TX0 0x01
#define RES_RX1 0x02
#define RES_TX1 0x03
#define RES_RX2 0x04
#define RES_TX2 0x05
#define RES_RX3 0x06
#define RES_TX3 0x07
#define RES_RX4 0x08
#define RES_TX4 0x09
#define RES_RX5 0x0a
#define RES_TX5 0x0b
#define RES_RX6 0x0c
#define RES_TX6 0x0d
#define RES_RX7 0x0e
#define RES_TX7 0x0f
// Serial command bytes
#define SERIAL_CONFIG 0x10
#define SERIAL_WRITE 0x20
#define SERIAL_READ 0x30
#define SERIAL_REPLY 0x40
#define SERIAL_CLOSE 0x50
#define SERIAL_FLUSH 0x60
#define SERIAL_LISTEN 0x70
// Serial read modes
#define SERIAL_READ_CONTINUOUSLY 0x00
#define SERIAL_STOP_READING 0x01
#define SERIAL_MODE_MASK 0xF0
namespace {
struct serial_pins {
uint8_t rx;
uint8_t tx;
};
/*
* Get the serial serial pin type (RX1, TX1, RX2, TX2, etc) for the specified pin.
*/
inline uint8_t getSerialPinType(uint8_t pin) {
#if defined(PIN_SERIAL_RX)
// TODO when use of HW_SERIAL0 is enabled
#endif
#if defined(PIN_SERIAL0_RX)
if (pin == PIN_SERIAL0_RX) return RES_RX0;
if (pin == PIN_SERIAL0_TX) return RES_TX0;
#endif
#if defined(PIN_SERIAL1_RX)
if (pin == PIN_SERIAL1_RX) return RES_RX1;
if (pin == PIN_SERIAL1_TX) return RES_TX1;
#endif
#if defined(PIN_SERIAL2_RX)
if (pin == PIN_SERIAL2_RX) return RES_RX2;
if (pin == PIN_SERIAL2_TX) return RES_TX2;
#endif
#if defined(PIN_SERIAL3_RX)
if (pin == PIN_SERIAL3_RX) return RES_RX3;
if (pin == PIN_SERIAL3_TX) return RES_TX3;
#endif
#if defined(PIN_SERIAL4_RX)
if (pin == PIN_SERIAL4_RX) return RES_RX4;
if (pin == PIN_SERIAL4_TX) return RES_TX4;
#endif
#if defined(PIN_SERIAL5_RX)
if (pin == PIN_SERIAL5_RX) return RES_RX5;
if (pin == PIN_SERIAL5_TX) return RES_TX5;
#endif
#if defined(PIN_SERIAL6_RX)
if (pin == PIN_SERIAL6_RX) return RES_RX6;
if (pin == PIN_SERIAL6_TX) return RES_TX6;
#endif
#if defined(PIN_SERIAL7_RX)
if (pin == PIN_SERIAL7_RX) return RES_RX7;
if (pin == PIN_SERIAL7_TX) return RES_TX7;
#endif
return 0;
}
/*
* Get the RX and TX pins numbers for the specified HW serial port.
*/
inline serial_pins getSerialPinNumbers(uint8_t portId) {
serial_pins pins;
switch (portId) {
#if defined(PIN_SERIAL_RX)
// case HW_SERIAL0:
// // TODO when use of HW_SERIAL0 is enabled
// break;
#endif
#if defined(PIN_SERIAL0_RX)
case HW_SERIAL0:
pins.rx = PIN_SERIAL0_RX;
pins.tx = PIN_SERIAL0_TX;
break;
#endif
#if defined(PIN_SERIAL1_RX)
case HW_SERIAL1:
pins.rx = PIN_SERIAL1_RX;
pins.tx = PIN_SERIAL1_TX;
break;
#endif
#if defined(PIN_SERIAL2_RX)
case HW_SERIAL2:
pins.rx = PIN_SERIAL2_RX;
pins.tx = PIN_SERIAL2_TX;
break;
#endif
#if defined(PIN_SERIAL3_RX)
case HW_SERIAL3:
pins.rx = PIN_SERIAL3_RX;
pins.tx = PIN_SERIAL3_TX;
break;
#endif
#if defined(PIN_SERIAL4_RX)
case HW_SERIAL4:
pins.rx = PIN_SERIAL4_RX;
pins.tx = PIN_SERIAL4_TX;
break;
#endif
#if defined(PIN_SERIAL5_RX)
case HW_SERIAL5:
pins.rx = PIN_SERIAL5_RX;
pins.tx = PIN_SERIAL5_TX;
break;
#endif
#if defined(PIN_SERIAL6_RX)
case HW_SERIAL6:
pins.rx = PIN_SERIAL6_RX;
pins.tx = PIN_SERIAL6_TX;
break;
#endif
#if defined(PIN_SERIAL7_RX)
case HW_SERIAL7:
pins.rx = PIN_SERIAL7_RX;
pins.tx = PIN_SERIAL7_TX;
break;
#endif
default:
pins.rx = 0;
pins.tx = 0;
}
return pins;
}
} // end namespace
class SerialFirmata: public FirmataFeature
{
public:
SerialFirmata();
boolean handlePinMode(byte pin, int mode);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte* argv);
void update();
void reset();
void checkSerial();
private:
byte reportSerial[MAX_SERIAL_PORTS];
int serialBytesToRead[SERIAL_READ_ARR_LEN];
signed char serialIndex;
#if defined(FIRMATA_SERIAL_RX_DELAY)
byte maxRxDelay[SERIAL_READ_ARR_LEN];
int lastBytesAvailable[SERIAL_READ_ARR_LEN];
unsigned long lastBytesReceived[SERIAL_READ_ARR_LEN];
#endif
#if defined(SoftwareSerial_h)
Stream *swSerial0;
Stream *swSerial1;
Stream *swSerial2;
Stream *swSerial3;
#endif
Stream* getPortFromId(byte portId);
};
#endif /* SerialFirmata_h */

View File

@@ -0,0 +1,105 @@
/*
WiFiClientStream.h
An Arduino Stream that wraps an instance of a WiFiClient. For use
with legacy Arduino WiFi shield and other boards and shields that
are compatible with the Arduino WiFi library.
Copyright (C) 2016 Jens B. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Parts of this class are based on
- EthernetClientStream - Copyright (C) 2013 Norbert Truchsess. All rights reserved.
published under the same license.
Last updated April 23rd, 2016
*/
#ifndef WIFI_CLIENT_STREAM_H
#define WIFI_CLIENT_STREAM_H
#include "WiFiStream.h"
#define MILLIS_RECONNECT 5000
class WiFiClientStream : public WiFiStream
{
protected:
uint32_t _time_connect = 0;
/**
* check if TCP client is connected
* @return true if connected
*/
virtual inline bool connect_client()
{
if ( _connected )
{
if ( _client && _client.connected() ) return true;
stop();
}
// active TCP connect
if ( WiFi.status() == WL_CONNECTED )
{
// if the client is disconnected, try to reconnect every 5 seconds
if ( millis() - _time_connect >= MILLIS_RECONNECT )
{
_connected = _client.connect( _remote_ip, _port );
if ( !_connected )
{
_time_connect = millis();
}
else if ( _currentHostConnectionCallback )
{
(*_currentHostConnectionCallback)(HOST_CONNECTION_CONNECTED);
}
}
}
return _connected;
}
public:
/**
* create a WiFi stream with a TCP client
*/
WiFiClientStream(IPAddress server_ip, uint16_t server_port) : WiFiStream(server_ip, server_port) {}
/**
* maintain WiFi and TCP connection
* @return true if WiFi and TCP connection are established
*/
virtual inline bool maintain()
{
return connect_client();
}
/**
* stop client connection
*/
virtual inline void stop()
{
if ( _client)
{
_client.stop();
if ( _currentHostConnectionCallback )
{
(*_currentHostConnectionCallback)(HOST_CONNECTION_DISCONNECTED);
}
}
_connected = false;
_time_connect = millis();
}
};
#endif //WIFI_CLIENT_STREAM_H

View File

@@ -0,0 +1,107 @@
/*
WiFiServerStream.h
An Arduino Stream extension for a WiFiClient or WiFiServer to be used
with legacy Arduino WiFi shield and other boards and shields that
are compatible with the Arduino WiFi library.
Copyright (C) 2016 Jens B. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Parts of this class are based on
- WiFiStream - Copyright (C) 2015-2016 Jesse Frush. All rights reserved.
published under the same license.
Last updated April 23rd, 2016
*/
#ifndef WIFI_SERVER_STREAM_H
#define WIFI_SERVER_STREAM_H
#include "WiFiStream.h"
class WiFiServerStream : public WiFiStream
{
protected:
WiFiServer _server = WiFiServer(3030);
bool _listening = false;
/**
* check if TCP client is connected
* @return true if connected
*/
virtual inline bool connect_client()
{
if ( _connected )
{
if ( _client && _client.connected() ) return true;
stop();
}
// passive TCP connect (accept)
WiFiClient newClient = _server.available();
if ( !newClient ) return false;
_client = newClient;
_connected = true;
if ( _currentHostConnectionCallback )
{
(*_currentHostConnectionCallback)(HOST_CONNECTION_CONNECTED);
}
return true;
}
public:
/**
* create a WiFi stream with a TCP server
*/
WiFiServerStream(uint16_t server_port) : WiFiStream(server_port) {}
/**
* maintain WiFi and TCP connection
* @return true if WiFi and TCP connection are established
*/
virtual inline bool maintain()
{
if ( connect_client() ) return true;
stop();
if ( !_listening && WiFi.status() == WL_CONNECTED )
{
// start TCP server after first WiFi connect
_server = WiFiServer(_port);
_server.begin();
_listening = true;
}
return false;
}
/**
* stop client connection
*/
virtual inline void stop()
{
if ( _client)
{
_client.stop();
if ( _currentHostConnectionCallback )
{
(*_currentHostConnectionCallback)(HOST_CONNECTION_DISCONNECTED);
}
}
_connected = false;
}
};
#endif //WIFI_SERVER_STREAM_H

View File

@@ -0,0 +1,4 @@
/*
* Implementation is in WiFiStream.h to avoid linker issues. Legacy WiFi and modern WiFi101 both define WiFiClass which
* will cause linker errors whenever Firmata.h is included.
*/

View File

@@ -0,0 +1,226 @@
/*
WiFiStream.h
An Arduino Stream extension for a WiFiClient or WiFiServer to be used
with legacy Arduino WiFi shield and other boards and shields that
are compatible with the Arduino WiFi library.
Copyright (C) 2015-2016 Jesse Frush. All rights reserved.
Copyright (C) 2016 Jens B. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated April 23rd, 2016
*/
#ifndef WIFI_STREAM_H
#define WIFI_STREAM_H
#include <inttypes.h>
#include <Stream.h>
#define HOST_CONNECTION_DISCONNECTED 0
#define HOST_CONNECTION_CONNECTED 1
extern "C" {
// callback function types
typedef void (*hostConnectionCallbackFunction)(byte);
}
class WiFiStream : public Stream
{
protected:
WiFiClient _client;
bool _connected = false;
hostConnectionCallbackFunction _currentHostConnectionCallback;
//configuration members
IPAddress _local_ip; // DHCP
IPAddress _subnet;
IPAddress _gateway;
IPAddress _remote_ip;
uint16_t _port;
uint8_t _key_idx; //WEP
const char *_key = nullptr; //WEP
const char *_passphrase = nullptr; //WPA
char *_ssid = nullptr;
/**
* check if TCP client is connected
* @return true if connected
*/
virtual bool connect_client() = 0;
public:
/** constructor for TCP server */
WiFiStream(uint16_t server_port) : _port(server_port) {}
/** constructor for TCP client */
WiFiStream(IPAddress server_ip, uint16_t server_port) : _remote_ip(server_ip), _port(server_port) {}
inline void attach( hostConnectionCallbackFunction newFunction ) { _currentHostConnectionCallback = newFunction; }
/******************************************************************************
* network configuration
******************************************************************************/
#ifndef ESP8266
/**
* configure a static local IP address without defining the local network
* DHCP will be used as long as local IP address is not defined
*/
inline void config(IPAddress local_ip)
{
_local_ip = local_ip;
WiFi.config( local_ip );
}
#endif
/**
* configure a static local IP address
* DHCP will be used as long as local IP address is not defined
*/
inline void config(IPAddress local_ip, IPAddress gateway, IPAddress subnet)
{
_local_ip = local_ip;
_subnet = subnet;
_gateway = gateway;
#ifndef ESP8266
WiFi.config( local_ip, IPAddress(0, 0, 0, 0), gateway, subnet );
#else
WiFi.config( local_ip, gateway, subnet );
#endif
}
/**
* @return local IP address
*/
inline IPAddress getLocalIP()
{
return WiFi.localIP();
}
/******************************************************************************
* network functions
******************************************************************************/
/**
* maintain WiFi and TCP connection
* @return true if WiFi and TCP connection are established
*/
virtual bool maintain() = 0;
#ifdef ESP8266
/**
* get status of TCP connection
* @return status of TCP connection
* CLOSED = 0 (typical)
* LISTEN = 1 (not used)
* SYN_SENT = 2
* SYN_RCVD = 3
* ESTABLISHED = 4 (typical)
* FIN_WAIT_1 = 5
* FIN_WAIT_2 = 6
* CLOSE_WAIT = 7
* CLOSING = 8
* LAST_ACK = 9
* TIME_WAIT = 10
*/
inline uint8_t status()
{
return _client.status();
}
#endif
/**
* close TCP client connection
*/
virtual void stop() = 0;
/******************************************************************************
* WiFi configuration
******************************************************************************/
/**
* initialize WiFi without security (open) and initiate client connection
* if WiFi connection is already established
* @return WL_CONNECTED if WiFi connection is established
*/
inline int begin(char *ssid)
{
_ssid = ssid;
WiFi.begin(ssid);
int result = WiFi.status();
return WiFi.status();
}
#ifndef ESP8266
/**
* initialize WiFi with WEP security and initiate client connection
* if WiFi connection is already established
* @return WL_CONNECTED if WiFi connection is established
*/
inline int begin(char *ssid, uint8_t key_idx, const char *key)
{
_ssid = ssid;
_key_idx = key_idx;
_key = key;
WiFi.begin( ssid, key_idx, key );
return WiFi.status();
}
#endif
/**
* initialize WiFi with WPA-PSK security and initiate client connection
* if WiFi connection is already established
* @return WL_CONNECTED if WiFi connection is established
*/
inline int begin(char *ssid, const char *passphrase)
{
_ssid = ssid;
_passphrase = passphrase;
WiFi.begin(ssid, passphrase);
return WiFi.status();
}
/******************************************************************************
* stream functions
******************************************************************************/
inline int available()
{
return connect_client() ? _client.available() : 0;
}
inline void flush()
{
if( _client ) _client.flush();
}
inline int peek()
{
return connect_client() ? _client.peek(): 0;
}
inline int read()
{
return connect_client() ? _client.read() : -1;
}
inline size_t write(uint8_t byte)
{
return connect_client() ? _client.write( byte ) : 0;
}
};
#endif //WIFI_STREAM_H

View File

@@ -0,0 +1,14 @@
#ifndef FIRMATA_DEBUG_H
#define FIRMATA_DEBUG_H
#ifdef SERIAL_DEBUG
#define DEBUG_BEGIN(baud) Serial.begin(baud); while(!Serial) {;}
#define DEBUG_PRINTLN(x) Serial.println (x); Serial.flush()
#define DEBUG_PRINT(x) Serial.print (x)
#else
#define DEBUG_BEGIN(baud)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINT(x)
#endif
#endif /* FIRMATA_DEBUG_H */