1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-27 10:06:13 +01:00
CommandStation-EX/Net_ENC28J60.h

149 lines
5.1 KiB
C
Raw Normal View History

2021-11-11 14:31:59 +01:00
/*
* © 2021, Neil McKechnie. All rights reserved.
*
* This file is part of DCC++EX API
*
* 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/>.
*/
/*
* ENC28J60 default mode of operation:
*
* The node number is used as the last byte of the MAC address
* field, purely so that the MAC address is unique for each distinct
* node number. The ENC28J60 driver doesn't implement ARP cache,
* since it would take up a minimum of 11 bytes per node and, with
* up to 254 nodes, this would take a significant part of the RAM.
* Instead, all transmissions are sent with MAC address allOnes, i.e.
* as a broadcast. Regular sensor state updates are broadcast anyway,
* and other write/writeAnalogue command packets are relatively
* infrequent.
*
* Usage:
* Net_ENC28J60 *encDriver = new Net_ENC28J60(10);
* Network<Net_ENC28J60>::create(4000, NUMREMOTEPINS(rpins), 1, rpins, encDriver);
*
* The ENC28J60 device has to be connected to the hardware MISO, MOSI, SCK and CS pins of the
* microcontroller. The CS pin is specified in the command above (e.g. 10).
* For details of the Network class configuration, see IO_Network.h.
*
*/
#ifndef NET_ENC28J60_H
#define NET_ENC28J60_H
#include "IO_Network.h"
#include "DIAG.h"
#include "EtherCard.h"
// The ethernet buffer is global to different protocol layers, to avoid almost
// all copying of data.
byte ENC28J60::buffer[74]; // Need space for 32 byte payload and 42 byte header.
class Net_ENC28J60 : public EtherCard {
private:
// pins must be arduino GPIO pins, not extender pins or HAL pins.
int _csPin;
// Number of the current node (1-254)
uint8_t _thisNode;
// 6-byte MAC address. Last byte will contain the node number (1-254).
byte _address[6] = {0xEE,0xCC,0xEE,0xEE,0xCC,0x00};
// 4-byte IP address. Last byte will contain the node number (1-254) or 255 for broadcast.
byte _ipAddress[4] = {192,168,1,0};
byte _gwAddress[4] = {192,168,1,254};
byte _netMask[4] = {255,255,255,0};
const uint16_t _port = 8900;
uint8_t _packetBytesAvailable;
public:
// Constructor performs static initialisation of the device object
Net_ENC28J60 (int csPin) {
_csPin = csPin;
_packetBytesAvailable = 0;
}
// Perform dynamic initialisation of the device
bool begin(uint8_t thisNode) {
_thisNode = thisNode;
_address[5] = _thisNode;
_ipAddress[3] = _thisNode;
if (ether.begin(sizeof(ENC28J60::buffer), _address, _csPin)) {
ether.staticSetup(_ipAddress, _gwAddress, 0, _netMask);
return true;
} else {
// Error in initialising
DIAG(F("ENC28J60 Failed to initialise"));
return false;
}
}
// The following function should be called regularly to handle incoming packets.
// Check if there is a received packet ready for reading.
bool available() {
uint16_t packetLen = ether.packetReceive();
if (packetLen > 0) {
// Packet received. First handle ICMP, ARP etc.
if (ether.packetLoop(packetLen)) {
// UDP packet to be handled. Check if it's our port number
byte *gbp = ENC28J60::buffer;
uint16_t destPort = (gbp[UDP_DST_PORT_H_P] << 8) + gbp[UDP_DST_PORT_L_P];
if (destPort == _port) {
// Yes, so mark that data is to be processed.
_packetBytesAvailable = packetLen;
return true;
}
}
}
_packetBytesAvailable = 0;
return false;
}
// Read packet from the ethernet buffer, and return the number of bytes
// that were read.
uint8_t read(uint8_t buffer[], uint8_t bufferSize) {
if (_packetBytesAvailable > 0) {
//DIAG(F("ReadPacket(%d)"), _packetBytesAvailable);
// Adjust length and pointer for UDP header
byte *gbp = ENC28J60::buffer;
int udpDataSize = (gbp[UDP_LEN_H_P] << 8) + gbp[UDP_LEN_L_P] - UDP_HEADER_LEN;
byte *udpFrame = &ENC28J60::buffer[UDP_DATA_P];
// Clear packet byte marker
_packetBytesAvailable = 0;
// Check if there's room for the data
if (bufferSize >= udpDataSize) {
memcpy(buffer, udpFrame, udpDataSize);
return udpDataSize;
}
}
return 0;
}
// Wrapper functions for ENC28J60 sendUdp function.
// The node parameter is either 1-254 (for specific nodes) or 255 (for broadcast).
// This aligns with the subnet broadcast IP address of "x.x.x.255".
bool sendCommand(uint8_t node, const uint8_t buffer[], uint8_t len) {
_ipAddress[3] = node;
ether.sendUdp((const char *)buffer, len, _port, _ipAddress, _port);
return true;
}
void loop() { }
};
#endif //NET_ENC28J60_H