You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

588 lines
14 KiB

/*
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 (no longer)
+ example "web server" with LED flash.
* Terrible hacked-together code
Author:
follower@rancidbacon.com
License:
LGPL
Version:
20071106-0005+
*/
#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:
NetworkConnection(uint16_t port); // TODO: Add UDP, TCP choice?
int listen();
int isConnected();
int read();
void NetworkConnection::close();
private:
SOCKET _socket;
static const int _MAX_SOCKETS = MAX_SOCK_NUM; // TODO: Use this.
};
NetworkConnection::NetworkConnection(uint16_t port) {
/*
*/
_socket = 0; // TODO: Do properly
socket(_socket, Sn_MR_TCP, port, 0);
}
int NetworkConnection::listen() { // TODO: Make private or protected?
/*
*/
return !!::listen(_socket); // TODO: Use C++ namespaces for the driver functions?
}
int NetworkConnection::read() {
/*
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;
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:
return (getSn_SR(_socket) == SOCK_ESTABLISHED);
}
void NetworkConnection::close() {
/*
*/
::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);
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;
}
// NetworkInterface Network = NetworkInterface(...); // TODO: Make this a global
/* ----------------------- */
// #define PIN_RESET 9 // WIZnet module /RESET
#define PIN_RESET 8 // WIZnet module /RESET
SOCKET testSocket;
byte ip[6];
#define PIN_LED 2
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);
Serial.println("End W5100 configuration...");
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH);
Serial.println("Setup exit...");
/**/
Serial.println("Test sockets...");
NetworkConnection conn = Network.listen(7);
Serial.println("Waiting for connection...");
while (!conn.isConnected()) {
//Serial.print(!conn.isConnected());
delay(500);
}
Serial.println("Connected...");
conn.close();
Serial.println("End test and dummy loop");
while (1) {}
/**/
}
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...");
}