|
|
|
/*
|
|
|
|
|
|
|
|
Code to test wiznet WIZ810MJ module
|
|
|
|
|
|
|
|
See:
|
|
|
|
|
|
|
|
<http://code.rancidbacon.com/LearningAboutArduinoWIZ810MJ>
|
|
|
|
|
|
|
|
Current features:
|
|
|
|
|
|
|
|
* Initial W5100 driver port:
|
|
|
|
|
|
|
|
+ new-style network configuration
|
|
|
|
|
|
|
|
+ socket creation/listening/closing
|
|
|
|
|
|
|
|
+ Sending/Receiving okay
|
|
|
|
|
|
|
|
+ example "echo" server (new version with classes and multiple connections)
|
|
|
|
|
|
|
|
+ example "web server" with LED flash. (not currently)
|
|
|
|
|
|
|
|
* (slighty less) Terrible hacked-together code
|
|
|
|
|
|
|
|
Author:
|
|
|
|
|
|
|
|
follower@rancidbacon.com
|
|
|
|
|
|
|
|
License:
|
|
|
|
|
|
|
|
LGPL
|
|
|
|
|
|
|
|
Version:
|
|
|
|
|
|
|
|
20071220-0006+
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <WIZ810MJ.h>
|
|
|
|
|
|
|
|
|
|
|
|
/* --------- SPI --------- */
|
|
|
|
|
|
|
|
// Define SPI-related pins
|
|
|
|
#define PIN_DATA_OUT 11 // MOSI (Master Out / Slave In)
|
|
|
|
#define PIN_DATA_IN 12 // MISO (Master In / Slave Out)
|
|
|
|
#define PIN_SPI_CLOCK 13 // SCK (Serial Clock)
|
|
|
|
#define PIN_SLAVE_SELECT 10 // SS (Slave Select)
|
|
|
|
|
|
|
|
class SpiConfiguration {
|
|
|
|
|
|
|
|
public:
|
|
|
|
static void begin(void);
|
|
|
|
};
|
|
|
|
|
|
|
|
void SpiConfiguration::begin(void) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
Configure pins and registers required for SPI communication.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Configure I/O pins
|
|
|
|
pinMode(PIN_DATA_OUT, OUTPUT);
|
|
|
|
pinMode(PIN_DATA_IN, INPUT);
|
|
|
|
pinMode(PIN_SPI_CLOCK, OUTPUT);
|
|
|
|
pinMode(PIN_SLAVE_SELECT, OUTPUT);
|
|
|
|
|
|
|
|
digitalWrite(PIN_SLAVE_SELECT, HIGH); // Disable slave
|
|
|
|
|
|
|
|
/*
|
|
|
|
Configure SPI Control Register (SPCR) (All values initially 0)
|
|
|
|
Bit Description
|
|
|
|
7 SPI Interrupt Enable -- disable (SPIE --> 0)
|
|
|
|
6 SPI Enable -- enable (SPE --> 1)
|
|
|
|
5 Data Order -- MSB 1st (DORD --> 0) (Slave specific)
|
|
|
|
4 Master/Slave Select -- master (MSTR --> 1)
|
|
|
|
3 Clock Polarity -- (CPOL --> 0) (Slave specific) ("Mode")
|
|
|
|
2 Clock Phase -- (CPHA --> 0) (Slave specific)
|
|
|
|
1 SPI Clock Rate Select 1 -- } (SPR1 --> 0)
|
|
|
|
0 SPI Clock Rate Select 0 -- } fOSC/4 (SPR0 --> 0) ("Fastest" but see SPI2X in SPSR)
|
|
|
|
*/
|
|
|
|
SPCR = (1<<SPE)| (1<<MSTR);
|
|
|
|
|
|
|
|
// Clear previous data and status (TODO: Determine if necessary/better way.)
|
|
|
|
// (Based on Playground SPI example.)
|
|
|
|
byte dummy;
|
|
|
|
dummy = SPSR;
|
|
|
|
dummy = SPDR;
|
|
|
|
delay(10);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
SpiConfiguration SPI = SpiConfiguration();
|
|
|
|
|
|
|
|
/* ----------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
/* ------ WIZ810MJ ------ */
|
|
|
|
|
|
|
|
class Wiz810MjDevice {
|
|
|
|
|
|
|
|
public:
|
|
|
|
Wiz810MjDevice(int resetPin);
|
|
|
|
|
|
|
|
void setIp(byte b0, byte b1, byte b2, byte b3);
|
|
|
|
void setMask(byte b0, byte b1, byte b2, byte b3);
|
|
|
|
void setGateway(byte b0, byte b1, byte b2, byte b3);
|
|
|
|
void setMac(byte b0, byte b1, byte b2, byte b3, byte b4, byte b5);
|
|
|
|
|
|
|
|
private:
|
|
|
|
void _init(void);
|
|
|
|
byte * _packBuffer(byte b0, byte b1, byte b2, byte b3);
|
|
|
|
byte * _packBuffer(byte b0, byte b1, byte b2, byte b3, byte b4, byte b5);
|
|
|
|
|
|
|
|
int _resetPin;
|
|
|
|
|
|
|
|
byte _scratchBuffer[6]; // Can we make this static?
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Wiz810MjDevice::Wiz810MjDevice(int resetPin) {
|
|
|
|
// TODO: We should really allow the chip-select pin to be set here?
|
|
|
|
// Or require that it's defined. (Currently in the library file 'types.h'.)
|
|
|
|
_resetPin = resetPin;
|
|
|
|
|
|
|
|
_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Wiz810MjDevice::_init(void) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
Initialise the WIZ810MJ module and driver.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Initialise the W5100 chip
|
|
|
|
|
|
|
|
(Originally I thought it was possible for the chip
|
|
|
|
to function without a hardware reset but it
|
|
|
|
seems not to be the case.)
|
|
|
|
*/
|
|
|
|
pinMode(_resetPin, OUTPUT);
|
|
|
|
|
|
|
|
// We rely on the time between function calls to
|
|
|
|
// be long enough for the chip to recognise the
|
|
|
|
// reset.
|
|
|
|
digitalWrite(_resetPin, HIGH);
|
|
|
|
digitalWrite(_resetPin, LOW); // reset
|
|
|
|
digitalWrite(_resetPin, HIGH);
|
|
|
|
|
|
|
|
// Chip initialisation by driver
|
|
|
|
// Might be redundant following the above reset,
|
|
|
|
// as this performs a software reset.
|
|
|
|
iinchip_init();
|
|
|
|
|
|
|
|
// Initialise driver
|
|
|
|
// (This is required to configure some variables used
|
|
|
|
// internally by the driver--even if the default chip
|
|
|
|
// configuration is used.)
|
|
|
|
sysinit(0x55, 0x55);
|
|
|
|
}
|
|
|
|
|
|
|
|
byte * Wiz810MjDevice::_packBuffer(byte b0, byte b1, byte b2, byte b3) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
return _packBuffer(b0, b1, b2, b3, 0, 0); // Adds two bytes of padding
|
|
|
|
}
|
|
|
|
|
|
|
|
byte * Wiz810MjDevice::_packBuffer(byte b0, byte b1, byte b2, byte b3, byte b4, byte b5) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
_scratchBuffer[0] = b0;
|
|
|
|
_scratchBuffer[1] = b1;
|
|
|
|
_scratchBuffer[2] = b2;
|
|
|
|
_scratchBuffer[3] = b3;
|
|
|
|
_scratchBuffer[4] = b4;
|
|
|
|
_scratchBuffer[5] = b5;
|
|
|
|
|
|
|
|
return _scratchBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Wiz810MjDevice::setIp(byte b0, byte b1, byte b2, byte b3) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
setSIPR(_packBuffer(b0, b1, b2, b3));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wiz810MjDevice::setMask(byte b0, byte b1, byte b2, byte b3) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
setSUBR(_packBuffer(b0, b1, b2, b3));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wiz810MjDevice::setGateway(byte b0, byte b1, byte b2, byte b3) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
setGAR(_packBuffer(b0, b1, b2, b3));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wiz810MjDevice::setMac(byte b0, byte b1, byte b2, byte b3, byte b4, byte b5) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
setSHAR(_packBuffer(b0, b1, b2, b3, b4, b5));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------- */
|
|
|
|
|
|
|
|
/* ---- NetworkConnection ---- */
|
|
|
|
|
|
|
|
// TODO: Make this 'NetworkServerConnection'? Or just 'ServerConnection'?
|
|
|
|
// TODO: Pull one-line methods into class definition to allow inlining?
|
|
|
|
class NetworkConnection { // Essentially a Socket wrapper
|
|
|
|
|
|
|
|
public:
|
|
|
|
// TODO: Split into client/server connections? Subclass?
|
|
|
|
NetworkConnection(uint16_t port); // TODO: Add UDP, TCP choice? // For servers
|
|
|
|
NetworkConnection(); // For clients--is using the default constructor hide misuse? TODO: As above.
|
|
|
|
|
|
|
|
int listen();
|
|
|
|
int connect(uint8 * addr, uint16 port);
|
|
|
|
int isConnected();
|
|
|
|
int available();
|
|
|
|
int read();
|
|
|
|
void print(uint8_t);
|
|
|
|
void print(const char * text);
|
|
|
|
void close();
|
|
|
|
|
|
|
|
private:
|
|
|
|
SOCKET _socket;
|
|
|
|
static const int _MAX_SOCKETS = MAX_SOCK_NUM; // TODO: Use this.
|
|
|
|
static int _nextSocket;
|
|
|
|
};
|
|
|
|
|
|
|
|
int NetworkConnection::_nextSocket = 0;
|
|
|
|
|
|
|
|
NetworkConnection::NetworkConnection(uint16_t port) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
_socket = _nextSocket; // TODO: Do properly (& max)
|
|
|
|
_nextSocket++;
|
|
|
|
socket(_socket, Sn_MR_TCP, port, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
NetworkConnection::NetworkConnection() {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
NetworkConnection(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int NetworkConnection::listen() { // TODO: Make private or protected?
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
return !!::listen(_socket); // TODO: Use C++ namespaces for the driver functions?
|
|
|
|
}
|
|
|
|
|
|
|
|
int NetworkConnection::connect(uint8 * addr, uint16 port) { // TODO: Make private or protected?
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// TODO: Accept bytes here for addr?
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
result = !!::connect(_socket, addr, port); // TODO: Use C++ namespaces for the driver functions?
|
|
|
|
Serial.println(IINCHIP_READ(Sn_DPORT0(_socket)), HEX);
|
|
|
|
Serial.println(IINCHIP_READ(Sn_DPORT0(_socket) + 1), HEX);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int NetworkConnection::available() {
|
|
|
|
/*
|
|
|
|
|
|
|
|
Functionality matches 'Serial.available()'.
|
|
|
|
|
|
|
|
Note: If the socket is not connected then this will return 0,
|
|
|
|
but it is intended that 'isConnected()' would be checked first.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
"The number of bytes available to read ... or 0 if none are available.
|
|
|
|
If any data has come in, [...].available() will be greater than 0."
|
|
|
|
|
|
|
|
*/
|
|
|
|
// TODO: Do we want to check for 'isConnected' as well, or not?
|
|
|
|
// We have to, I guess, for valid behaviour with 'getSn_*'.
|
|
|
|
if (!isConnected()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return getSn_RX_RSR(_socket);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NO_READ_DATA_AVAILABLE -1
|
|
|
|
|
|
|
|
int NetworkConnection::read() {
|
|
|
|
/*
|
|
|
|
|
|
|
|
Functionality matches 'Serial.read()'.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
"an int, the first byte of incoming serial data available
|
|
|
|
(or -1 if no data is available)."
|
|
|
|
|
|
|
|
|
|
|
|
Note: I thought 'recv' blocked until data was available,
|
|
|
|
but it currently seems not to block after all.
|
|
|
|
However, because I'm seeking to match the 'Serial.read'
|
|
|
|
behaviour we don't actually want to block anyway,
|
|
|
|
so we'll ignore it for the moment and handle things
|
|
|
|
ourself for now.
|
|
|
|
|
|
|
|
*/
|
|
|
|
uint8_t theByte;
|
|
|
|
|
|
|
|
if (!available()) {
|
|
|
|
return NO_READ_DATA_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
recv(_socket, &theByte, 1);
|
|
|
|
return theByte;
|
|
|
|
}
|
|
|
|
|
|
|
|
int NetworkConnection::isConnected() {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
// TODO: If we want the 'Network*' classes to be generic we
|
|
|
|
// would need to handle this differently:
|
|
|
|
//Serial.println(getSn_SR(_socket), HEX);
|
|
|
|
return (getSn_SR(_socket) == SOCK_ESTABLISHED);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetworkConnection::print(uint8_t b) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
if (isConnected()) {
|
|
|
|
send(_socket, &b, 1);
|
|
|
|
} else {
|
|
|
|
// Just drop it if we're not connected.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetworkConnection::print(const char * text) {
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
for (int idx = 0; idx < strlen(text); idx++) {
|
|
|
|
print(text[idx]);
|
|
|
|
Serial.print(text[idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetworkConnection::close() {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
// TODO: Determine if we need/want the disconnect (see pg 26 W5100)
|
|
|
|
::close(_socket);
|
|
|
|
disconnect(_socket);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------- */
|
|
|
|
|
|
|
|
/* -- NetworkInterface -- */
|
|
|
|
|
|
|
|
#define HANDLE_BAD_ERROR() while (1) {};
|
|
|
|
|
|
|
|
// TODO?: Do we want to have a "default" socket accessible by 'Network.read()' etc?
|
|
|
|
class NetworkInterface {
|
|
|
|
|
|
|
|
public:
|
|
|
|
NetworkInterface(Wiz810MjDevice& networkDevice);
|
|
|
|
|
|
|
|
NetworkConnection listen(uint16_t port);
|
|
|
|
NetworkConnection connect(byte b0, byte b1, byte b2, byte b3, uint16 port);
|
|
|
|
|
|
|
|
Wiz810MjDevice& device; // TODO: Make this a generic "network device" interface
|
|
|
|
};
|
|
|
|
|
|
|
|
NetworkInterface::NetworkInterface(Wiz810MjDevice& networkDevice) : device (networkDevice) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
Note: The "weirdness" in this function declaration is a "initalization list",
|
|
|
|
i.e. this bit:
|
|
|
|
|
|
|
|
) : device (networkDevice) {
|
|
|
|
|
|
|
|
it is needed to give the 'device' Reference a value supplied to the method
|
|
|
|
rather than having a new 'Wiz810MjDevice' instance created.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// This may not be the best place to do all this, but it'll do for now:
|
|
|
|
device.setMac(0x02,0xDE,0xAD,0xBE,0xEF,0x00);
|
|
|
|
|
|
|
|
device.setIp(169,254,254,169); // A local-link IP -- NOTE: This is out of spec.
|
|
|
|
device.setMask(255,255,0,0); // Matches local-link IP /16
|
|
|
|
|
|
|
|
device.setGateway(0,0,0,0); // We can't even guess this, so just skip it.
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
NetworkConnection NetworkInterface::listen(uint16_t port) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
|
|
NOTE: For reasons I don't understand, this FAILS TO WORK if port >255.
|
|
|
|
The port that gets opened is ~(port mod 255) but if you limit
|
|
|
|
port to be uint8_t for example you don't get an error until
|
|
|
|
the port number is bigger than 'long'.
|
|
|
|
|
|
|
|
TODO: Track and fix this for port numbers > 255.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Returns a NetworkConnection listening on the specified TCP port.
|
|
|
|
|
|
|
|
*/
|
|
|
|
NetworkConnection connection = NetworkConnection(port);
|
|
|
|
|
|
|
|
// TODO: How to best handle errors?
|
|
|
|
// There's two major categories I can think of currently:
|
|
|
|
// 1) A socket wasn't available to listen in the first place
|
|
|
|
// 2) The listen attempt failed for some reason
|
|
|
|
// Using 'HANDLE_BAD_ERROR()' (currently an infinite loop) is
|
|
|
|
// not ideal but should prevent the above errors slipping by unnoticed.
|
|
|
|
if (!connection.listen()) {
|
|
|
|
HANDLE_BAD_ERROR();
|
|
|
|
}
|
|
|
|
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
NetworkConnection NetworkInterface::connect(byte b0, byte b1, byte b2, byte b3, uint16 port) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
NetworkConnection connection = NetworkConnection(4000);
|
|
|
|
|
|
|
|
byte _scratchBuffer[4]; // TODO: Move this?
|
|
|
|
|
|
|
|
// TODO: Better?
|
|
|
|
_scratchBuffer[0] = b0;
|
|
|
|
_scratchBuffer[1] = b1;
|
|
|
|
_scratchBuffer[2] = b2;
|
|
|
|
_scratchBuffer[3] = b3;
|
|
|
|
|
|
|
|
if (!connection.connect(_scratchBuffer, port)) {
|
|
|
|
HANDLE_BAD_ERROR();
|
|
|
|
}
|
|
|
|
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NetworkInterface Network = NetworkInterface(...); // TODO: Make this a global
|
|
|
|
|
|
|
|
/* ----------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
/* ----- EchoServer ------ */
|
|
|
|
|
|
|
|
#define ECHO_CONNECT_WAIT 0
|
|
|
|
#define ECHO_CONNECTED 1
|
|
|
|
#define ECHO_CLOSE 2
|
|
|
|
#define ECHO_HALT 3
|
|
|
|
|
|
|
|
class EchoServer {
|
|
|
|
|
|
|
|
public:
|
|
|
|
EchoServer(int port);
|
|
|
|
void next(); // TODO: Return something useful?
|
|
|
|
|
|
|
|
private:
|
|
|
|
NetworkConnection _connection; // TODO: Make public?
|
|
|
|
int _state;
|
|
|
|
};
|
|
|
|
|
|
|
|
EchoServer::EchoServer(int port) : _connection (NetworkConnection(port)) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
_state = ECHO_CONNECT_WAIT;
|
|
|
|
|
|
|
|
_connection.listen(); // TODO: We should be using Network.listen(...) here and in initialisation list.
|
|
|
|
}
|
|
|
|
|
|
|
|
void EchoServer::next() {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// TODO: Use better state machine implementation?
|
|
|
|
|
|
|
|
if (_state == ECHO_CONNECT_WAIT) {
|
|
|
|
|
|
|
|
if (_connection.isConnected()) {
|
|
|
|
_state = ECHO_CONNECTED;
|
|
|
|
} else {
|
|
|
|
// Keep waiting
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (_state == ECHO_CONNECTED) {
|
|
|
|
|
|
|
|
if (_connection.available()) {
|
|
|
|
_connection.print(_connection.read());
|
|
|
|
} else if (!_connection.isConnected()) {
|
|
|
|
// Data finished and client disconnected
|
|
|
|
_state == ECHO_CLOSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (_state == ECHO_CLOSE) {
|
|
|
|
|
|
|
|
_connection.close();
|
|
|
|
_state = ECHO_HALT;
|
|
|
|
|
|
|
|
} else if (_state == ECHO_HALT) {
|
|
|
|
// Do nothing
|
|
|
|
} else {
|
|
|
|
// Unknown state, do nothing.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------- */
|
|
|
|
|
|
|
|
// #define PIN_RESET 9 // WIZnet module /RESET
|
|
|
|
|
|
|
|
#define PIN_RESET 8 // WIZnet module /RESET
|
|
|
|
|
|
|
|
SOCKET testSocket;
|
|
|
|
byte ip[6];
|
|
|
|
|
|
|
|
|
|
|
|
#define PIN_LED 2
|
|
|
|
|
|
|
|
//------
|
|
|
|
// Note: Probably not efficient or anything...
|
|
|
|
class StreamConnection {
|
|
|
|
|
|
|
|
public:
|
|
|
|
StreamConnection(NetworkConnection& connection);
|
|
|
|
int peekByte();
|
|
|
|
int readByte();
|
|
|
|
int skipSegment(const char * separators); // TODO: Return if separators found or not?
|
|
|
|
int readSegmentByte(const char * separators);
|
|
|
|
int debug;
|
|
|
|
int gobbleMatch(const char * separators, const char * target);
|
|
|
|
|
|
|
|
private:
|
|
|
|
NetworkConnection& _connection;
|
|
|
|
int _byteSeen;
|
|
|
|
};
|
|
|
|
|
|
|
|
StreamConnection::StreamConnection(NetworkConnection& connection) : _connection (connection) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
|
|
_byteSeen = -1;
|
|
|
|
debug = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int StreamConnection::peekByte() {
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
if (_byteSeen < 0) {
|
|
|
|
if (_connection.available()) {
|
|
|
|
_byteSeen = _connection.read();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return _byteSeen;
|
|
|
|
}
|
|
|
|
|
|
|
|
int StreamConnection::readByte() {
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
int newByte = -1;
|
|
|
|
|
|
|
|
if (_byteSeen < 0) {
|
|
|
|
if (_connection.available()) {
|
|
|
|
newByte = _connection.read();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newByte = _byteSeen;
|
|
|
|
_byteSeen = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug == 1) {
|
|
|
|
Serial.print(newByte, BYTE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return newByte;
|
|
|
|
}
|
|
|
|
|
|
|
|
int StreamConnection::readSegmentByte(const char * separators) {
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
// Blocks
|
|
|
|
while (peekByte() < 0) { // TODO: Time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
if (!strchr(separators, peekByte())) {
|
|
|
|
return readByte();
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int StreamConnection::skipSegment(const char * separators) {
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (debug == 1) {
|
|
|
|
Serial.println("Start skipping non-separator");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (peekByte() < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// Skip everything not a separator
|
|
|
|
} while (!strchr(separators, peekByte()) && readByte());
|
|
|
|
|
|
|
|
if (debug == 1) {
|
|
|
|
Serial.println("Finished skipping non-separator");
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* // We can't return here or we get stuck above.
|
|
|
|
if (peekByte() < 0) {
|
|
|
|
return -1;
|
|
|
|
}*/
|
|
|
|
while (peekByte() < 0) {
|
|
|
|
delay(500); // TODO: Time out?
|
|
|
|
}
|
|
|
|
// Skip everything that *is* a separator
|
|
|
|
} while (strchr(separators, peekByte()) && readByte());
|
|
|
|
|
|
|
|
if (debug == 1) {
|
|
|
|
Serial.println("Finished skipping separator");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int StreamConnection::gobbleMatch(const char * separators, const char * target) {
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
|
|
|
int idx = 0;
|
|
|
|
int byteRead = -1;
|
|
|
|
|
|
|
|
while ((byteRead = readSegmentByte(separators)) >=0) {
|
|
|
|
if (idx < strlen(target)) {
|
|
|
|
if (byteRead != target[idx]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((byteRead == -1) && (idx == strlen(target))) { // Matched
|
|
|
|
while (skipSegment(separators) < 0) {
|
|
|
|
// Ummm, we ran outta data--this screws things up...
|
|
|
|
// TODO: Have a time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
//------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void setup () {
|
|
|
|
Serial.begin(9600);
|
|
|
|
Serial.println("Setup enter...");
|
|
|
|
|
|
|
|
Serial.println("Start W5100 configuration...");
|
|
|
|
|
|
|
|
SPI.begin();
|
|
|
|
|
|
|
|
Wiz810MjDevice WIZ810MJ = Wiz810MjDevice(PIN_RESET);
|
|
|
|
|
|
|
|
NetworkInterface Network = NetworkInterface(WIZ810MJ);
|
|
|
|
|
|
|
|
Network.device.setIp(192,168,2,105);
|
|
|
|
Network.device.setMask(255,255,255,0);
|
|
|
|
|
|
|
|
Network.device.setGateway(192,168,2,101);
|
|
|
|
|
|
|
|
Serial.println("End W5100 configuration...");
|
|
|
|
|
|
|
|
pinMode(PIN_LED, OUTPUT);
|
|
|
|
digitalWrite(PIN_LED, HIGH);
|
|
|
|
|
|
|
|
Serial.println("Setup exit...");
|
|
|
|
|
|
|
|
// Serial.println(((analogRead(2)*70)/630)/10+'0', BYTE);
|
|
|
|
|
|
|
|
/**/
|
|
|
|
Serial.println("Test sockets...");
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
#if 1 //b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NetworkConnection conn = Network.connect(209,177,146,34, 6667); // TODO: Better?
|
|
|
|
|
|
|
|
Serial.println("Waiting to connect...");
|
|
|
|
|
|
|
|
while (!conn.isConnected()) {
|
|
|
|
//Serial.print(!conn.isConnected());
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.println("Connected...");
|
|
|
|
|
|
|
|
int byteRead = -1;
|
|
|
|
int dataWait = 0;
|
|
|
|
|
|
|
|
StreamConnection stream = StreamConnection(conn);
|
|
|
|
|
|
|
|
conn.print("NICK Arduino\n");
|
|
|
|
conn.print("USER Arduino 0 0 Arduino\n");
|
|
|
|
|
|
|
|
byte buf[4] = {0, 0, 0, 0};
|
|
|
|
|
|
|
|
int state = 0;
|
|
|
|
|
|
|
|
while (conn.isConnected()) {
|
|
|
|
#if 1 //c
|
|
|
|
|
|
|
|
if (stream.peekByte() >= 0) {
|
|
|
|
|
|
|
|
// New line
|
|
|
|
//Serial.println("Process new line...");
|
|
|
|
if (stream.peekByte() != ':') {
|
|
|
|
// Skip local responses/messages entirely. TODO: Handle PINGs?
|
|
|
|
while (stream.skipSegment("\x0A\x0D") < 0) {
|
|
|
|
// Ummm, we ran outta data--this screws things up...
|
|
|
|
// TODO: Have a time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//Serial.println(state, HEX);
|
|
|
|
if (state == 0) {
|
|
|
|
// Skip sending servername // TODO: Check it's the main one?
|
|
|
|
while (stream.skipSegment(" ") < 0) {
|
|
|
|
// Ummm, we ran outta data--this screws things up...
|
|
|
|
// TODO: Have a time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int idx=0; idx < 3; idx++) {
|
|
|
|
while (stream.peekByte() < 0) { // TODO: Time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
buf[idx] = stream.readByte();
|
|
|
|
//Serial.print(buf[idx], BYTE);
|
|
|
|
}
|
|
|
|
//Serial.print("//");
|
|
|
|
Serial.println((const char *) buf);
|
|
|
|
|
|
|
|
if (strcmp((const char *) buf, "376") == 0) { // End MOTD
|
|
|
|
state=1;
|
|
|
|
conn.print("JOIN #arduino\n");
|
|
|
|
//stream.debug = 1;
|
|
|
|
}
|
|
|
|
} else if (state == 1) {
|
|
|
|
//stream.debug = 1;
|
|
|
|
//Serial.println("Skip servername");
|
|
|
|
// Skip sending servername // TODO: Check it's the main one?
|
|
|
|
while (stream.skipSegment(" ") < 0) {
|
|
|
|
// Ummm, we ran outta data--this screws things up...
|
|
|
|
// TODO: Have a time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
//stream.debug = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
int idx = 0;
|
|
|
|
const char * PRIVMSG = "PRIVMSG";
|
|
|
|
//Serial.println("Checking for PRIVMSG.");
|
|
|
|
|
|
|
|
//stream.debug = 1;
|
|
|
|
|
|
|
|
while ((byteRead = stream.readSegmentByte(" ")) >=0) {
|
|
|
|
if (idx < strlen(PRIVMSG)) {
|
|
|
|
if (byteRead != PRIVMSG[idx]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// stream.debug = 0;
|
|
|
|
|
|
|
|
|
|
|
|
//Serial.println("Finished check PRIVMSG, determining result.");
|
|
|
|
|
|
|
|
if ((byteRead == -1) && (idx == strlen(PRIVMSG))) { // Matched
|
|
|
|
//Serial.println("Matched.");
|
|
|
|
while (stream.skipSegment(" ") < 0) {
|
|
|
|
// Ummm, we ran outta data--this screws things up...
|
|
|
|
// TODO: Have a time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (stream.gobbleMatch(" ", "PRIVMSG") > 0) {
|
|
|
|
/* **
|
|
|
|
while ((byteRead = stream.readSegmentByte("\x0A\x0D")) >=0) {
|
|
|
|
Serial.print(byteRead, BYTE);
|
|
|
|
}
|
|
|
|
Serial.println("");
|
|
|
|
*/
|
|
|
|
Serial.println("Matched PRIV MSG");
|
|
|
|
if (stream.gobbleMatch(" :", "#arduino") > 0) { // We treat the ":" as a separator too--does this break?
|
|
|
|
Serial.println("Matched #arduino");
|
|
|
|
//Serial.println(stream.peekByte(), HEX);
|
|
|
|
//if (stream.gobbleMatch("\x0A\x0D", "arduino:") > 0) {
|
|
|
|
//if (stream.gobbleMatch(":", "Arduino") > 0) {
|
|
|
|
//Serial.println("Matched Arduino");
|
|
|
|
if ((stream.peekByte() == 'A') && (stream.gobbleMatch(":", "Arduino") > 0)) {
|
|
|
|
Serial.println("Matched something");
|
|
|
|
//conn.print("PRIVMSG #arduino :Huh, what? Was someone talking to me?\n");
|
|
|
|
conn.print("PRIVMSG #arduino :My current pot status is ");
|
|
|
|
conn.print(((analogRead(2)*70)/630)/10+'0');
|
|
|
|
conn.print("3%, man!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
//Serial.println("No match.");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
while ((byteRead = stream.readSegmentByte("\x0A\x0D")) >=0) {
|
|
|
|
Serial.print(byteRead, BYTE);
|
|
|
|
}
|
|
|
|
Serial.println("");
|
|
|
|
}
|
|
|
|
//Serial.println("Skipping this:");
|
|
|
|
//stream.debug = 1;
|
|
|
|
while (stream.skipSegment("\x0A\x0D") < 0) {
|
|
|
|
// Ummm, we ran outta data--this screws things up...
|
|
|
|
// TODO: Have a time out?
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
//stream.debug = 0;
|
|
|
|
//Serial.println("\nFinish skipping.");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#else //c
|
|
|
|
if (conn.available()) {
|
|
|
|
byteRead = conn.read();
|
|
|
|
Serial.print(byteRead, BYTE);
|
|
|
|
if (byteRead == '\n') {
|
|
|
|
dataWait++;
|
|
|
|
Serial.println(dataWait, DEC);
|
|
|
|
}
|
|
|
|
if (dataWait==4) {
|
|
|
|
//conn.print("NICK Arduino\n");
|
|
|
|
//conn.print("USER Arduino Arduino Arduino Arduino\n");
|
|
|
|
dataWait++;
|
|
|
|
}
|
|
|
|
if (dataWait==67) {
|
|
|
|
conn.print("JOIN #arduino\n");
|
|
|
|
//conn.print("PRIVMSG #arduino :Hello, world!\n");
|
|
|
|
//delay(10000);
|
|
|
|
conn.print("QUIT\n");
|
|
|
|
dataWait++;
|
|
|
|
}
|
|
|
|
//conn.print(conn.read());
|
|
|
|
} else {
|
|
|
|
//dataWait++;
|
|
|
|
//Serial.print(dataWait, DEC);
|
|
|
|
}
|
|
|
|
#endif //c
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.println("");
|
|
|
|
|
|
|
|
conn.close();
|
|
|
|
|
|
|
|
|
|
|
|
#else //b
|
|
|
|
NetworkConnection conn = Network.listen(7);
|
|
|
|
|
|
|
|
Serial.println("Waiting for connection...");
|
|
|
|
|
|
|
|
while (!conn.isConnected()) {
|
|
|
|
//Serial.print(!conn.isConnected());
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.println("Connected...");
|
|
|
|
|
|
|
|
while (conn.isConnected()) {
|
|
|
|
if (conn.available()) {
|
|
|
|
// Serial.print(conn.read(), BYTE);
|
|
|
|
conn.print(conn.read());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.println("");
|
|
|
|
|
|
|
|
conn.close();
|
|
|
|
#endif //b
|
|
|
|
#else
|
|
|
|
|
|
|
|
// This uses the 1-argument constructor of EchoServer and supplies it with 7 for each instance.
|
|
|
|
EchoServer servers[MAX_SOCK_NUM] = {7, 7, 7, 7}; // This will break if MAX_SOCK_NUM changes.
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
for (int i=0; i<MAX_SOCK_NUM; i++) {
|
|
|
|
servers[i].next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
Serial.println("End test and dummy loop");
|
|
|
|
while (1) {}
|
|
|
|
/**/
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
void sendBanner(uint8_t *buffer, int ledState) { // {Socket targetSocket, ) {
|
|
|
|
|
|
|
|
//strcpy((char *) buffer, "Content-Type: text/plain\n\nfoo!\n");
|
|
|
|
strcpy((char *) buffer, "HTTP/1.1 200 OK\nContent-Type: text/html\n\n<html><body bgcolor='#000000'>foo!</body></html>\n");
|
|
|
|
if (ledState) {
|
|
|
|
buffer[63] = 'F';
|
|
|
|
} else {
|
|
|
|
buffer[65] = 'F';
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.print("send result: ");
|
|
|
|
Serial.println(send(testSocket, buffer, strlen((char *)buffer)), DEC);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t readByte() {
|
|
|
|
uint8_t theByte;
|
|
|
|
recv(testSocket, &theByte, 1);
|
|
|
|
return theByte;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int readMatch(char *stringToMatch) {
|
|
|
|
/*
|
|
|
|
|
|
|
|
Routine to read and match bytes received.
|
|
|
|
|
|
|
|
(Essentially strcmp replacement without requiring a large receive buffer.)
|
|
|
|
|
|
|
|
NOTE: Failed matches drop all bytes read so far. (i.e. you can't check for
|
|
|
|
a bunch of possible matches from the same starting position).
|
|
|
|
TODO: Fix this.
|
|
|
|
|
|
|
|
Note: This blocks if there isn't enough data in the rx buffer.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (getSn_RX_RSR(testSocket) < strlen(stringToMatch)) {
|
|
|
|
// block
|
|
|
|
// TODO: Return error or wait or "too short"?
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Do fancy string-matching techniques to avoid reading the whole string
|
|
|
|
// on non-matches. :-)
|
|
|
|
for (int currCharIdx = 0; currCharIdx < strlen(stringToMatch); currCharIdx++) {
|
|
|
|
if (readByte() != stringToMatch[currCharIdx]) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
Serial.println("Test W5100 socket...");
|
|
|
|
|
|
|
|
Serial.print("Create socket result: ");
|
|
|
|
Serial.println(socket(testSocket, Sn_MR_TCP, 80, 0), DEC);
|
|
|
|
|
|
|
|
Serial.print("Socket status: ");
|
|
|
|
Serial.println(IINCHIP_READ(Sn_SR(testSocket)), HEX);
|
|
|
|
|
|
|
|
if (IINCHIP_READ(Sn_SR(testSocket)) == SOCK_CLOSED) {
|
|
|
|
Serial.println("Socket still closed, waiting...");
|
|
|
|
while (IINCHIP_READ(Sn_SR(testSocket)) == SOCK_CLOSED) {
|
|
|
|
//pass
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.print("Listen on socket result: ");
|
|
|
|
Serial.println(listen(testSocket), DEC);
|
|
|
|
|
|
|
|
Serial.println("Waiting for connection...");
|
|
|
|
|
|
|
|
while (getSn_SR(testSocket) == SOCK_LISTEN) {
|
|
|
|
delay(500);
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.println(getSn_SR(testSocket),HEX);
|
|
|
|
|
|
|
|
getSn_DIPR(testSocket, ip);
|
|
|
|
|
|
|
|
Serial.print("Destination IP read (last digit): ");
|
|
|
|
Serial.println(ip[3], DEC);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Avoid buffer overflows...
|
|
|
|
#define MAX_TX_BUFFER_SIZE 200
|
|
|
|
uint8_t bytesToSend[MAX_TX_BUFFER_SIZE];
|
|
|
|
|
|
|
|
//sendBanner(bytesReceived);
|
|
|
|
|
|
|
|
int dataLength = 0;
|
|
|
|
|
|
|
|
#define STATE_GET 0
|
|
|
|
#define STATE_READ 5
|
|
|
|
|
|
|
|
#define STATE_END -2
|
|
|
|
|
|
|
|
#define STATE_ERR -1
|
|
|
|
int state = STATE_GET;
|
|
|
|
int ledState = 1;
|
|
|
|
unsigned char theByte;
|
|
|
|
|
|
|
|
while (getSn_SR(testSocket) == SOCK_ESTABLISHED) {
|
|
|
|
while (getSn_RX_RSR(testSocket) > 0) {
|
|
|
|
|
|
|
|
if (state == STATE_GET) {
|
|
|
|
if (readMatch("GET /")) {
|
|
|
|
state = STATE_READ;
|
|
|
|
} else {
|
|
|
|
state = STATE_ERR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state == STATE_READ) {
|
|
|
|
|
|
|
|
theByte = readByte();
|
|
|
|
|
|
|
|
if (theByte == '0') {
|
|
|
|
digitalWrite(PIN_LED, LOW);
|
|
|
|
ledState = 0;
|
|
|
|
delay(100);
|
|
|
|
} else if (theByte == '1') {
|
|
|
|
digitalWrite(PIN_LED, HIGH);
|
|
|
|
ledState = 1;
|
|
|
|
delay(100);
|
|
|
|
} else {
|
|
|
|
// It's not a valid byte.
|
|
|
|
state = STATE_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
state = STATE_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((state == STATE_ERR) || (state == STATE_END)) {
|
|
|
|
Serial.println("");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
dataLength = getSn_RX_RSR(testSocket);
|
|
|
|
|
|
|
|
if (dataLength >= MAX_RX_BUFFER_SIZE) { // TODO: blah, blah...
|
|
|
|
dataLength = MAX_RX_BUFFER_SIZE-1;
|
|
|
|
}
|
|
|
|
// Serial.print("dataLength: "); Serial.println(dataLength, HEX);
|
|
|
|
//Serial.print("recv result: ");
|
|
|
|
//Serial.println(recv(testSocket, bytesReceived, dataLength), DEC); // NOTE: Throws away unread portion? No?
|
|
|
|
recv(testSocket, bytesReceived, dataLength); // TODO: Return length?
|
|
|
|
bytesReceived[dataLength]=0x00;
|
|
|
|
Serial.print((char *)bytesReceived);
|
|
|
|
|
|
|
|
//Serial.print("send result: ");
|
|
|
|
//Serial.println(send(testSocket, bytesReceived, dataLength), DEC);
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
}
|
|
|
|
sendBanner(bytesToSend, ledState);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(testSocket);
|
|
|
|
disconnect(testSocket);
|
|
|
|
|
|
|
|
Serial.println("End test W5100 socket...");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
void loop() {
|
|
|
|
}
|
|
|
|
#endif
|