mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-07-28 18:03:45 +02:00
Compare commits
22 Commits
v5.5.37-De
...
NetworkInt
Author | SHA1 | Date | |
---|---|---|---|
|
8fa6ded079 | ||
|
dda7bc7277 | ||
|
edc38169dc | ||
|
34fe1256e7 | ||
|
6d7d01b1b5 | ||
|
0eb902f169 | ||
|
b41fb4b46b | ||
|
fe274cd059 | ||
|
003cb95591 | ||
|
2e09c9965d | ||
|
0144ff7ee4 | ||
|
1b624cdfdb | ||
|
09ae1e7ed1 | ||
|
a355cc74a0 | ||
|
77f0b99f8d | ||
|
5888e21090 | ||
|
1948773684 | ||
|
9b5e6c447f | ||
|
c1e4727ee8 | ||
|
d00864cbf4 | ||
|
c9c5f6a67b | ||
|
740ba6859a |
121
ArduinoUniqueID.cpp
Normal file
121
ArduinoUniqueID.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues, Luiz Henrique Cassettari. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#include "ArduinoUniqueID.h"
|
||||
|
||||
ArduinoUniqueID::ArduinoUniqueID()
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
for (size_t i = 0; i < UniqueIDsize; i++)
|
||||
{
|
||||
id[i] = boot_signature_byte_get(0x0E + i + (UniqueIDsize == 9 && i > 5 ? 1 : 0));
|
||||
}
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
uint32_t chipid = ESP.getChipId();
|
||||
id[0] = 0;
|
||||
id[1] = 0;
|
||||
id[2] = 0;
|
||||
id[3] = 0;
|
||||
id[4] = chipid >> 24;
|
||||
id[5] = chipid >> 16;
|
||||
id[6] = chipid >> 8;
|
||||
id[7] = chipid;
|
||||
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
uint64_t chipid = ESP.getEfuseMac();
|
||||
id[0] = 0;
|
||||
id[1] = 0;
|
||||
id[2] = chipid;
|
||||
id[3] = chipid >> 8;
|
||||
id[4] = chipid >> 16;
|
||||
id[5] = chipid >> 24;
|
||||
id[6] = chipid >> 32;
|
||||
id[7] = chipid >> 40;
|
||||
|
||||
#elif defined(ARDUINO_ARCH_SAM)
|
||||
unsigned int status ;
|
||||
/* Send the Start Read unique Identifier command (STUI) by writing the Flash Command Register with the STUI command.*/
|
||||
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_STUI;
|
||||
do
|
||||
{
|
||||
status = EFC1->EEFC_FSR ;
|
||||
} while ( (status & EEFC_FSR_FRDY) == EEFC_FSR_FRDY ) ;
|
||||
|
||||
/* The Unique Identifier is located in the first 128 bits of the Flash memory mapping. So, at the address 0x400000-0x400003. */
|
||||
uint32_t pdwUniqueID[4];
|
||||
pdwUniqueID[0] = *(uint32_t *)IFLASH1_ADDR;
|
||||
pdwUniqueID[1] = *(uint32_t *)(IFLASH1_ADDR + 4);
|
||||
pdwUniqueID[2] = *(uint32_t *)(IFLASH1_ADDR + 8);
|
||||
pdwUniqueID[3] = *(uint32_t *)(IFLASH1_ADDR + 12);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
id[i*4+0] = (uint8_t)(pdwUniqueID[i] >> 24);
|
||||
id[i*4+1] = (uint8_t)(pdwUniqueID[i] >> 16);
|
||||
id[i*4+2] = (uint8_t)(pdwUniqueID[i] >> 8);
|
||||
id[i*4+3] = (uint8_t)(pdwUniqueID[i] >> 0);
|
||||
}
|
||||
|
||||
/* To stop the Unique Identifier mode, the user needs to send the Stop Read unique Identifier
|
||||
command (SPUI) by writing the Flash Command Register with the SPUI command. */
|
||||
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_SPUI ;
|
||||
|
||||
/* When the Stop read Unique Unique Identifier command (SPUI) has been performed, the
|
||||
FRDY bit in the Flash Programming Status Register (EEFC_FSR) rises. */
|
||||
do
|
||||
{
|
||||
status = EFC1->EEFC_FSR ;
|
||||
} while ( (status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY );
|
||||
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
|
||||
// from section 9.3.3 of the datasheet
|
||||
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C)
|
||||
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040)
|
||||
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044)
|
||||
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048)
|
||||
|
||||
uint32_t pdwUniqueID[4];
|
||||
pdwUniqueID[0] = SERIAL_NUMBER_WORD_0;
|
||||
pdwUniqueID[1] = SERIAL_NUMBER_WORD_1;
|
||||
pdwUniqueID[2] = SERIAL_NUMBER_WORD_2;
|
||||
pdwUniqueID[3] = SERIAL_NUMBER_WORD_3;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
id[i*4+0] = (uint8_t)(pdwUniqueID[i] >> 24);
|
||||
id[i*4+1] = (uint8_t)(pdwUniqueID[i] >> 16);
|
||||
id[i*4+2] = (uint8_t)(pdwUniqueID[i] >> 8);
|
||||
id[i*4+3] = (uint8_t)(pdwUniqueID[i] >> 0);
|
||||
}
|
||||
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
uint32_t pdwUniqueID[3];
|
||||
pdwUniqueID[0] = HAL_GetUIDw0();
|
||||
pdwUniqueID[1] = HAL_GetUIDw1();
|
||||
pdwUniqueID[2] = HAL_GetUIDw2();
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
id[i*4+0] = (uint8_t)(pdwUniqueID[i] >> 24);
|
||||
id[i*4+1] = (uint8_t)(pdwUniqueID[i] >> 16);
|
||||
id[i*4+2] = (uint8_t)(pdwUniqueID[i] >> 8);
|
||||
id[i*4+3] = (uint8_t)(pdwUniqueID[i] >> 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ArduinoUniqueID _UniqueID;
|
104
ArduinoUniqueID.h
Normal file
104
ArduinoUniqueID.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues, Luiz Henrique Cassettari. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _ARDUINO_UNIQUE_ID_H_
|
||||
#define _ARDUINO_UNIQUE_ID_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
#include <avr/boot.h>
|
||||
#ifndef SIGRD
|
||||
#define SIGRD 5
|
||||
#endif
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
#elif defined(ARDUINO_ARCH_SAM)
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
#else
|
||||
#error "ArduinoUniqueID only works on AVR, SAM, SAMD, STM32 and ESP Architecture"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
|
||||
#if defined(__AVR_ATmega328PB__)
|
||||
#define UniqueIDsize 10
|
||||
#else
|
||||
#define UniqueIDsize 9
|
||||
#endif
|
||||
|
||||
#define UniqueIDbuffer UniqueIDsize
|
||||
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
#define UniqueIDsize 4
|
||||
#define UniqueIDbuffer 8
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
#define UniqueIDsize 6
|
||||
#define UniqueIDbuffer 8
|
||||
#elif defined(ARDUINO_ARCH_SAM)
|
||||
#define UniqueIDsize 16
|
||||
#define UniqueIDbuffer 16
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#define UniqueIDsize 16
|
||||
#define UniqueIDbuffer 16
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
#define UniqueIDsize 12
|
||||
#define UniqueIDbuffer 12
|
||||
#endif
|
||||
|
||||
#define UniqueID8 (_UniqueID.id + UniqueIDbuffer - 8)
|
||||
#define UniqueID (_UniqueID.id + UniqueIDbuffer - UniqueIDsize)
|
||||
|
||||
#define UniqueIDdump(stream) \
|
||||
{ \
|
||||
stream.print("UniqueID: "); \
|
||||
for (size_t i = 0; i < UniqueIDsize; i++) \
|
||||
{ \
|
||||
if (UniqueID[i] < 0x10) \
|
||||
stream.print("0"); \
|
||||
stream.print(UniqueID[i], HEX); \
|
||||
stream.print(" "); \
|
||||
} \
|
||||
stream.println(); \
|
||||
}
|
||||
|
||||
#define UniqueID8dump(stream) \
|
||||
{ \
|
||||
stream.print("UniqueID: "); \
|
||||
for (size_t i = 0; i < 8; i++) \
|
||||
{ \
|
||||
if (UniqueID8[i] < 0x10) \
|
||||
stream.print("0"); \
|
||||
stream.print(UniqueID8[i], HEX); \
|
||||
stream.print(" "); \
|
||||
} \
|
||||
stream.println(); \
|
||||
}
|
||||
|
||||
class ArduinoUniqueID
|
||||
{
|
||||
public:
|
||||
ArduinoUniqueID();
|
||||
uint8_t id[UniqueIDbuffer];
|
||||
};
|
||||
|
||||
extern ArduinoUniqueID _UniqueID;
|
||||
|
||||
#endif
|
@@ -17,6 +17,21 @@
|
||||
// to be issued from the USB serial console.
|
||||
DCCEXParser serialParser;
|
||||
|
||||
// (0) Declare NetworkInterfaces
|
||||
NetworkInterface nwi1;
|
||||
NetworkInterface nwi2;
|
||||
// (0) Declared NetworkInterfaces
|
||||
|
||||
// (1) Start NetworkInterface - HTTP callback
|
||||
void httpRequestHandler(ParsedRequest *req, Client* client) {
|
||||
DIAG(F("\nParsed Request:"));
|
||||
DIAG(F("\nMethod: [%s]"), req->method);
|
||||
DIAG(F("\nURI: [%s]"), req->uri);
|
||||
DIAG(F("\nHTTP version: [%s]"), req->version);
|
||||
DIAG(F("\nParameter count:[%d]\n"), *req->paramCount);
|
||||
}
|
||||
// (1) End NetworkInterface - HTTP callback
|
||||
|
||||
void setup()
|
||||
{
|
||||
// The main sketch has responsibilities during setup()
|
||||
@@ -49,6 +64,36 @@ void setup()
|
||||
// waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2
|
||||
|
||||
DCC::begin(MOTOR_SHIELD_TYPE);
|
||||
|
||||
// (2) Start NetworkInterface - The original WifiInterface is still there but disabled
|
||||
|
||||
DIAG(F("\nFree RAM before network init: [%d]\n"),freeMemory());
|
||||
DIAG(F("\nNetwork Setup In Progress ...\n\n"));
|
||||
|
||||
// WIFI, TCP on Port 2560, Wifi (ssid/password) has been configured permanetly already on the esp. If
|
||||
// the connection fails will go into AP mode
|
||||
// wifi.setup(WIFI);
|
||||
|
||||
// New connection on known ssid / password combo / port can be added as a last parameter other wise the default of 2560
|
||||
// will be used. If it passes the connection will be stored as permanent default. If fails will go into AP mode.
|
||||
// wifi.init(WIFI, TCP, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME));
|
||||
// wifi.init(WIFI, TCP, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME, 2323)
|
||||
// wifi.init
|
||||
|
||||
|
||||
// nwi1.setup(ETHERNET, UDPR); // ETHERNET/UDP on Port 2560
|
||||
// nwi2.setup(ETHERNET, UDPR, 8888); // ETHERNET/UDP on Port 8888
|
||||
// nwi1.setup(ETHERNET, TCP); // ETHERNET/TCP on Port 2560
|
||||
nwi2.setup(ETHERNET, TCP, 23); // ETHERNET/TCP on Port 23 for the CLI
|
||||
// nwi1.setup(ETHERNET, TCP, 8888); // ETHERNET/TCP on Port 8888
|
||||
nwi2.setup(WIFI, TCP); // WIFI/TCP on Port 2560
|
||||
// nwi1.setHttpCallback(httpRequestHandler); // HTTP callback
|
||||
|
||||
DIAG(F("\nNetwork Setup done ...\n"));
|
||||
DIAG(F("\nFree RAM after network init: [%d]\n"),freeMemory());
|
||||
|
||||
// (2) End starting NetworkInterface
|
||||
|
||||
LCD(1,F("Ready"));
|
||||
}
|
||||
|
||||
@@ -68,6 +113,11 @@ void loop()
|
||||
WifiInterface::loop();
|
||||
#endif
|
||||
|
||||
|
||||
// (3) Start Loop NetworkInterface
|
||||
NetworkInterface::loop();
|
||||
// (3) End Loop NetworkInterface
|
||||
|
||||
LCDDisplay::loop(); // ignored if LCD not in use
|
||||
|
||||
// Optionally report any decrease in memory (will automatically trigger on first call)
|
||||
|
2
DCCEX.h
2
DCCEX.h
@@ -10,7 +10,7 @@
|
||||
#include "DCCEXParser.h"
|
||||
#include "version.h"
|
||||
#include "WifiInterface.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include "NetworkInterface.h"
|
||||
#include "LCD_Implementation.h"
|
||||
#include "freeMemory.h"
|
||||
#include <Arduino.h>
|
||||
|
@@ -28,6 +28,8 @@
|
||||
#include "GITHUB_SHA.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "NetworkDiag.h"
|
||||
|
||||
#include "EEStore.h"
|
||||
#include "DIAG.h"
|
||||
|
||||
@@ -48,6 +50,7 @@ const int HASH_KEYWORD_SLOW = -17209;
|
||||
const int HASH_KEYWORD_PROGBOOST = -6353;
|
||||
const int HASH_KEYWORD_EEPROM = -7168;
|
||||
const int HASH_KEYWORD_LIMIT = 27413;
|
||||
const int HASH_KEYWORD_NET = 21503;
|
||||
|
||||
int DCCEXParser::stashP[MAX_PARAMS];
|
||||
bool DCCEXParser::stashBusy;
|
||||
@@ -619,6 +622,10 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
|
||||
EEStore::dump(p[1]);
|
||||
return true;
|
||||
|
||||
case HASH_KEYWORD_NET:
|
||||
_nLogLevel = p[1];
|
||||
return true;
|
||||
|
||||
default: // invalid/unknown
|
||||
break;
|
||||
}
|
||||
|
1
DIAG.h
1
DIAG.h
@@ -19,6 +19,7 @@
|
||||
#ifndef DIAG_h
|
||||
#define DIAG_h
|
||||
#include "StringFormatter.h"
|
||||
|
||||
#define DIAG StringFormatter::diag
|
||||
#define LCD StringFormatter::lcd
|
||||
#endif
|
||||
|
@@ -1,304 +0,0 @@
|
||||
/*
|
||||
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
||||
*
|
||||
* This file is part of DCC-EX/CommandStation-EX
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Ethernet Interface added by Gregor Baues
|
||||
*/
|
||||
|
||||
#include "EthernetInterface.h"
|
||||
#include "DIAG.h"
|
||||
#include "StringFormatter.h"
|
||||
|
||||
//#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <EthernetUdp.h>
|
||||
|
||||
|
||||
// Support Functions
|
||||
/**
|
||||
* @brief Aquire IP Address from DHCP; if that fails try a statically configured address
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool EthernetInterface::setupConnection()
|
||||
{
|
||||
|
||||
singleton=this;
|
||||
|
||||
DIAG(F("\nInitialize Ethernet with DHCP:"));
|
||||
server = EthernetServer(LISTEN_PORT); // Ethernet Server listening on default port LISTEN_PORT
|
||||
ip = IPAddress(IP_ADDRESS); // init with fixed IP address needed to get to the server
|
||||
connected = false; // Connection status
|
||||
streamer= new MemStream(buffer, MAX_ETH_BUFFER, MAX_ETH_BUFFER, true); // streamer who writes the results to the buffer
|
||||
|
||||
if (Ethernet.begin(EthernetInterface::mac) == 0)
|
||||
{
|
||||
DIAG(F("\nFailed to configure Ethernet using DHCP ... Trying with fixed IP"));
|
||||
Ethernet.begin(EthernetInterface::mac, EthernetInterface::ip); // default ip address
|
||||
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware)
|
||||
{
|
||||
DIAG(F("\nEthernet shield was not found. Sorry, can't run without hardware. :("));
|
||||
return false;
|
||||
};
|
||||
if (Ethernet.linkStatus() == LinkOFF)
|
||||
{
|
||||
DIAG(F("\nEthernet cable is not connected."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ip = Ethernet.localIP(); // reassign the obtained ip address
|
||||
|
||||
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
||||
DIAG(F("\nListening on port: [%d]"), port);
|
||||
dnsip = Ethernet.dnsServerIP();
|
||||
DIAG(F("\nDNS server IP address: [%d.%d.%d.%d] "), ip[0], ip[1], ip[2], ip[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles command requests recieved via UDP. UDP is a connection less, unreliable protocol as it doesn't maintain state but fast.
|
||||
*
|
||||
*/
|
||||
void EthernetInterface::udpHandler() {
|
||||
singleton->udpHandler2();
|
||||
}
|
||||
void EthernetInterface::udpHandler2()
|
||||
{
|
||||
|
||||
int packetSize = Udp.parsePacket();
|
||||
if (packetSize)
|
||||
{
|
||||
DIAG(F("\nReceived packet of size:[%d]\n"), packetSize);
|
||||
IPAddress remote = Udp.remoteIP();
|
||||
DIAG(F("From: [%d.%d.%d.%d:"), remote[0], remote[1], remote[2], remote[3]);
|
||||
char portBuffer[6];
|
||||
DIAG(F("%s]\n"), utoa(Udp.remotePort(), portBuffer, 10)); // DIAG has issues with unsigend int's so go through utoa
|
||||
|
||||
// read the packet into packetBufffer
|
||||
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
|
||||
|
||||
DIAG(F("Command: [%s]\n"), packetBuffer);
|
||||
|
||||
streamer->flush();
|
||||
|
||||
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
|
||||
|
||||
ethParser.parse(streamer, (byte *)packetBuffer, true); // set to true so it is sync cf. WifiInterface
|
||||
|
||||
if (streamer->available() == 0)
|
||||
{
|
||||
DIAG(F("\nNo response\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// send the reply
|
||||
DIAG(F("Response: %s\n"), (char *)buffer);
|
||||
Udp.write((char *)buffer);
|
||||
Udp.endPacket();
|
||||
}
|
||||
|
||||
memset(packetBuffer, 0, UDP_TX_PACKET_MAX_SIZE); // reset PacktBuffer
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles command requests recieved via TCP. Supports up to the max# of simultaneous requests which is 8. The connection gets closed as soon as we finished processing
|
||||
*
|
||||
*/
|
||||
void EthernetInterface::tcpHandler()
|
||||
{
|
||||
singleton->tcpHandler2();
|
||||
}
|
||||
void EthernetInterface::tcpHandler2()
|
||||
{
|
||||
// get client from the server
|
||||
EthernetClient client = getServer().accept();
|
||||
|
||||
// check for new client
|
||||
if (client)
|
||||
{
|
||||
for (byte i = 0; i < MAX_SOCK_NUM; i++)
|
||||
{
|
||||
if (!clients[i])
|
||||
{
|
||||
// On accept() the EthernetServer doesn't track the client anymore
|
||||
// so we store it in our client array
|
||||
clients[i] = client;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for incoming data from all possible clients
|
||||
for (byte i = 0; i < MAX_SOCK_NUM; i++)
|
||||
{
|
||||
if (clients[i] && clients[i].available() > 0)
|
||||
{
|
||||
// read bytes from a client
|
||||
int count = clients[i].read(buffer, MAX_ETH_BUFFER);
|
||||
buffer[count] = '\0'; // terminate the string properly
|
||||
DIAG(F("\nReceived packet of size:[%d]\n"), count);
|
||||
DIAG(F("From Client #: [%d]\n"), i);
|
||||
DIAG(F("Command: [%s]\n"), buffer);
|
||||
|
||||
// as we use buffer for recv and send we have to reset the write position
|
||||
streamer->setBufferContentPosition(0, 0);
|
||||
|
||||
ethParser.parse(streamer, buffer, true); // set to true to that the execution in DCC is sync
|
||||
|
||||
if (streamer->available() == 0)
|
||||
{
|
||||
DIAG(F("No response\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[streamer->available()] = '\0'; // mark end of buffer, so it can be used as a string later
|
||||
DIAG(F("Response: %s\n"), (char *)buffer);
|
||||
if (clients[i].connected())
|
||||
{
|
||||
clients[i].write(buffer, streamer->available());
|
||||
}
|
||||
}
|
||||
}
|
||||
// stop any clients which disconnect
|
||||
for (byte i = 0; i < MAX_SOCK_NUM; i++)
|
||||
{
|
||||
if (clients[i] && !clients[i].connected())
|
||||
{
|
||||
DIAG(F("Disconnect client #%d \n"), i);
|
||||
clients[i].stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Class Functions
|
||||
/**
|
||||
* @brief Setup Ethernet Connection
|
||||
*
|
||||
* @param pt Protocol used
|
||||
* @param localPort Port number for the connection
|
||||
*/
|
||||
void EthernetInterface::setup(protocolType pt, uint16_t localPort)
|
||||
{
|
||||
DIAG(F("\n++++++ Ethernet Setup In Progress ++++++++\n"));
|
||||
port = localPort;
|
||||
if (setupConnection())
|
||||
{
|
||||
DIAG(F("\nProtocol: [%s]\n"), pt ? "UDP" : "TCP");
|
||||
switch (pt)
|
||||
{
|
||||
case UDP:
|
||||
{
|
||||
if (Udp.begin(localPort))
|
||||
{
|
||||
connected = true;
|
||||
protocolHandler = udpHandler;
|
||||
}
|
||||
else
|
||||
{
|
||||
DIAG(F("\nUDP client failed to start"));
|
||||
connected = false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case TCP:
|
||||
{
|
||||
Ethernet.begin(mac, ip);
|
||||
EthernetServer server(localPort);
|
||||
setServer(server);
|
||||
server.begin();
|
||||
connected = true;
|
||||
protocolHandler = tcpHandler;
|
||||
break;
|
||||
};
|
||||
default:
|
||||
{
|
||||
DIAG(F("Unkown Ethernet protocol; Setup failed"));
|
||||
connected = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
connected = false;
|
||||
};
|
||||
DIAG(F("\n++++++ Ethernet Setup %S ++++++++\n"), connected ? F("OK") : F("FAILED"));
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Setup Ethernet on default port and user choosen protocol
|
||||
*
|
||||
* @param pt Protocol UDP or TCP
|
||||
*/
|
||||
void EthernetInterface::setup(protocolType pt)
|
||||
{
|
||||
setup(pt, LISTEN_PORT);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Ethernet setup with defaults TCP / Listen Port
|
||||
*
|
||||
*/
|
||||
void EthernetInterface::setup()
|
||||
{
|
||||
setup(TCP, LISTEN_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main loop for the EthernetInterface
|
||||
*
|
||||
*/
|
||||
void EthernetInterface::loop()
|
||||
{
|
||||
switch (Ethernet.maintain())
|
||||
{
|
||||
case 1:
|
||||
//renewed fail
|
||||
DIAG(F("\nError: renewed fail"));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
//renewed success
|
||||
DIAG(F("\nRenewed success: "));
|
||||
ip = Ethernet.localIP(); // reassign the obtained ip address
|
||||
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"),ip[0], ip[1], ip[2], ip[3]);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
//rebind fail
|
||||
DIAG(F("Error: rebind fail"));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
//rebind success
|
||||
DIAG(F("Rebind success"));
|
||||
ip = Ethernet.localIP(); // reassign the obtained ip address
|
||||
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
||||
break;
|
||||
|
||||
default:
|
||||
//nothing happened
|
||||
break;
|
||||
}
|
||||
protocolHandler();
|
||||
}
|
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
||||
*
|
||||
* This file is part of DCC-EX/CommandStation-EX
|
||||
*
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Ethernet Interface added by Gregor Baues
|
||||
*/
|
||||
|
||||
#ifndef EthernetInterface_h
|
||||
#define EthernetInterface_h
|
||||
|
||||
#include "DCCEXParser.h"
|
||||
#include "MemStream.h"
|
||||
#include <Arduino.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <Ethernet.h>
|
||||
|
||||
/* some generated mac addresses as EthernetShields don't have one by default in HW.
|
||||
* Sometimes they come on a sticker on the EthernetShield then use this address otherwise
|
||||
* just choose one from below or generate one yourself. Only condition is that there is no
|
||||
* other device on your network with the same Mac address.
|
||||
*
|
||||
* 52:b8:8a:8e:ce:21
|
||||
* e3:e9:73:e1:db:0d
|
||||
* 54:2b:13:52:ac:0c
|
||||
* c2:d8:d4:7d:7c:cb
|
||||
* 86:cf:fa:9f:07:79
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Network Configuration
|
||||
*
|
||||
*/
|
||||
#define MAC_ADDRESS { 0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 } // MAC address of your networking card found on the sticker on your card or take one from above
|
||||
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one; make sure
|
||||
// this one is not used elsewhere and corresponds to your network layout
|
||||
#define LISTEN_PORT 3366 // default listen port for the server
|
||||
#define MAX_ETH_BUFFER 250
|
||||
|
||||
typedef void (*HTTP_CALLBACK)(Print * stream, byte * cmd);
|
||||
|
||||
enum protocolType {
|
||||
TCP,
|
||||
UDP
|
||||
};
|
||||
|
||||
typedef void (*protocolCallback)();
|
||||
|
||||
class EthernetInterface {
|
||||
|
||||
private:
|
||||
EthernetServer server;
|
||||
|
||||
public:
|
||||
DCCEXParser ethParser;
|
||||
bool connected;
|
||||
byte mac[6];
|
||||
IPAddress ip;
|
||||
uint16_t port;
|
||||
IPAddress dnsip;
|
||||
|
||||
void setup(protocolType pt, uint16_t lp); // specific port nummber
|
||||
void setup(protocolType pt); // uses default port number
|
||||
void setup(); // all defaults (protocol/port)
|
||||
|
||||
protocolCallback protocolHandler;
|
||||
|
||||
void loop();
|
||||
|
||||
private:
|
||||
static EthernetInterface * singleton;
|
||||
|
||||
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer to hold incoming UDP packet,
|
||||
uint8_t buffer[MAX_ETH_BUFFER]; // buffer provided to the streamer to be filled with the reply (used by TCP also for the recv)
|
||||
MemStream * streamer; // streamer who writes the results to the buffer
|
||||
EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time; This depends on the chipset used on the Shield
|
||||
|
||||
bool setupConnection();
|
||||
static void udpHandler();
|
||||
static void tcpHandler();
|
||||
void udpHandler2();
|
||||
void tcpHandler2();
|
||||
EthernetUDP Udp;
|
||||
|
||||
EthernetServer getServer() {
|
||||
return server;
|
||||
};
|
||||
void setServer(EthernetServer s) {
|
||||
server = s;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
122
EthernetSetup.cpp
Normal file
122
EthernetSetup.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "NetworkDiag.h"
|
||||
#include "NetworkSetup.h"
|
||||
#include "EthernetSetup.h"
|
||||
|
||||
byte EthernetSetup::setup()
|
||||
{
|
||||
INFO(F("Initialize MAC Address ..."));
|
||||
NetworkSetup::genMacAddress();
|
||||
|
||||
INFO(F("Initialize Ethernet with DHCP"));
|
||||
if (Ethernet.begin(mac) == 0)
|
||||
{
|
||||
WARN(F("Failed to configure Ethernet using DHCP ... Trying with fixed IP"));
|
||||
Ethernet.begin(mac, IPAddress(IP_ADDRESS)); // default ip address
|
||||
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware)
|
||||
{
|
||||
ERR(F("Ethernet shield was not found. Sorry, can't run without hardware. :("));
|
||||
return 0;
|
||||
};
|
||||
if (Ethernet.linkStatus() == LinkOFF)
|
||||
{
|
||||
ERR(F("Ethernet cable is not connected."));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
maxConnections = MAX_SOCK_NUM;
|
||||
|
||||
if (Ethernet.hardwareStatus() == EthernetW5100)
|
||||
{
|
||||
INFO(F("W5100 Ethernet controller detected."));
|
||||
maxConnections = 4; // Max supported officaly by the W5100 but i have been running over 8 as well. Perf has to be evaluated though comparing 4 vs. 8 connections
|
||||
}
|
||||
else if (Ethernet.hardwareStatus() == EthernetW5200)
|
||||
{
|
||||
INFO(F("W5200 Ethernet controller detected."));
|
||||
maxConnections = 8;
|
||||
}
|
||||
else if (Ethernet.hardwareStatus() == EthernetW5500)
|
||||
{
|
||||
INFO(F("W5500 Ethernet controller detected."));
|
||||
maxConnections = 8;
|
||||
}
|
||||
|
||||
INFO(F("Network Protocol: [%s]"), protocol ? "UDP" : "TCP");
|
||||
switch (protocol)
|
||||
{
|
||||
case UDPR:
|
||||
{
|
||||
udp = new EthernetUDP();
|
||||
byte udpState = udp->begin(port);
|
||||
if (udpState)
|
||||
{
|
||||
TRC(F("UDP status: %d"), udpState);
|
||||
maxConnections = 1; // there is only one UDP object listening for incomming data
|
||||
connected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR(F("UDP failed to start"));
|
||||
connected = false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case TCP:
|
||||
{
|
||||
server = new EthernetServer(port);
|
||||
server->begin();
|
||||
connected = true;
|
||||
break;
|
||||
};
|
||||
case MQTT:
|
||||
{
|
||||
// do the MQTT setup stuff ...
|
||||
};
|
||||
default:
|
||||
{
|
||||
ERR(F("\nUnkown Ethernet protocol; Setup failed"));
|
||||
connected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (connected)
|
||||
{
|
||||
ip = Ethernet.localIP();
|
||||
NetworkSetup::printMacAddress(NetworkSetup::mac);
|
||||
INFO(F("Local IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
||||
INFO(F("Listening on port: [%d]"), port);
|
||||
dnsip = Ethernet.dnsServerIP();
|
||||
INFO(F("DNS server IP address: [%d.%d.%d.%d] "), dnsip[0], dnsip[1], dnsip[2], dnsip[3]);
|
||||
INFO(F("Number of connections: [%d]"), maxConnections);
|
||||
return true;
|
||||
}
|
||||
return false; // something went wrong
|
||||
}
|
||||
|
||||
EthernetSetup::EthernetSetup() {}
|
||||
EthernetSetup::EthernetSetup(uint16_t p, protocolType pt ) { port = p; protocol = pt; }
|
||||
EthernetSetup::~EthernetSetup() {}
|
||||
|
48
EthernetSetup.h
Normal file
48
EthernetSetup.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef EthernetSetup_h
|
||||
#define EthernetSetup_h
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include "NetworkSetup.h"
|
||||
|
||||
class EthernetSetup: public NetworkSetup {
|
||||
|
||||
private:
|
||||
|
||||
EthernetServer* server = 0;
|
||||
EthernetUDP* udp = 0;
|
||||
|
||||
public:
|
||||
|
||||
byte setup(); // sets the TCP server or UDP udp object; returns 1 if the connection was successfull 0 otherwise
|
||||
EthernetServer *getTCPServer() {
|
||||
return server;
|
||||
}
|
||||
EthernetUDP *getUDPServer() {
|
||||
return udp;
|
||||
}
|
||||
|
||||
EthernetSetup();
|
||||
EthernetSetup(uint16_t port, protocolType protocol);
|
||||
~EthernetSetup();
|
||||
};
|
||||
|
||||
#endif
|
432
HttpRequest.cpp
Normal file
432
HttpRequest.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* © 2012 Francisco G. Paletta, © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "HttpRequest.h"
|
||||
#include "NetworkInterface.h"
|
||||
#include "DIAG.h"
|
||||
|
||||
// public interface to the parsed request
|
||||
// static ParsedRequest req;
|
||||
|
||||
HttpRequest::HttpRequest()
|
||||
{
|
||||
resetRequest();
|
||||
req.method = method;
|
||||
req.uri = uri;
|
||||
req.version = version;
|
||||
req.paramCount = ¶mCount;
|
||||
/**
|
||||
* @todo add list of parameters
|
||||
*
|
||||
*/
|
||||
}
|
||||
|
||||
ParsedRequest HttpRequest::getParsedRequest()
|
||||
{
|
||||
return req;
|
||||
}
|
||||
|
||||
void HttpRequest::resetRequest()
|
||||
{
|
||||
|
||||
freeParamMem(firstParam);
|
||||
freeCookieMem(firstCookie);
|
||||
|
||||
parseStatus = HTTP_PARSE_INIT;
|
||||
method[0] = '\0';
|
||||
uri[0] = '\0';
|
||||
version[0] = '\0';
|
||||
firstParam = NULL;
|
||||
firstCookie = NULL;
|
||||
paramCount = 0;
|
||||
cookieCount = 0;
|
||||
tmpParamName[0] = '\0';
|
||||
tmpParamValue[0] = '\0';
|
||||
tmpAttribName[0] = '\0';
|
||||
tmpAttribValue[0] = '\0';
|
||||
tmpCookieName[0] = '\0';
|
||||
tmpCookieValue[0] = '\0';
|
||||
dataBlockLength = 0;
|
||||
dataCount = 0;
|
||||
}
|
||||
|
||||
void HttpRequest::freeParamMem(Params *paramNode)
|
||||
{
|
||||
if (paramNode != NULL)
|
||||
{
|
||||
freeParamMem(paramNode->next);
|
||||
delete paramNode;
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequest::freeCookieMem(Cookies *cookieNode)
|
||||
{
|
||||
if (cookieNode != NULL)
|
||||
{
|
||||
freeCookieMem(cookieNode->next);
|
||||
delete cookieNode;
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequest::parseRequest(char c)
|
||||
{
|
||||
|
||||
char cStr[2];
|
||||
cStr[0] = c;
|
||||
cStr[1] = '\0';
|
||||
switch (parseStatus)
|
||||
{
|
||||
|
||||
case HTTP_METHOD:
|
||||
if (c == ' ')
|
||||
parseStatus = HTTP_URI;
|
||||
else if (strlen(method) < HTTP_REQ_METHOD_LENGTH - 1)
|
||||
strcat(method, cStr);
|
||||
break;
|
||||
|
||||
case HTTP_URI:
|
||||
// DIAG(F("HTTP_URI: %c\n"), c);
|
||||
if (c == ' ')
|
||||
parseStatus = HTTP_VERSION;
|
||||
else if (c == '?')
|
||||
parseStatus = HTTP_GET_NAME;
|
||||
else if (strlen(uri) < HTTP_REQ_URI_LENGTH - 1)
|
||||
strcat(uri, cStr);
|
||||
break;
|
||||
|
||||
case HTTP_GET_NAME:
|
||||
// DIAG(F("HTTP_GET_NAME: %c\n"), c);
|
||||
if (c == ' ')
|
||||
parseStatus = HTTP_VERSION;
|
||||
else if (c != '=')
|
||||
{
|
||||
if (strlen(tmpParamName) < HTTP_REQ_PARAM_NAME_LENGTH - 1)
|
||||
strcat(tmpParamName, cStr);
|
||||
}
|
||||
else
|
||||
parseStatus = HTTP_GET_VALUE;
|
||||
break;
|
||||
|
||||
case HTTP_GET_VALUE:
|
||||
// DIAG(F("HTTP_GET_VALUE: %c\n"), c);
|
||||
if (c == '&')
|
||||
{
|
||||
addParam();
|
||||
parseStatus = HTTP_GET_NAME;
|
||||
}
|
||||
else if (c == ' ')
|
||||
{
|
||||
addParam();
|
||||
parseStatus = HTTP_VERSION;
|
||||
}
|
||||
else if (strlen(tmpParamValue) < HTTP_REQ_PARAM_VALUE_LENGTH - 1)
|
||||
strcat(tmpParamValue, cStr);
|
||||
break;
|
||||
|
||||
case HTTP_VERSION:
|
||||
// DIAG(F("HTTP_VERSION: %c\n"), c);
|
||||
if (c == '\n')
|
||||
parseStatus = HTTP_NEW_LINE;
|
||||
else if (c != '\r' && strlen(version) < HTTP_REQ_VERSION_LENGTH - 1)
|
||||
strcat(version, cStr);
|
||||
break;
|
||||
|
||||
case HTTP_NEW_LINE:
|
||||
// DIAG(F("HTTP_NEW_LINE: %c\n"), c);
|
||||
if (c != '\r' && c != '\n')
|
||||
{
|
||||
parseStatus = HTTP_ATTRIB_NAME;
|
||||
tmpAttribName[0] = '\0';
|
||||
tmpAttribValue[0] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(method, "POST") == 0 && dataBlockLength > 0)
|
||||
parseStatus = HTTP_POST_NAME;
|
||||
else
|
||||
// DIAG(F("HTTP_REQUEST_END: %c\n"), c);
|
||||
parseStatus = HTTP_REQUEST_END;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_ATTRIB_NAME:
|
||||
// DIAG(F("HTTP_ATTRIB_NAME: %c\n"), c);
|
||||
if (c == '\n')
|
||||
parseStatus = HTTP_NEW_LINE;
|
||||
else if (c != ':' && c != ' ' && c != '\r')
|
||||
{
|
||||
if (strlen(tmpAttribName) < HTTP_REQ_ATTRIB_NAME_LENGTH - 1)
|
||||
strcat(tmpAttribName, cStr);
|
||||
}
|
||||
else if (c == ' ')
|
||||
{
|
||||
if (strcmp(tmpAttribName, "Cookie") == 0)
|
||||
parseStatus = HTTP_COOKIE_NAME;
|
||||
else
|
||||
parseStatus = HTTP_ATTRIB_VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_ATTRIB_VALUE:
|
||||
// DIAG(F("HTTP_ATTRIB_VALUE: %c\n"), c);
|
||||
if (c == '\n')
|
||||
{
|
||||
addAttrib();
|
||||
parseStatus = HTTP_NEW_LINE;
|
||||
}
|
||||
else if (c != '\r')
|
||||
if (strlen(tmpAttribValue) < HTTP_REQ_ATTRIB_VALUE_LENGTH - 1)
|
||||
strcat(tmpAttribValue, cStr);
|
||||
break;
|
||||
|
||||
case HTTP_POST_NAME:
|
||||
dataCount++;
|
||||
if (c != '=')
|
||||
{
|
||||
if (strlen(tmpParamName) < HTTP_REQ_PARAM_NAME_LENGTH - 1)
|
||||
strcat(tmpParamName, cStr);
|
||||
}
|
||||
else
|
||||
parseStatus = HTTP_POST_VALUE;
|
||||
if (dataCount > dataBlockLength)
|
||||
{
|
||||
addParam();
|
||||
// DIAG(F("HTTP_REQUEST_END: %c\n"), c);
|
||||
parseStatus = HTTP_REQUEST_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_POST_VALUE:
|
||||
dataCount++;
|
||||
if (c == '&')
|
||||
{
|
||||
addParam();
|
||||
parseStatus = HTTP_POST_NAME;
|
||||
}
|
||||
else if (strlen(tmpParamValue) < HTTP_REQ_PARAM_VALUE_LENGTH - 1)
|
||||
strcat(tmpParamValue, cStr);
|
||||
if (dataCount > dataBlockLength)
|
||||
{
|
||||
addParam();
|
||||
// DIAG(F("HTTP_REQUEST_END: %c\n"), c);
|
||||
parseStatus = HTTP_REQUEST_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_COOKIE_NAME:
|
||||
if (c == '\n')
|
||||
parseStatus = HTTP_NEW_LINE;
|
||||
else if (c != '=')
|
||||
{
|
||||
if (c != ' ' && strlen(tmpCookieName) < HTTP_REQ_COOKIE_NAME_LENGTH - 1)
|
||||
strcat(tmpCookieName, cStr);
|
||||
}
|
||||
else
|
||||
parseStatus = HTTP_COOKIE_VALUE;
|
||||
break;
|
||||
|
||||
case HTTP_COOKIE_VALUE:
|
||||
if (c == ';')
|
||||
{
|
||||
addCookie();
|
||||
parseStatus = HTTP_COOKIE_NAME;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
addCookie();
|
||||
parseStatus = HTTP_NEW_LINE;
|
||||
}
|
||||
else if (c != '\r' && c != ' ' && strlen(tmpCookieValue) < HTTP_REQ_COOKIE_VALUE_LENGTH - 1)
|
||||
strcat(tmpCookieValue, cStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpRequest::endOfRequest()
|
||||
{
|
||||
if (parseStatus == HTTP_REQUEST_END)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void HttpRequest::addParam()
|
||||
{
|
||||
|
||||
Params **cursor;
|
||||
|
||||
cursor = &firstParam;
|
||||
while ((*cursor) != NULL)
|
||||
{
|
||||
if (strcmp((*cursor)->name, tmpParamName) == 0)
|
||||
break;
|
||||
cursor = &((*cursor)->next);
|
||||
}
|
||||
if ((*cursor) == NULL)
|
||||
{
|
||||
// DIAG(F("New Param: %s\n"), tmpParamName);
|
||||
(*cursor) = new Params;
|
||||
strcpy((*cursor)->name, tmpParamName);
|
||||
strcpy((*cursor)->value, tmpParamValue);
|
||||
(*cursor)->next = NULL;
|
||||
paramCount++;
|
||||
}
|
||||
|
||||
tmpParamName[0] = '\0';
|
||||
tmpParamValue[0] = '\0';
|
||||
}
|
||||
|
||||
void HttpRequest::addCookie()
|
||||
{
|
||||
|
||||
Cookies **cursor;
|
||||
|
||||
cursor = &firstCookie;
|
||||
while ((*cursor) != NULL)
|
||||
{
|
||||
if (strcmp((*cursor)->name, tmpCookieName) == 0)
|
||||
break;
|
||||
cursor = &((*cursor)->next);
|
||||
}
|
||||
if ((*cursor) == NULL)
|
||||
{
|
||||
(*cursor) = new Cookies;
|
||||
strcpy((*cursor)->name, tmpCookieName);
|
||||
strcpy((*cursor)->value, tmpCookieValue);
|
||||
(*cursor)->next = NULL;
|
||||
cookieCount++;
|
||||
}
|
||||
|
||||
tmpCookieName[0] = '\0';
|
||||
tmpCookieValue[0] = '\0';
|
||||
}
|
||||
|
||||
void HttpRequest::addAttrib()
|
||||
{
|
||||
|
||||
if (strcmp(tmpAttribName, "Content-Length") == 0)
|
||||
dataBlockLength = atoi(tmpAttribValue);
|
||||
|
||||
tmpAttribName[0] = '\0';
|
||||
tmpAttribValue[0] = '\0';
|
||||
}
|
||||
|
||||
uint8_t HttpRequest::getParam(uint8_t paramNum, char *name, char *value)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
|
||||
Params **cursor;
|
||||
|
||||
cursor = &firstParam;
|
||||
while ((*cursor) != NULL)
|
||||
{
|
||||
i++;
|
||||
if (i == paramNum)
|
||||
{
|
||||
strcpy(name, (*cursor)->name);
|
||||
strcpy(value, (*cursor)->value);
|
||||
break;
|
||||
}
|
||||
cursor = &((*cursor)->next);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
Params* HttpRequest::getParam(uint8_t paramNum)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
|
||||
Params **cursor;
|
||||
|
||||
cursor = &firstParam;
|
||||
while ((*cursor) != NULL)
|
||||
{
|
||||
i++;
|
||||
if (i == paramNum)
|
||||
{
|
||||
break;
|
||||
}
|
||||
cursor = &((*cursor)->next);
|
||||
}
|
||||
return *cursor;
|
||||
}
|
||||
|
||||
uint8_t HttpRequest::getParam(char *name, char *value)
|
||||
{
|
||||
|
||||
uint8_t pos = 0, i = 0;
|
||||
Params **cursor;
|
||||
|
||||
cursor = &firstParam;
|
||||
while ((*cursor) != NULL)
|
||||
{
|
||||
i++;
|
||||
if (strcmp((*cursor)->name, name) == 0)
|
||||
{
|
||||
strcpy(value, (*cursor)->value);
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
cursor = &((*cursor)->next);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
uint8_t HttpRequest::getCookie(uint8_t cookieNum, char *name, char *value)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
|
||||
Cookies **cursor;
|
||||
|
||||
cursor = &firstCookie;
|
||||
while ((*cursor) != NULL)
|
||||
{
|
||||
i++;
|
||||
if (i == cookieNum)
|
||||
{
|
||||
strcpy(name, (*cursor)->name);
|
||||
strcpy(value, (*cursor)->value);
|
||||
break;
|
||||
}
|
||||
cursor = &((*cursor)->next);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
uint8_t HttpRequest::getCookie(char *name, char *value)
|
||||
{
|
||||
|
||||
uint8_t pos = 0, i = 0;
|
||||
Cookies **cursor;
|
||||
|
||||
cursor = &firstCookie;
|
||||
while ((*cursor) != NULL)
|
||||
{
|
||||
i++;
|
||||
if (strcmp((*cursor)->name, name) == 0)
|
||||
{
|
||||
strcpy(value, (*cursor)->value);
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
cursor = &((*cursor)->next);
|
||||
}
|
||||
return pos;
|
||||
}
|
123
HttpRequest.h
Normal file
123
HttpRequest.h
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
/*
|
||||
* © 2012 Francisco G. Paletta, © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HttpRequest_h
|
||||
#define HttpRequest_h
|
||||
|
||||
#include "Ethernet.h"
|
||||
|
||||
// Buffer lengths
|
||||
#define HTTP_REQ_METHOD_LENGTH 10 //10 is enough
|
||||
#define HTTP_REQ_URI_LENGTH 32 //adjust if you have long path/file names
|
||||
#define HTTP_REQ_VERSION_LENGTH 10 //10 is enough
|
||||
#define HTTP_REQ_PARAM_NAME_LENGTH 16 //adjust to meet your needs
|
||||
#define HTTP_REQ_PARAM_VALUE_LENGTH 16 //adjust to meet your needs
|
||||
#define HTTP_REQ_ATTRIB_NAME_LENGTH 16 //enough to track attribute name
|
||||
#define HTTP_REQ_ATTRIB_VALUE_LENGTH 16 //enough to track "Content-Length" value
|
||||
#define HTTP_REQ_COOKIE_NAME_LENGTH 10 //adjust to meet your needs
|
||||
#define HTTP_REQ_COOKIE_VALUE_LENGTH 16 //adjust to meet your needs
|
||||
|
||||
// Parsing status
|
||||
#define HTTP_PARSE_INIT 0 //Initial Parser Status
|
||||
#define HTTP_METHOD 0 //Parse the Method: GET POST UPDATE etc
|
||||
#define HTTP_URI 1 //Parse the URI
|
||||
#define HTTP_GET_NAME 11 //Parse the GET parameter NAME
|
||||
#define HTTP_GET_VALUE 12 //Parse the GET parameter VALUE
|
||||
#define HTTP_VERSION 2 //Parse the version: HTTP1.1
|
||||
#define HTTP_NEW_LINE 3 //Starts reading a new line
|
||||
#define HTTP_ATTRIB_NAME 41 //Read the attibutes NAME
|
||||
#define HTTP_ATTRIB_VALUE 42 //Read the attribute VALUE
|
||||
#define HTTP_POST_NAME 51 //Read the POST parameter NAME
|
||||
#define HTTP_POST_VALUE 52 //Read the POST paramenter VALUE
|
||||
#define HTTP_COOKIE_NAME 61 //Read the COOKIE NAME
|
||||
#define HTTP_COOKIE_VALUE 62 //Read the COOKIE VALUE
|
||||
#define HTTP_REQUEST_END 99 //Finished reading the HTTP Request
|
||||
|
||||
// returned to the callback
|
||||
struct ParsedRequest {
|
||||
char* method;
|
||||
char* uri;
|
||||
char* version;
|
||||
uint8_t* paramCount;
|
||||
// uint8_t (*getByIndex)(int, char*, char*);
|
||||
// uint8_t (*getByName)(char*, char*);
|
||||
};
|
||||
|
||||
struct Params
|
||||
{
|
||||
char name[HTTP_REQ_PARAM_NAME_LENGTH];
|
||||
char value[HTTP_REQ_PARAM_VALUE_LENGTH];
|
||||
Params *next;
|
||||
};
|
||||
|
||||
class HttpRequest
|
||||
{
|
||||
private:
|
||||
|
||||
// no Cookies
|
||||
struct Cookies
|
||||
{
|
||||
char name[HTTP_REQ_COOKIE_NAME_LENGTH];
|
||||
char value[HTTP_REQ_COOKIE_VALUE_LENGTH];
|
||||
Cookies *next;
|
||||
};
|
||||
|
||||
uint8_t parseStatus;
|
||||
Params *firstParam;
|
||||
Cookies *firstCookie;
|
||||
|
||||
char tmpParamName[HTTP_REQ_PARAM_NAME_LENGTH];
|
||||
char tmpParamValue[HTTP_REQ_PARAM_VALUE_LENGTH];
|
||||
char tmpAttribName[HTTP_REQ_ATTRIB_NAME_LENGTH];
|
||||
char tmpAttribValue[HTTP_REQ_ATTRIB_NAME_LENGTH];
|
||||
char tmpCookieName[HTTP_REQ_COOKIE_NAME_LENGTH]; // no use
|
||||
char tmpCookieValue[HTTP_REQ_COOKIE_VALUE_LENGTH]; // no use
|
||||
|
||||
uint16_t dataBlockLength, dataCount;
|
||||
|
||||
void addParam();
|
||||
void addAttrib();
|
||||
void addCookie(); // no use
|
||||
void freeParamMem(Params *paramNode);
|
||||
void freeCookieMem(Cookies *cookieNode);
|
||||
|
||||
char method[HTTP_REQ_METHOD_LENGTH]; // user
|
||||
char uri[HTTP_REQ_URI_LENGTH]; // user
|
||||
char version[HTTP_REQ_VERSION_LENGTH]; // user
|
||||
|
||||
uint8_t paramCount; // user
|
||||
uint8_t cookieCount; // no use - no cookie support
|
||||
|
||||
uint8_t getParam(uint8_t paramNum, char *name, char *value); // user
|
||||
uint8_t getParam(char *name, char *value); // user
|
||||
uint8_t getCookie(uint8_t cookieNum, char *name, char *value); // no use
|
||||
uint8_t getCookie(char *name, char *value); // no use
|
||||
|
||||
ParsedRequest req;
|
||||
|
||||
public:
|
||||
HttpRequest();
|
||||
void resetRequest();
|
||||
void parseRequest(char c);
|
||||
bool endOfRequest();
|
||||
ParsedRequest getParsedRequest();
|
||||
Params* getParam(uint8_t paramNum);
|
||||
void (* callback)(ParsedRequest *req, Client *client);
|
||||
};
|
||||
|
||||
#endif
|
@@ -21,7 +21,7 @@ const byte UNUSED_PIN = 255;
|
||||
// Arduino standard Motor Shield
|
||||
#define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \
|
||||
new MotorDriver(3, 12, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \
|
||||
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 2000, UNUSED_PIN)
|
||||
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A2, 2.99, 2000, UNUSED_PIN)
|
||||
|
||||
// Pololu Motor Shield
|
||||
#define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \
|
||||
|
64
NetworkConfig.h
Normal file
64
NetworkConfig.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* © 2020, Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* some generated mac addresses as EthernetShields don't have one by default in HW.
|
||||
* Sometimes they come on a sticker on the EthernetShield then use this address otherwise
|
||||
* just choose one from below or generate one yourself. Only condition is that there is no
|
||||
* other device on your network with the same Mac address.
|
||||
*
|
||||
* 52:b8:8a:8e:ce:21
|
||||
* e3:e9:73:e1:db:0d
|
||||
* 54:2b:13:52:ac:0c
|
||||
* c2:d8:d4:7d:7c:cb
|
||||
* 86:cf:fa:9f:07:79
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Build configuration
|
||||
*
|
||||
*/
|
||||
|
||||
#define DCCEX_ENABLED // uncomment to enable CS-EX integration; Commented will operate as standalone and only echo commands as replies
|
||||
|
||||
/**
|
||||
* @brief Network operational configuration
|
||||
*
|
||||
*/
|
||||
#define LISTEN_PORT 2560 // default listen port for the server
|
||||
#define MAC_ADDRESS {0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21} // MAC address of your networking card found on the sticker on your card or take one from above
|
||||
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one;
|
||||
|
||||
/**
|
||||
* @brief NetworkInterface configuration
|
||||
*
|
||||
*/
|
||||
#define MAX_INTERFACES 4 // Consume too much memeory beyond in general not more than 2 should be required
|
||||
#define MAX_SOCK_NUM 8 // Maximum number of sockets allowed for any WizNet based EthernetShield. The W5100 only supports 4
|
||||
#define MAX_WIFI_SOCK 5 // ESP8266 doesn't support more than 5 connections
|
||||
#define MAX_ETH_BUFFER 128 // maximum length we read in one go from a TCP packet.
|
||||
#define MAX_OVERFLOW MAX_ETH_BUFFER / 2 // length of the overflow buffer to be used for a given connection.
|
||||
#define MAX_JMRI_CMD MAX_ETH_BUFFER / 2 // MAX Length of a JMRI Command
|
||||
#define OUTBOUND_RING_SIZE 2048
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @todo - MAC address automation
|
||||
* @todo - Wifi setup process in case no permanent setup yet done
|
||||
* @todo - RingBuffer hack to be reviewed
|
||||
*
|
||||
*/
|
36
NetworkDiag.cpp
Normal file
36
NetworkDiag.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// #include "DIAG.h"
|
||||
// #include "MemoryFree.h"
|
||||
// #include "NetworkDiag.h"
|
||||
|
||||
|
||||
int _dMem = 0;
|
||||
int _cMem = 0;
|
||||
byte _nLogLevel = 0; // (Third digit)
|
||||
byte _nInfoLevel = 1; // (Second digit)runtime level of the details of the messages displayed; 1 = only the diag messagges, 2 - incl time / file / line / freemem information - TBD
|
||||
byte _dOutput = 1; // (First digit) where the diag messages shall be send; 1 = Serial, 2 = future CLI on port 23 - TBD (Hundreds)
|
||||
|
||||
// e.g. 124 Send up to TRACE with full info to Serial or 211 send basic INFO level messages to the CLI
|
||||
|
||||
// nLogLevel 0 to 5 send to serial according to the log level 0 = SILENT, 1 = INFO, 2 = WARNING, 3 = ERROR, 4 = TRACE, 5 = DEBUG
|
||||
// nLoglevel 10 to 15 send to network client (10) + according to the log level 0 = SILENT, 1 = INFO, 2 = WARNING, 3 = ERROR, 4 = TRACE, 5 = DEBUG
|
124
NetworkDiag.h
Normal file
124
NetworkDiag.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef NetworkDiag_h
|
||||
#define NetworkDiag_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "freeMemory.h"
|
||||
#include "StringFormatter.h"
|
||||
|
||||
#include "TransportProcessor.h"
|
||||
|
||||
#define EH_DW(code) \
|
||||
do \
|
||||
{ \
|
||||
code \
|
||||
} while (0) //wraps in a do while(0) so that the syntax is correct.
|
||||
|
||||
|
||||
// enable/ disbale the loglevel at runtime everything up to the compile time level will be shown the rest not !
|
||||
#define EH_IFLL(LL,code) if(_nLogLevel >= LL){code}
|
||||
#define EH_IFIL(IL,code) if(_nInfoLevel >= IL){code}
|
||||
|
||||
#ifndef DEBUG
|
||||
#define DEBUG
|
||||
#ifndef LOGLEVEL
|
||||
#define LOGLEVEL 4 // compile time level by default up to error can be overridden at compiletime with a -D flag in build_flags (PIO) extra.comiler.flags on Arduino IDE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class NetworkDiag: public StringFormatter {
|
||||
|
||||
public:
|
||||
|
||||
static void setDiagOut(Connection *c) {
|
||||
if ( c->client->connected() ) {
|
||||
diagSerial = c->client;
|
||||
}
|
||||
}
|
||||
|
||||
static void resetDiagOut() {
|
||||
diagSerial = &Serial;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#define NDIAG NetworkDiag::diag
|
||||
|
||||
extern int _dMem;
|
||||
extern int _cMem;
|
||||
extern byte _nLogLevel; // runtime level set by <D NET #level >; by default set to 0 i.e. silent excpet for network startup at level 3 to get the init mesages shown
|
||||
extern byte _nInfoLevel; // runtime lvel of the details of the messages displayed - TBD
|
||||
extern byte _dOutput; // where the diag messages shall be send; 1 = Serial, 2 = future CLI on port 23 - TBD
|
||||
|
||||
#define LOGV_DEBUG 5
|
||||
#define LOGV_TRACE 4
|
||||
#define LOGV_ERROR 3
|
||||
#define LOGV_WARN 2
|
||||
#define LOGV_INFO 1
|
||||
#define LOGV_SILENT 0
|
||||
|
||||
#ifdef LOGLEVEL
|
||||
#if LOGLEVEL == LOGV_SILENT
|
||||
#define INFO(message...)
|
||||
#define WARN(message...)
|
||||
#define ERR(message...)
|
||||
#define TRC(message...)
|
||||
#define DBG(message...)
|
||||
#endif
|
||||
#if LOGLEVEL == LOGV_INFO
|
||||
#define INFO(message...) EH_DW(EH_IFLL(LOGV_INFO, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[INF]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define WARN(message...)
|
||||
#define ERR(message...)
|
||||
#define TRC(message...)
|
||||
#define DBG(message...)
|
||||
#endif
|
||||
#if LOGLEVEL == LOGV_WARN
|
||||
#define INFO(message...) EH_DW(EH_IFLL(LOGV_INFO, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[INF]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define WARN(message...) EH_DW(EH_IFLL(LOGV_ERROR, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[WRN]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define ERR(message...)
|
||||
#define TRC(message...)
|
||||
#define DBG(message...)
|
||||
#endif
|
||||
#if LOGLEVEL == LOGV_ERROR
|
||||
#define INFO(message...) EH_DW(EH_IFLL(LOGV_INFO, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[INF]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define WARN(message...) EH_DW(EH_IFLL(LOGV_WARN, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[WRN]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define ERR(message...) EH_DW(EH_IFLL(LOGV_ERROR, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[ERR]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define TRC(message...)
|
||||
#define DBG(message...)
|
||||
#endif
|
||||
#if LOGLEVEL == LOGV_TRACE
|
||||
#define INFO(message...) EH_DW(EH_IFLL(LOGV_INFO, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[INF]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define WARN(message...) EH_DW(EH_IFLL(LOGV_WARN, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[WRN]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define ERR(message...) EH_DW(EH_IFLL(LOGV_ERROR, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[ERR]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define TRC(message...) EH_DW(EH_IFLL(LOGV_TRACE, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[TRC]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define DBG(message...)
|
||||
#endif
|
||||
#if LOGLEVEL >= LOGV_DEBUG
|
||||
#define INFO(message...) EH_DW(EH_IFLL(LOGV_INFO, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[INF]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define WARN(message...) EH_DW(EH_IFLL(LOGV_WARN, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[WRN]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define ERR(message...) EH_DW(EH_IFLL(LOGV_ERROR, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[ERR]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define TRC(message...) EH_DW(EH_IFLL(LOGV_TRACE, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[TRC]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#define DBG(message...) EH_DW(EH_IFLL(LOGV_DEBUG, _cMem = freeMemory(); _dMem = _cMem - _dMem; NDIAG(F("::[DBG]:%d:%d:%s:%d : "), _cMem, _dMem, __FILE__, __LINE__); _dMem = _cMem; NDIAG(message); NDIAG(F("\n")); ))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
172
NetworkInterface.cpp
Normal file
172
NetworkInterface.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "NetworkDiag.h"
|
||||
#include "NetworkSetup.h"
|
||||
#include "NetworkInterface.h"
|
||||
#include "Transport.h"
|
||||
#include "EthernetSetup.h"
|
||||
#include "WifiSetup.h"
|
||||
|
||||
Transport<WiFiServer, WiFiClient, WiFiUDP> *wifiTransport;
|
||||
Transport<EthernetServer, EthernetClient, EthernetUDP> *ethernetTransport;
|
||||
|
||||
DCCNetwork _dccNet;
|
||||
|
||||
void DCCNetwork::loop()
|
||||
{
|
||||
for (byte i = 0; i < _tCounter; i++)
|
||||
{
|
||||
|
||||
Transport<EthernetServer, EthernetClient, EthernetUDP> *e;
|
||||
Transport<WiFiServer, WiFiClient, WiFiUDP> *w;
|
||||
|
||||
switch (_t[i])
|
||||
{
|
||||
case ETHERNET:
|
||||
{
|
||||
e = (Transport<EthernetServer, EthernetClient, EthernetUDP> *)transports[i];
|
||||
e->loop();
|
||||
break;
|
||||
}
|
||||
case WIFI:
|
||||
{
|
||||
w = (Transport<WiFiServer, WiFiClient, WiFiUDP> *)transports[i];
|
||||
w->loop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte DCCNetwork::add(AbstractTransport *t, transportType transport)
|
||||
{
|
||||
if (_tCounter != MAX_INTERFACES)
|
||||
{
|
||||
_t[_tCounter] = transport;
|
||||
transports[_tCounter] = t; // add to array of network interfaces returns the index + 1 if added
|
||||
_tCounter++; // if max intefaces is reached returns 0 for too many ...
|
||||
return _tCounter; // normally a delete shall not be necessary as all is setup at the beginning and shall not change over a session
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkInterface::setup(transportType transport, protocolType protocol, uint16_t port)
|
||||
{
|
||||
bool ok = false;
|
||||
_nLogLevel = 4; // set the log level to ERROR during setup to get proper information
|
||||
|
||||
NetworkSetup::setDeviceId();
|
||||
|
||||
INFO(F("[%s] Transport Setup In Progress ..."), transport ? "Ethernet" : "Wifi");
|
||||
|
||||
// configure the Transport and get Ethernet/Wifi server up and running
|
||||
|
||||
t = transport;
|
||||
switch (transport)
|
||||
{
|
||||
case WIFI:
|
||||
{
|
||||
WifiSetup wSetup(port, protocol);
|
||||
if (wSetup.setup())
|
||||
{
|
||||
wifiTransport = new Transport<WiFiServer, WiFiClient, WiFiUDP>;
|
||||
wifiTransport->id = _dccNet.add(wifiTransport, transport);
|
||||
wifiTransport->server = wSetup.getTCPServer();
|
||||
wifiTransport->port = port;
|
||||
wifiTransport->protocol = protocol;
|
||||
wifiTransport->transport = transport;
|
||||
wifiTransport->udp = wSetup.getUDPServer(); // 0 if TCP is used
|
||||
wifiTransport->maxConnections = wSetup.maxConnections;
|
||||
ok = wifiTransport->setup(this);
|
||||
DBG(F("Interface [%x] bound to transport id [%d:%x]"), this, wifiTransport->id, wifiTransport);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case ETHERNET:
|
||||
{
|
||||
EthernetSetup eSetup(port, protocol);
|
||||
if( eSetup.setup() ) {
|
||||
ethernetTransport = new Transport<EthernetServer, EthernetClient, EthernetUDP>;
|
||||
ethernetTransport->id = _dccNet.add(ethernetTransport, transport);
|
||||
ethernetTransport->server = eSetup.getTCPServer(); // 0 if UDP is used
|
||||
ethernetTransport->port = port;
|
||||
ethernetTransport->protocol = protocol;
|
||||
ethernetTransport->transport = transport;
|
||||
ethernetTransport->udp = eSetup.getUDPServer(); // 0 if TCP is used
|
||||
ethernetTransport->maxConnections = eSetup.maxConnections; // that has been determined during the ethernet/wifi setup
|
||||
ok = ethernetTransport->setup(this); // start the transport i.e. setup all the client connections; We don't need the setup object anymore from here on
|
||||
DBG(F("Interface [%x] bound to transport id [%d:%x]"), this, ethernetTransport->id, ethernetTransport);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
default:
|
||||
{
|
||||
ERR(F("ERROR: Unknown Transport")); // Something went wrong
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INFO(F("[%s] Transport %s ..."), transport ? "Ethernet" : "Wifi", ok ? "OK" : "Failed");
|
||||
// _nLogLevel = 0; // set loging back to silent;
|
||||
}
|
||||
|
||||
void NetworkInterface::setup(transportType tt, protocolType pt)
|
||||
{
|
||||
NetworkInterface::setup(tt, pt, LISTEN_PORT);
|
||||
}
|
||||
|
||||
void NetworkInterface::setup(transportType tt)
|
||||
{
|
||||
NetworkInterface::setup(tt, TCP, LISTEN_PORT);
|
||||
}
|
||||
|
||||
void NetworkInterface::setup()
|
||||
{
|
||||
NetworkInterface::setup(ETHERNET, TCP, LISTEN_PORT);
|
||||
}
|
||||
|
||||
void NetworkInterface::loop()
|
||||
{
|
||||
// loop over all the transports in
|
||||
_dccNet.loop();
|
||||
|
||||
}
|
||||
|
||||
void NetworkInterface::setHttpCallback(HttpCallback callback)
|
||||
{
|
||||
this->httpCallback = callback;
|
||||
}
|
||||
|
||||
HttpCallback NetworkInterface::getHttpCallback()
|
||||
{
|
||||
return this->httpCallback;
|
||||
}
|
||||
|
||||
NetworkInterface::NetworkInterface() {}
|
||||
NetworkInterface::~NetworkInterface() {}
|
94
NetworkInterface.h
Normal file
94
NetworkInterface.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef NetworkInterface_h
|
||||
#define NetworkInterface_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "NetworkConfig.h"
|
||||
#include "HttpRequest.h"
|
||||
|
||||
typedef enum protocolType {
|
||||
TCP,
|
||||
UDPR, // UDP clashes with a class name in the network stack
|
||||
MQTT
|
||||
} protocolType;
|
||||
|
||||
typedef enum transportType {
|
||||
WIFI, // using an AT (Version >= V1.7) command enabled ESP8266 not to be used in conjunction with the WifiInterface though! not tested for conflicts
|
||||
ETHERNET // using the EthernetShield
|
||||
} transportType;
|
||||
|
||||
using HttpCallback = void(*)(ParsedRequest *req, Client *client);
|
||||
|
||||
/**
|
||||
* @brief Abstract parent class of the templated ( Ethernet or Wifi ) class
|
||||
* Instances of Transports are hold through this in an array in DCCNetwork which describes and
|
||||
* actually manages the available transports.
|
||||
*/
|
||||
struct AbstractTransport {
|
||||
void loop(){};
|
||||
virtual ~AbstractTransport(){};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Core class holding and running the instantiated Transports
|
||||
* initalized through the NetworkInterface. The number of transports is
|
||||
* limited by MAX_INTERFACES
|
||||
*
|
||||
*/
|
||||
class DCCNetwork {
|
||||
private:
|
||||
byte _tCounter = 0;
|
||||
transportType _t[MAX_INTERFACES];
|
||||
public:
|
||||
AbstractTransport *transports[MAX_INTERFACES];
|
||||
|
||||
byte add(AbstractTransport* t, transportType _t);
|
||||
void loop();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Main entry point and provider of callbacks. Sole responsibility is to create
|
||||
* the transport endpoints and loop over them for processing
|
||||
*
|
||||
*/
|
||||
class NetworkInterface
|
||||
{
|
||||
private:
|
||||
HttpCallback httpCallback;
|
||||
transportType t;
|
||||
|
||||
public:
|
||||
|
||||
void setHttpCallback(HttpCallback callback);
|
||||
HttpCallback getHttpCallback();
|
||||
void setup(transportType t, protocolType p, uint16_t port); // specific port nummber
|
||||
void setup(transportType t, protocolType p); // uses default port number
|
||||
void setup(transportType t); // defaults for protocol/port
|
||||
|
||||
void setup(); // defaults for all as above plus CABLE (i.e. using EthernetShield ) as default
|
||||
static void loop();
|
||||
|
||||
NetworkInterface();
|
||||
~NetworkInterface();
|
||||
};
|
||||
|
||||
#endif
|
76
NetworkSetup.cpp
Normal file
76
NetworkSetup.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "ArduinoUniqueID.h"
|
||||
#include "NetworkSetup.h"
|
||||
#include "NetworkDiag.h"
|
||||
|
||||
bool NetworkSetup::deviceIdSet = false;
|
||||
bool NetworkSetup::macAddressSet = false;
|
||||
char NetworkSetup::_deviceId[MAXDEVICEID] = {0};
|
||||
uint8_t NetworkSetup::mac[6] = MAC_ADDRESS; // default MacAddress
|
||||
uint8_t NetworkSetup::apWifiMacAddress[6] = MAC_ADDRESS; // default MacAddress
|
||||
uint8_t NetworkSetup::stWifiMacAddress[6] = MAC_ADDRESS; // default MacAddress
|
||||
|
||||
static void array_to_string(byte array[], unsigned int len, char buffer[])
|
||||
{
|
||||
for (unsigned int i = 0; i < len; i++)
|
||||
{
|
||||
byte nib1 = (array[i] >> 4) & 0x0F;
|
||||
byte nib2 = (array[i] >> 0) & 0x0F;
|
||||
buffer[i * 2 + 0] = nib1 < 0xA ? '0' + nib1 : 'A' + nib1 - 0xA;
|
||||
buffer[i * 2 + 1] = nib2 < 0xA ? '0' + nib2 : 'A' + nib2 - 0xA;
|
||||
}
|
||||
buffer[len * 2] = '\0';
|
||||
}
|
||||
|
||||
void NetworkSetup::setDeviceId()
|
||||
{
|
||||
array_to_string(UniqueID, UniqueIDsize, _deviceId);
|
||||
DBG(F("Unique device ID: %s\n"), _deviceId);
|
||||
deviceIdSet = true;
|
||||
}
|
||||
|
||||
void NetworkSetup::printMacAddress(uint8_t a[]) {
|
||||
INFO(F("MAC Address: [%x:%x:%x:%x:%x:%x]"),a[0],a[1],a[2],a[3],a[4],a[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief generates Mac Addresses for Ethernet and WiFi
|
||||
*
|
||||
*/
|
||||
void NetworkSetup::genMacAddress() {
|
||||
|
||||
if (!deviceIdSet) NetworkSetup::setDeviceId();
|
||||
if (!macAddressSet) {
|
||||
for (byte i = 0; i < 6; i++)
|
||||
{
|
||||
mac[i] = UniqueID[i];
|
||||
};
|
||||
}
|
||||
macAddressSet = true;
|
||||
}
|
||||
|
||||
NetworkSetup::NetworkSetup() {}
|
||||
NetworkSetup::~NetworkSetup() {}
|
||||
|
||||
|
||||
// WiFi.apMacAddress(apWifiMacAddress);
|
||||
// WiFi.macAddress(stWifiMacAddress);
|
62
NetworkSetup.h
Normal file
62
NetworkSetup.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef NetworkSetup_h
|
||||
#define NetworkSetup_h
|
||||
|
||||
#include <Ethernet.h>
|
||||
|
||||
#include "NetworkConfig.h"
|
||||
#include "NetworkInterface.h"
|
||||
|
||||
#define MAXDEVICEID 20
|
||||
|
||||
class NetworkSetup
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
static char _deviceId[MAXDEVICEID];
|
||||
static bool macAddressSet;
|
||||
static bool deviceIdSet;
|
||||
|
||||
public:
|
||||
IPAddress dnsip;
|
||||
IPAddress ip;
|
||||
static uint8_t mac[6]; // Default if not set automatically for EthernetShield
|
||||
static uint8_t apWifiMacAddress[6]; // for the WiFi AP
|
||||
static uint8_t stWifiMacAddress[6]; // for the normal WiFi connection
|
||||
|
||||
uint8_t maxConnections;
|
||||
bool connected; // semantics is that the server has successfullt started or not; client connections will be started in the Transport object
|
||||
protocolType protocol;
|
||||
uint16_t port = LISTEN_PORT; // Default port
|
||||
|
||||
static void setDeviceId();
|
||||
char *getDeviceId() {
|
||||
return _deviceId;
|
||||
}
|
||||
static void genMacAddress();
|
||||
static void printMacAddress(uint8_t a[]);
|
||||
|
||||
NetworkSetup();
|
||||
~NetworkSetup();
|
||||
};
|
||||
|
||||
#endif
|
@@ -97,3 +97,24 @@ bool RingStream::commit() {
|
||||
_buffer[_mark]=lowByte(_count);
|
||||
return true; // commit worked
|
||||
}
|
||||
|
||||
//grbba
|
||||
byte *RingStream::getBuffer() {
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
void RingStream::printStream() {
|
||||
DIAG(F(" _len %d _pos_write %d _pos_read %d _overflow %d _mark %d _count %d\n"), _len, _pos_write, _pos_read, _overflow, _mark, _count);
|
||||
};
|
||||
|
||||
void RingStream::resetStream()
|
||||
{
|
||||
memset(_buffer, 0, _len);
|
||||
_pos_write=0;
|
||||
_pos_read=0;
|
||||
_buffer[0]=0;
|
||||
_overflow=false;
|
||||
_mark=0;
|
||||
_count=0;
|
||||
}
|
||||
//grbba
|
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "DIAG.h"
|
||||
|
||||
class RingStream : public Print {
|
||||
|
||||
@@ -34,6 +35,12 @@ class RingStream : public Print {
|
||||
void mark(uint8_t b);
|
||||
bool commit();
|
||||
|
||||
// grbba
|
||||
byte *getBuffer();
|
||||
void resetStream();
|
||||
void printStream();
|
||||
// grbba
|
||||
|
||||
private:
|
||||
int _len;
|
||||
int _pos_write;
|
||||
@@ -42,6 +49,7 @@ class RingStream : public Print {
|
||||
int _mark;
|
||||
int _count;
|
||||
byte * _buffer;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -185,5 +185,4 @@ void StringFormatter::printPadded(Print* stream, long value, byte width, bool fo
|
||||
}
|
||||
if (!formatLeft) stream->print(value, DEC);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -58,4 +58,4 @@ class StringFormatter
|
||||
static void printPadded(Print* stream, long value, byte width, bool formatLeft);
|
||||
|
||||
};
|
||||
#endif
|
||||
#endif
|
189
Transport.cpp
Normal file
189
Transport.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "NetworkDiag.h"
|
||||
#include "NetworkInterface.h"
|
||||
#include "Transport.h"
|
||||
|
||||
#ifdef DCCEX_ENABLED
|
||||
#include "RingStream.h"
|
||||
#endif
|
||||
|
||||
extern bool diagNetwork;
|
||||
extern uint8_t diagNetworkClient;
|
||||
|
||||
template<class S, class C, class U>
|
||||
bool Transport<S,C,U>::setup(NetworkInterface *nw) {
|
||||
t = new TransportProcessor();
|
||||
|
||||
if (protocol == TCP) {
|
||||
connectionPool(server); // server should have started here so create the connection pool only for TCP though
|
||||
t->udp = 0;
|
||||
} else {
|
||||
connectionPool(udp);
|
||||
t->udp = udp;
|
||||
}
|
||||
t->nwi = nw; // The TransportProcessor needs to know which Interface he is connected to
|
||||
connected = true; // server & clients which will recieve/send data have all e setup and are available
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class S, class C, class U>
|
||||
void Transport<S,C,U>::loop() {
|
||||
switch (protocol)
|
||||
{
|
||||
case UDPR:
|
||||
{
|
||||
udpHandler(udp);
|
||||
break;
|
||||
};
|
||||
case TCP:
|
||||
{
|
||||
DBG(F("Transport: %s"), this->transport == WIFI ? "WIFI" : "ETHERNET");
|
||||
tcpSessionHandler(server);
|
||||
};
|
||||
case MQTT:
|
||||
{
|
||||
// MQTT
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template<class S, class C, class U>
|
||||
void Transport<S, C, U>::connectionPool(S *server)
|
||||
{
|
||||
for (int i = 0; i < Transport::maxConnections; i++)
|
||||
{
|
||||
clients[i] = server->accept();
|
||||
connections[i].client = &clients[i];
|
||||
memset(connections[i].overflow, 0, MAX_OVERFLOW);
|
||||
connections[i].id = i;
|
||||
TRC(F("TCP Connection pool: [%d:%x]"), i, connections[i].client);
|
||||
}
|
||||
}
|
||||
template<class S, class C, class U>
|
||||
void Transport<S, C, U>::connectionPool(U *udp)
|
||||
{
|
||||
for (int i = 0; i < Transport::maxConnections; i++)
|
||||
{
|
||||
// clients[i] = server->accept();
|
||||
// connections[i].client = &clients[i];
|
||||
memset(connections[i].overflow, 0, MAX_OVERFLOW);
|
||||
connections[i].id = i;
|
||||
|
||||
TRC(F("UDP Connection pool: [%d:%x]"), i, udp);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @todo implement UDP properly
|
||||
*
|
||||
* @tparam S
|
||||
* @tparam C
|
||||
* @tparam U
|
||||
*/
|
||||
|
||||
template<class S, class C, class U>
|
||||
void Transport<S, C, U>::udpHandler(U* udp)
|
||||
{
|
||||
int packetSize = udp->parsePacket();
|
||||
if (packetSize > 0)
|
||||
{
|
||||
TRC(F("Received packet of size:[%d]"), packetSize);
|
||||
IPAddress remote = udp->remoteIP();
|
||||
char portBuffer[6];
|
||||
TRC(F("From: [%d.%d.%d.%d: %s]"), remote[0], remote[1], remote[2], remote[3], utoa(udp->remotePort(), portBuffer, 10)); // DIAG has issues with unsigend int's so go through utoa
|
||||
|
||||
udp->read(t->buffer, MAX_ETH_BUFFER);
|
||||
t->buffer[packetSize] = 0; // terminate buffer
|
||||
t->readStream(&connections[0], false ); // there is only one connection for UDP; reading into the buffer has been done
|
||||
|
||||
memset(t->buffer, 0, MAX_ETH_BUFFER); // reset PacktBuffer
|
||||
return;
|
||||
|
||||
// send the reply
|
||||
// udp.beginPacket(udp.remoteIP(), udp.remotePort());
|
||||
// parse(&udp, (byte *)buffer, true); //////////// Put into the TransportProcessor Attn the default udp TX buffer on ethernet is 24 on wifi its 256 ??
|
||||
// udp.endPacket();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief As tcpHandler but this time the connections are kept open (thus creating a statefull session) as long as the client doesn't disconnect. A connection
|
||||
* pool has been setup beforehand and determines the number of available sessions depending on the network hardware. Commands crossing packet boundaries will be captured
|
||||
*
|
||||
*/
|
||||
template<class S, class C, class U>
|
||||
void Transport<S,C,U>::tcpSessionHandler(S* server)
|
||||
{
|
||||
// get client from the server
|
||||
C client = server->accept();
|
||||
|
||||
// check for new client
|
||||
if (client)
|
||||
{
|
||||
for (byte i = 0; i < maxConnections; i++)
|
||||
{
|
||||
if (!clients[i])
|
||||
{
|
||||
// On accept() the EthernetServer doesn't track the client anymore
|
||||
// so we store it in our client array
|
||||
clients[i] = client;
|
||||
INFO(F("New Client: [%d:%x]"), i, clients[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for incoming data from all possible clients
|
||||
for (byte i = 0; i < maxConnections; i++)
|
||||
{
|
||||
if (clients[i] && clients[i].available() > 0)
|
||||
{
|
||||
t->readStream(&connections[i], true);
|
||||
}
|
||||
// stop any clients which disconnect
|
||||
for (byte i = 0; i < maxConnections; i++)
|
||||
{
|
||||
if (clients[i] && !clients[i].connected())
|
||||
{
|
||||
INFO(F("Disconnect client #%d"), i);
|
||||
clients[i].stop();
|
||||
connections[i].isProtocolDefined = false;
|
||||
if (diagNetworkClient == i && diagNetwork)
|
||||
{
|
||||
diagNetwork = false;
|
||||
NetworkDiag::resetDiagOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class S, class C, class U>
|
||||
Transport<S,C,U>::Transport(){}
|
||||
template<class S, class C, class U>
|
||||
Transport<S,C,U>::~Transport(){}
|
||||
|
||||
// explicitly instatiate to get the relevant copies for ethernet / wifi build @compile time
|
||||
template class Transport<EthernetServer,EthernetClient,EthernetUDP>;
|
||||
template class Transport<WiFiServer, WiFiClient, WiFiUDP>;
|
97
Transport.h
Normal file
97
Transport.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef Transport_h
|
||||
#define Transport_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Ethernet.h>
|
||||
#include <WiFiEspAT.h>
|
||||
|
||||
#include "NetworkConfig.h"
|
||||
#include "NetworkInterface.h"
|
||||
#include "TransportProcessor.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DCCEX, // if char[0] = < opening bracket the client should be a JMRI / DCC EX client_h
|
||||
WITHROTTLE, //
|
||||
HTTP, // If char[0] = G || P || D; if P then char [1] = U || O || A
|
||||
N_DIAG, // '#' send form a telnet client as FIRST message will then reroute all DIAG messages to that client whilst being able to send jmri type commands
|
||||
UNKNOWN_PROTOCOL
|
||||
} appProtocol;
|
||||
|
||||
// Needed forward declarations
|
||||
struct Connection;
|
||||
class TransportProcessor;
|
||||
|
||||
using appProtocolCallback = void (*)(Connection* c, TransportProcessor* t);
|
||||
|
||||
struct Connection
|
||||
{
|
||||
uint8_t id; // initalized when the pool is setup
|
||||
Client *client; // idem
|
||||
char overflow[MAX_OVERFLOW]; // idem
|
||||
appProtocol p; // dynamically determined upon message reception; first message wins
|
||||
char delimiter = '\0'; // idem
|
||||
bool isProtocolDefined = false; // idem
|
||||
appProtocolCallback appProtocolHandler; // idem
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template <class S, class C, class U> class Transport: public AbstractTransport
|
||||
{
|
||||
|
||||
private:
|
||||
C clients[MAX_SOCK_NUM]; // Client objects created by the connectionPool
|
||||
Connection connections[MAX_SOCK_NUM]; // All the connections build by the connectionPool
|
||||
bool connected = false;
|
||||
TransportProcessor* t; // pointer to the object which handles the incomming/outgoing flow
|
||||
|
||||
void udpHandler(U* udp); // Reads from a Udp socket - todo add incomming queue for processing when the flow is faster than we can process commands
|
||||
void tcpSessionHandler(S* server); // tcpSessionHandler -> connections are maintained open until close by the client
|
||||
void connectionPool(S* server); // allocates the Sockets at setup time and creates the Connections
|
||||
void connectionPool(U* udp); // allocates the UDP Sockets at setup time and creates the Connection
|
||||
|
||||
public:
|
||||
|
||||
uint8_t id;
|
||||
uint16_t port;
|
||||
uint8_t protocol; // TCP or UDP
|
||||
uint8_t transport; // WIFI or ETHERNET
|
||||
S* server; // WiFiServer or EthernetServer
|
||||
U* udp; // UDP socket object
|
||||
uint8_t maxConnections; // number of supported connections depending on the network equipment use
|
||||
|
||||
bool setup(NetworkInterface* nwi); // we get the callbacks from the NetworkInterface
|
||||
void loop();
|
||||
|
||||
bool isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
Transport<S,C,U>();
|
||||
~Transport<S,C,U>();
|
||||
|
||||
};
|
||||
|
||||
#endif // !Transport_h
|
512
TransportProcessor.cpp
Normal file
512
TransportProcessor.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "NetworkDiag.h"
|
||||
#include "NetworkInterface.h"
|
||||
#include "HttpRequest.h"
|
||||
#include "TransportProcessor.h"
|
||||
|
||||
#ifdef DCCEX_ENABLED
|
||||
|
||||
#include "DCCEXParser.h"
|
||||
#include "WiThrottle.h"
|
||||
#include "MemStream.h"
|
||||
|
||||
DCCEXParser dccParser;
|
||||
|
||||
#endif
|
||||
|
||||
HttpRequest httpReq;
|
||||
|
||||
uint16_t _rseq[MAX_SOCK_NUM] = {0}; // sequence number for packets recieved per connection
|
||||
uint16_t _sseq[MAX_SOCK_NUM] = {0}; // sequence number for replies send per connection
|
||||
uint16_t _pNum = 0; // number of total packets recieved
|
||||
uint64_t _tPayload = 0; // number of total bytes recieved
|
||||
unsigned int _nCmds = 0; // total number of commands processed
|
||||
|
||||
char protocolName[5][11] = {"JMRI", "WITHROTTLE", "HTTP", "DIAG", "UNKNOWN"}; // change for Progmem
|
||||
|
||||
bool diagNetwork = false; // if true diag data will be send to the connected telnet client
|
||||
uint8_t diagNetworkClient = 0; // client id for diag output
|
||||
|
||||
#ifdef DCCEX_ENABLED
|
||||
|
||||
void dumpRingStreamBuffer(byte *b, int len)
|
||||
{
|
||||
TRC(F("RingStream buffer length [%d] out of [%d] bytes"), strlen((char *)b), len);
|
||||
TRC(F("%e"), b);
|
||||
}
|
||||
|
||||
RingStream streamer(512); // buffer into which to feed the commands for handling; there will not be an immediate reply
|
||||
// as this is async written to another RingStream i.e. we have to see where in the loop we
|
||||
// generate the replies.
|
||||
|
||||
void sendWiThrottleToDCC(Connection *c, TransportProcessor *t, bool blocking)
|
||||
{
|
||||
|
||||
byte *_buffer = streamer.getBuffer();
|
||||
memset(_buffer, 0, 512); // clear out the _buffer
|
||||
WiThrottle *wt = WiThrottle::getThrottle(c->id); // get a throttle for the Connection; will be created if it doesn't exist
|
||||
|
||||
TRC(F("WiThrottle [%x:%x] parsing: [%e]"), wt, _buffer, t->command);
|
||||
|
||||
wt->parse(&streamer, (byte *)t->command); // get the response; not all commands will produce a reply
|
||||
if (streamer.count() != -1)
|
||||
{
|
||||
dumpRingStreamBuffer(_buffer, 512);
|
||||
TRC(F("UDP %x"), t->udp);
|
||||
|
||||
if ( t->udp != 0) {
|
||||
TRC(F("Sending UDP WiThrottle response ..."));
|
||||
t->udp->beginPacket(t->udp->remoteIP(), t->udp->remotePort());
|
||||
t->udp->write(_buffer, strlen((char *)_buffer));
|
||||
t->udp->endPacket();
|
||||
} else if (c->client->connected()) {
|
||||
c->client->write(_buffer, strlen((char *)_buffer));
|
||||
}
|
||||
}
|
||||
streamer.resetStream();
|
||||
}
|
||||
|
||||
void sendJmriToDCC(Connection *c, TransportProcessor *t, bool blocking)
|
||||
{
|
||||
MemStream streamer((byte *)t->command, MAX_ETH_BUFFER, MAX_ETH_BUFFER, true);
|
||||
|
||||
DBG(F("DCC parsing: [%e]"), t->command);
|
||||
// as we use buffer for recv and send we have to reset the write position
|
||||
streamer.setBufferContentPosition(0, 0);
|
||||
dccParser.parse(&streamer, (byte *)t->command, true); // set to true to that the execution in DCC is sync
|
||||
|
||||
if (streamer.available() == 0)
|
||||
{
|
||||
DBG(F("No response"));
|
||||
}
|
||||
else
|
||||
{
|
||||
t->command[streamer.available()] = '\0'; // mark end of buffer, so it can be used as a string later
|
||||
DBG(F("Response: %s"), t->command);
|
||||
if (c->client->connected())
|
||||
{
|
||||
c->client->write((byte *)t->command, streamer.available());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sending a reply by using the StringFormatter (this will result in every byte send individually which may/will create an important Network overhead).
|
||||
* Here we hook back into the DCC code for actually processing the command using a DCCParser. Alternatively we could use MemeStream in order to build the entiere reply
|
||||
* before ending it.
|
||||
*
|
||||
* @param stream Actually the Client to whom to send the reply. As Clients implement Print this is working
|
||||
* @param t TransportProcessor used for accessing the buffers to be send
|
||||
* @param blocking if set to true will instruct the DCC code to not use the async callback functions
|
||||
*/
|
||||
|
||||
void sendToDCC(Connection *c, TransportProcessor *t, bool blocking)
|
||||
{
|
||||
|
||||
switch (c->p)
|
||||
{
|
||||
case WITHROTTLE:
|
||||
{
|
||||
sendWiThrottleToDCC(c, t, blocking);
|
||||
break;
|
||||
}
|
||||
case DCCEX:
|
||||
{
|
||||
sendJmriToDCC(c, t, blocking);
|
||||
break;
|
||||
}
|
||||
case N_DIAG:
|
||||
case HTTP:
|
||||
case UNKNOWN_PROTOCOL:
|
||||
{
|
||||
// we shall never get here they should have been caught before
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
/**
|
||||
* @brief Sending a reply without going through the StringFormatter. Sends the repy in one go
|
||||
*
|
||||
* @param client Client who send the command to which the reply shall be send
|
||||
* @param command Command initaliy recieved to be echoed back
|
||||
*/
|
||||
void sendReply(Connection *c, TransportProcessor *t)
|
||||
{
|
||||
byte reply[MAX_ETH_BUFFER];
|
||||
byte *response;
|
||||
char *number;
|
||||
char *command = t->command;
|
||||
char seqNumber[6];
|
||||
int i = 0;
|
||||
|
||||
memset(reply, 0, MAX_ETH_BUFFER); // reset reply
|
||||
|
||||
// This expects messages to be send with a trailing sequence number <R 1 1 1:0>
|
||||
// as of my stress test program to verify the arrival of messages
|
||||
|
||||
number = strrchr(command, ':'); // replace the int after the last ':' if number != 0
|
||||
if (number != 0)
|
||||
{
|
||||
while (&command[i] != number)
|
||||
{ // copy command into the reply upto the last ':'
|
||||
reply[i] = command[i];
|
||||
i++;
|
||||
}
|
||||
strcat((char *)reply, ":");
|
||||
itoa(_sseq[c->id], seqNumber, 10);
|
||||
strcat((char *)reply, seqNumber);
|
||||
strcat((char *)reply, ">");
|
||||
response = reply;
|
||||
}
|
||||
else
|
||||
{
|
||||
response = (byte *)command;
|
||||
}
|
||||
|
||||
DBG(F("Response: [%e]"), (char *)response);
|
||||
if (c->client->connected())
|
||||
{
|
||||
c->client->write(response, strlen((char *)response));
|
||||
_sseq[c->id]++;
|
||||
DBG(F("Send"));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief creates a HttpRequest object for the user callback. Some conditions apply esp reagrding the length of the items in the Request
|
||||
* can be found in @file HttpRequest.h
|
||||
*
|
||||
* @param client Client object from whom we receievd the data
|
||||
* @param c id of the Client object
|
||||
*/
|
||||
void httpProcessor(Connection *c, TransportProcessor *t)
|
||||
{
|
||||
|
||||
if (httpReq.callback == 0)
|
||||
return; // no callback i.e. nothing to do
|
||||
/**
|
||||
* @todo look for jmri formatted uris and execute those if there is no callback. If no command found ignore and
|
||||
* ev. send a 401 error back
|
||||
*/
|
||||
uint8_t i, l = 0;
|
||||
ParsedRequest preq;
|
||||
l = strlen((char *)t->buffer);
|
||||
for (i = 0; i < l; i++)
|
||||
{
|
||||
httpReq.parseRequest((char)t->buffer[i]);
|
||||
}
|
||||
if (httpReq.endOfRequest())
|
||||
{
|
||||
preq = httpReq.getParsedRequest();
|
||||
httpReq.callback(&preq, c->client);
|
||||
httpReq.resetRequest();
|
||||
} // else do nothing and continue with the next packet
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the App Protocol. The detection id done upon the very first message recieved. The client will then be bound to that protocol. Its very brittle
|
||||
* as e.g. The N message as first message for WiThrottle is not a requirement by the protocol; If any client talking Withrottle doesn't implement this the detection
|
||||
* will default to JMRI. For HTTP we base this only on a subset of th HTTP verbs which can be used.
|
||||
*
|
||||
* @param a First character of the recieved buffer upon first connection
|
||||
* @param b Second character of the recieved buffer upon first connection
|
||||
* @return appProtocol
|
||||
*/
|
||||
appProtocol setAppProtocol(char a, char b, Connection *c)
|
||||
{
|
||||
appProtocol p;
|
||||
switch (a)
|
||||
{
|
||||
case 'G': // GET
|
||||
case 'C': // CONNECT
|
||||
case 'O': // OPTIONS
|
||||
case 'T': // TRACE
|
||||
{
|
||||
p = HTTP;
|
||||
break;
|
||||
}
|
||||
case 'D': // DELETE or D plux hex value
|
||||
{
|
||||
if (b == 'E')
|
||||
{
|
||||
p = HTTP;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = WITHROTTLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'P':
|
||||
{
|
||||
if (b == 'T' || b == 'R')
|
||||
{
|
||||
p = WITHROTTLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = HTTP; // PUT / PATCH / POST
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'H':
|
||||
{
|
||||
if (b == 'U')
|
||||
{
|
||||
p = WITHROTTLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = HTTP; // HEAD
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
case '*':
|
||||
case 'R':
|
||||
case 'Q': // That doesn't make sense as it's the Q or close on app level
|
||||
case 'N':
|
||||
{
|
||||
p = WITHROTTLE;
|
||||
break;
|
||||
}
|
||||
case '<':
|
||||
{
|
||||
p = DCCEX;
|
||||
break;
|
||||
}
|
||||
case '#':
|
||||
{
|
||||
p = DCCEX;
|
||||
INFO(F("\nDiagnostics routed to network client"));
|
||||
NetworkDiag::setDiagOut(c);
|
||||
diagNetwork = true;
|
||||
diagNetworkClient = c->id;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// here we don't know
|
||||
p = UNKNOWN_PROTOCOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
INFO(F("Client speaks: [%s]"), protocolName[p]);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parses the buffer to extract commands to be executed
|
||||
*
|
||||
*/
|
||||
void processStream(Connection *c, TransportProcessor *t)
|
||||
{
|
||||
uint8_t i, j, k, l = 0;
|
||||
uint8_t *_buffer = t->buffer;
|
||||
|
||||
DBG(F("Buffer: [%e]"), _buffer);
|
||||
memset(t->command, 0, MAX_JMRI_CMD); // clear out the command
|
||||
|
||||
// copy overflow into the command
|
||||
if ((i = strlen(c->overflow)) != 0)
|
||||
{
|
||||
// DBG(F("Copy overflow to command: %e"), c->overflow);
|
||||
strncpy(t->command, c->overflow, i);
|
||||
k = i;
|
||||
}
|
||||
// reset the overflow
|
||||
memset(c->overflow, 0, MAX_OVERFLOW);
|
||||
|
||||
// check if there is again an overflow and copy if needed
|
||||
if ((i = strlen((char *)_buffer)) == MAX_ETH_BUFFER - 1)
|
||||
{
|
||||
// DBG(F("Possible overflow situation detected: %d "), i);
|
||||
j = i;
|
||||
while (_buffer[i] != c->delimiter)
|
||||
{
|
||||
i--;
|
||||
}
|
||||
i++; // start of the buffer to copy
|
||||
l = i;
|
||||
k = j - i; // length to copy
|
||||
|
||||
for (j = 0; j < k; j++, i++)
|
||||
{
|
||||
c->overflow[j] = _buffer[i];
|
||||
// DBG(F("%d %d %d %c"),k,j,i, buffer[i]);
|
||||
}
|
||||
_buffer[l] = '\0'; // terminate buffer just after the last '>'
|
||||
// DBG(F("New buffer: [%s] New overflow: [%s]"), (char*) buffer, c->overflow );
|
||||
}
|
||||
// breakup the buffer using its changed length
|
||||
i = 0;
|
||||
k = strlen(t->command); // current length of the command buffer telling us where to start copy in
|
||||
l = strlen((char *)_buffer);
|
||||
// DBG(F("Command buffer cid[%d]: [%s]:[%d:%d:%d:%x]"), c->id, t->command, i, l, k, c->delimiter );
|
||||
unsigned long _startT = micros();
|
||||
_nCmds = 0;
|
||||
while (i < l)
|
||||
{
|
||||
// DBG(F("l: %d - k: %d - i: %d - %c"), l, k, i, _buffer[i]);
|
||||
t->command[k] = _buffer[i];
|
||||
if (_buffer[i] == c->delimiter)
|
||||
{ // closing bracket need to fix if there is none before an opening bracket ?
|
||||
|
||||
t->command[k + 1] = '\0';
|
||||
|
||||
DBG(F("Command: [%d:%e]"), _rseq[c->id], t->command);
|
||||
#ifdef DCCEX_ENABLED
|
||||
|
||||
sendToDCC(c, t, true); // send the command into the parser and replies back to the client
|
||||
#else
|
||||
sendReply(c, t); // standalone version without CS-EX integration
|
||||
#endif
|
||||
_rseq[c->id]++;
|
||||
_nCmds++;
|
||||
j = 0;
|
||||
k = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
k++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
unsigned long _endT = micros();
|
||||
char time[10] = {0};
|
||||
ultoa(_endT - _startT, time, 10);
|
||||
INFO(F("[%d] Commands processed in [%s]uS\n"), _nCmds, time);
|
||||
}
|
||||
|
||||
void echoProcessor(Connection *c, TransportProcessor *t)
|
||||
{
|
||||
byte reply[MAX_ETH_BUFFER];
|
||||
|
||||
memset(reply, 0, MAX_ETH_BUFFER);
|
||||
sprintf((char *)reply, "ERROR: malformed content in [%s]\n", t->buffer);
|
||||
if (c->client->connected())
|
||||
{
|
||||
c->client->write(reply, strlen((char *)reply));
|
||||
_sseq[c->id]++;
|
||||
c->isProtocolDefined = false; // reset the protocol to not defined so that we can recover the next time
|
||||
}
|
||||
}
|
||||
void jmriProcessor(Connection *c, TransportProcessor *t)
|
||||
{
|
||||
DBG(F("Processing JMRI ..."));
|
||||
processStream(c, t);
|
||||
}
|
||||
void withrottleProcessor(Connection *c, TransportProcessor *t)
|
||||
{
|
||||
DBG(F("Processing WiThrottle ..."));
|
||||
processStream(c, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads what is available on the incomming TCP stream and hands it over to the protocol handler.
|
||||
*
|
||||
* @param c Pointer to the connection struct contining relevant information handling the data from that connection
|
||||
*/
|
||||
|
||||
void TransportProcessor::readStream(Connection *c, bool read)
|
||||
{
|
||||
int count = 0;
|
||||
// read bytes from a TCP client if required
|
||||
if (read) {
|
||||
int len = c->client->read(buffer, MAX_ETH_BUFFER - 1); // count is the amount of data ready for reading, -1 if there is no data, 0 is the connection has been closed
|
||||
buffer[len] = 0;
|
||||
count = len;
|
||||
} else {
|
||||
count = strlen((char *)buffer);
|
||||
}
|
||||
|
||||
// figure out which protocol
|
||||
|
||||
if (!c->isProtocolDefined)
|
||||
{
|
||||
c->p = setAppProtocol(buffer[0], buffer[1], c);
|
||||
c->isProtocolDefined = true;
|
||||
switch (c->p)
|
||||
{
|
||||
case N_DIAG:
|
||||
case DCCEX:
|
||||
{
|
||||
c->delimiter = '>';
|
||||
c->appProtocolHandler = (appProtocolCallback)jmriProcessor;
|
||||
break;
|
||||
}
|
||||
case WITHROTTLE:
|
||||
{
|
||||
c->delimiter = '\n';
|
||||
c->appProtocolHandler = (appProtocolCallback)withrottleProcessor;
|
||||
break;
|
||||
}
|
||||
case HTTP:
|
||||
{
|
||||
c->appProtocolHandler = (appProtocolCallback)httpProcessor;
|
||||
httpReq.callback = nwi->getHttpCallback();
|
||||
break;
|
||||
}
|
||||
case UNKNOWN_PROTOCOL:
|
||||
{
|
||||
INFO(F("Requests will not be handeled and packet echoed back"));
|
||||
c->appProtocolHandler = (appProtocolCallback)echoProcessor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_pNum++;
|
||||
_tPayload = _tPayload + count;
|
||||
#ifdef DCCEX_ENABLED
|
||||
INFO(F("Client #[%d] received packet #[%d] of size:[%d/%d]"), c->id, _pNum, count, _tPayload);
|
||||
#else
|
||||
IPAddress remote = c->client->remoteIP();
|
||||
INFO(F("Client #[%d] Received packet #[%d] of size:[%d] from [%d.%d.%d.%d]"), c->id, _pNum, count, remote[0], remote[1], remote[2], remote[3]);
|
||||
#endif
|
||||
buffer[count] = '\0'; // terminate the string properly
|
||||
INFO(F("Packet: [%e]"), buffer);
|
||||
|
||||
// chop the buffer into CS / WiThrottle commands || assemble command across buffer read boundaries
|
||||
c->appProtocolHandler(c, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sending a reply by using the StringFormatter (this will result in every byte send individually which may/will create an important Network overhead).
|
||||
* Here we hook back into the DCC code for actually processing the command using a DCCParser. Alternatively we could use MemeStream in order to build the entiere reply
|
||||
* before ending it (cf. Scratch pad below)
|
||||
*
|
||||
* @param stream Actually the Client to whom to send the reply. As Clients implement Print this is working
|
||||
* @param command The reply to be send ( echo as in sendReply() )
|
||||
* @param blocking if set to true will instruct the DCC code to not use the async callback functions
|
||||
*/
|
||||
void parse(Print *stream, byte *command, bool blocking)
|
||||
{
|
||||
DBG(F("DCC parsing: [%e]"), command);
|
||||
// echo back (as mock parser )
|
||||
StringFormatter::send(stream, F("reply to: %s"), command);
|
||||
}
|
||||
|
||||
|
55
TransportProcessor.h
Normal file
55
TransportProcessor.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef TransportProcessor_h
|
||||
#define TransportProcessor_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Ethernet.h>
|
||||
#include <WiFiEspAT.h>
|
||||
|
||||
#include "Transport.h"
|
||||
#include "NetworkConfig.h"
|
||||
#include "NetworkInterface.h"
|
||||
|
||||
#ifdef DCCEX_ENABLED
|
||||
#include "RingStream.h"
|
||||
#endif
|
||||
|
||||
class TransportProcessor
|
||||
{
|
||||
private:
|
||||
#ifdef DCCEX_ENABLED
|
||||
void sendToDCC(Connection *c, TransportProcessor* t, bool blocking);
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
UDP *udp; // need to carry the single UDP server instance over to the processor for sending packest
|
||||
NetworkInterface *nwi;
|
||||
uint8_t buffer[MAX_ETH_BUFFER];
|
||||
char command[MAX_JMRI_CMD];
|
||||
|
||||
void readStream(Connection *c, bool read); // process incomming packets and processes them; if read = false the buffer has already been filled
|
||||
|
||||
TransportProcessor(){};
|
||||
~TransportProcessor(){};
|
||||
};
|
||||
|
||||
#endif // !Transport_h
|
@@ -36,8 +36,10 @@ class WifiInboundHandler {
|
||||
IPD_IGNORE_DATA, // got +IPD,c,ll,: ignoring the data that won't fit inblound Ring
|
||||
|
||||
GOT_CLIENT_ID, // clientid prefix to CONNECTED / CLOSED
|
||||
GOT_CLIENT_ID2, // clientid prefix to CONNECTED / CLOSED
|
||||
GOT_CLIENT_ID3 // clientid prefix to CONNECTED / CLOSED
|
||||
GOT_CLIENT_ID2 // clientid prefix to CONNECTED / CLOSED
|
||||
|
||||
//grbba removed following remark from Chris
|
||||
// GOT_CLIENT_ID3 // clientid prefix to CONNECTED / CLOSED
|
||||
};
|
||||
|
||||
|
||||
|
134
WifiSetup.cpp
Normal file
134
WifiSetup.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "NetworkDiag.h"
|
||||
#include "NetworkSetup.h"
|
||||
#include "WifiSetup.h"
|
||||
|
||||
void reverseArray(uint8_t arr[], int start, int end);
|
||||
|
||||
bool WifiSetup::setup() {
|
||||
/**
|
||||
* @todo setup using SoftwareSerial or any other Hardware Serial port on the mega (i.e. 2 or 3);
|
||||
*
|
||||
*/
|
||||
Serial1.begin(AT_BAUD_RATE);
|
||||
WiFi.init(Serial1);
|
||||
|
||||
maxConnections = MAX_WIFI_SOCK;
|
||||
|
||||
if (WiFi.status() == WL_NO_MODULE)
|
||||
{
|
||||
ERR(F("Communication with WiFi module failed!"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
INFO(F("Waiting for connection to WiFi "));
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
delay(1000);
|
||||
DBG(F("."));
|
||||
}
|
||||
|
||||
INFO(F("Network Protocol: [%s]"), protocol ? "UDP" : "TCP");
|
||||
INFO(F("Initialize MAC Addresses ... "));
|
||||
WiFi.apMacAddress(apWifiMacAddress);
|
||||
reverseArray(apWifiMacAddress, 0, 5); // the MAc is provided in reverse order ...
|
||||
WiFi.macAddress(stWifiMacAddress);
|
||||
reverseArray(stWifiMacAddress, 0, 5);
|
||||
|
||||
// Setup the protocol handler
|
||||
switch (protocol)
|
||||
{
|
||||
case UDPR:
|
||||
{
|
||||
connected = false;
|
||||
udp = new WiFiUDP();
|
||||
byte udpState = udp->begin(port);
|
||||
if (udpState)
|
||||
{
|
||||
TRC(F("UDP status: %d"), udpState);
|
||||
maxConnections = 1; // there is only one UDP object listening for incomming data
|
||||
connected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR(F("UDP failed to start"));
|
||||
connected = false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case TCP:
|
||||
{
|
||||
server = new WiFiServer(port);
|
||||
server->begin(MAX_WIFI_SOCK, 240);
|
||||
if(server->status()) {
|
||||
connected = true;
|
||||
|
||||
} else {
|
||||
ERR(F("\nWiFi server failed to start"));
|
||||
connected = false;
|
||||
} // Connection pool not used for WiFi
|
||||
break;
|
||||
};
|
||||
case MQTT: {
|
||||
// do the MQTT setup stuff here
|
||||
};
|
||||
default:
|
||||
{
|
||||
ERR(F("Unkown Ethernet protocol; Setup failed"));
|
||||
connected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (connected)
|
||||
{
|
||||
ip = WiFi.localIP();
|
||||
NetworkSetup::printMacAddress(apWifiMacAddress);
|
||||
NetworkSetup::printMacAddress(stWifiMacAddress);
|
||||
|
||||
INFO(F("Local IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
||||
INFO(F("Listening on port: [%d]"), port);
|
||||
dnsip = WiFi.dnsServer1();
|
||||
INFO(F("DNS server IP address: [%d.%d.%d.%d] "), dnsip[0], dnsip[1], dnsip[2], dnsip[3]);
|
||||
INFO(F("Number of connections: [%d]"), maxConnections);
|
||||
return true;
|
||||
}
|
||||
return false; // something went wrong
|
||||
|
||||
};
|
||||
|
||||
void reverseArray(uint8_t arr[], int start, int end)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
uint8_t temp = arr[start];
|
||||
arr[start] = arr[end];
|
||||
arr[end] = temp;
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
}
|
||||
|
||||
WifiSetup::WifiSetup() {}
|
||||
WifiSetup::WifiSetup(uint16_t p, protocolType pt ) { port = p; protocol = pt; }
|
||||
WifiSetup::~WifiSetup() {}
|
60
WifiSetup.h
Normal file
60
WifiSetup.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* © 2020 Gregor Baues. All rights reserved.
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* See the GNU General Public License for more details <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef WifiSetup_h
|
||||
#define WifiSetup_h
|
||||
|
||||
#include "NetworkSetup.h"
|
||||
#include "WiFiEspAT.h"
|
||||
|
||||
// Emulate Serial1 on pins 6/7 if not present
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(HAVE_HWSERIAL1)
|
||||
#include <SoftwareSerial.h>
|
||||
SoftwareSerial Serial1(6, 7); // RX, TX
|
||||
#define AT_BAUD_RATE 9600
|
||||
#else
|
||||
#define AT_BAUD_RATE 115200
|
||||
#endif
|
||||
|
||||
|
||||
class WifiSetup: public NetworkSetup {
|
||||
|
||||
private:
|
||||
|
||||
WiFiServer* server;
|
||||
WiFiUDP* udp;
|
||||
|
||||
public:
|
||||
|
||||
bool setup();
|
||||
|
||||
WiFiUDP* getUDPServer() {
|
||||
return udp;
|
||||
}
|
||||
|
||||
WiFiServer* getTCPServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
WifiSetup();
|
||||
WifiSetup(uint16_t port, protocolType protocol);
|
||||
~WifiSetup();
|
||||
};
|
||||
|
||||
#endif
|
@@ -11,7 +11,7 @@
|
||||
[platformio]
|
||||
default_envs =
|
||||
mega2560
|
||||
uno
|
||||
;uno
|
||||
src_dir = .
|
||||
|
||||
[env]
|
||||
@@ -37,8 +37,11 @@ lib_deps =
|
||||
arduino-libraries/Ethernet
|
||||
SPI
|
||||
mathertel/LiquidCrystal_PCF8574
|
||||
;jandrassy/WiFiEspAT@1.3.0
|
||||
WiFiEspAT
|
||||
monitor_speed = 115200
|
||||
monitor_flags = --echo
|
||||
build_flags =
|
||||
|
||||
[env:mega328]
|
||||
platform = atmelavr
|
||||
@@ -50,6 +53,7 @@ lib_deps =
|
||||
arduino-libraries/Ethernet
|
||||
SPI
|
||||
mathertel/LiquidCrystal_PCF8574
|
||||
jandrassy/WiFiEspAT@^1.3.0
|
||||
monitor_speed = 115200
|
||||
monitor_flags = --echo
|
||||
|
||||
@@ -63,6 +67,7 @@ lib_deps =
|
||||
arduino-libraries/Ethernet
|
||||
SPI
|
||||
mathertel/LiquidCrystal_PCF8574
|
||||
jandrassy/WiFiEspAT@^1.3.0
|
||||
monitor_speed = 115200
|
||||
monitor_flags = --echo
|
||||
|
||||
@@ -76,5 +81,6 @@ lib_deps =
|
||||
arduino-libraries/Ethernet
|
||||
SPI
|
||||
mathertel/LiquidCrystal_PCF8574
|
||||
jandrassy/WiFiEspAT@^1.3.0
|
||||
monitor_speed = 115200
|
||||
monitor_flags = --echo
|
||||
|
Reference in New Issue
Block a user