Created
December 31, 2009 02:21
-
-
Save lisa/266573 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Ethernet.h> | |
#include <ASocket.h> | |
#include <EEPROM.h> | |
#include "Dhcp.h" | |
#ifdef HOST_NAME | |
#undef HOST_NAME | |
#endif | |
#define HOST_NAME "myarduino" | |
#define DEBUG | |
// where the MAC address starts in EEPROM. | |
#define MACADDR_START 0 | |
#define BUFFER_LEN 64 | |
enum receive_frame_state { COMMAND_READ_WAIT, COMMAND_READ, | |
HEADER_KEY_READ_WAIT, HEADER_KEY_READ, | |
HEADER_VAL_READ_WAIT, HEADER_VAL_READ, | |
BODY_READ_WAIT, BODY_READ, | |
FRAME_DONE_CLEANUP, FRAME_DONE }; | |
enum Cmd { CONNECT, CONNECTED, RECEIPT, MESSAGE, ERROR, UNKNOWN = 900 }; | |
struct hash { | |
char *key; | |
char *value; | |
}; | |
struct stomp_frame { | |
Cmd cmd; | |
char *header; | |
struct hash **headers; | |
char *content; | |
}; | |
typedef struct stomp_frame STOMP_FRAME; | |
/* */ | |
struct stomp_message { | |
char *header; | |
char *content; | |
}; | |
/* | |
typedef struct stomp_message STOMP_MESSAGE; | |
*/ | |
struct stomp_connection { | |
unsigned short port; | |
byte broker_ip[4]; // aaa.bbb.ccc.ddd | |
char *username; | |
char *password; | |
char buffer[BUFFER_LEN]; | |
// Some socket here | |
}; | |
typedef struct stomp_connection STOMP_CONNECTION; | |
unsigned long time; | |
STOMP_CONNECTION conn; | |
byte *mac = (byte *)malloc(sizeof(byte) * 6); | |
byte my_ip[] = { 192, 168, 2, 4 }; | |
ASocket as; | |
#define SERVER_CONNECT 0 | |
#define SERVER_CONNECTING 1 | |
#define SERVER_RECEIVE 2 | |
#define NET_IDLE 3 | |
#define NET_TIMER 4 | |
uint8 networkState = SERVER_CONNECT; | |
void stomp_login(ASocket *, struct stomp_connection *); | |
uint8 read_char(ASocket *as); | |
char * stpcpy(char *, const char *); | |
void loop_forever(void); | |
void setup() { | |
Serial.begin(9600); | |
read_mac_address(); | |
Ethernet.begin(mac, my_ip); | |
conn.broker_ip[0] = 192;conn.broker_ip[1] = 168; | |
conn.broker_ip[2] = 2 ;conn.broker_ip[3] = 1; | |
conn.port = 61613; | |
conn.username = ""; | |
conn.password = ""; | |
Serial.println("Done setup"); | |
} | |
void loop() { | |
switch(networkState) { | |
case SERVER_CONNECT: | |
Serial.println("Connecting..."); | |
if (as.initTCP(0)) { | |
as.connectTCP(conn.broker_ip,conn.port); | |
networkState = SERVER_CONNECTING; | |
} | |
else { | |
Serial.println("Failed to init socket"); | |
networkState = NET_IDLE; | |
} | |
break; | |
case SERVER_CONNECTING: | |
if (as.isConnectedTCP()) { | |
Serial.println("Connected. Trying to login to STOMP"); | |
stomp_login(&as,&conn); | |
networkState = SERVER_RECEIVE; | |
} | |
break; | |
case SERVER_RECEIVE: | |
if (as.isConnectedTCP()) { | |
// Serial.println("SERVER RECV - is connected"); | |
if (as.available()) { | |
uint8 c; | |
as.read(&c,1); | |
Serial.print(c); | |
} | |
} | |
else { | |
as.disconnectTCP(); | |
as.close(); | |
networkState = NET_IDLE; | |
} | |
break; | |
case NET_IDLE: | |
networkState = NET_TIMER; | |
break; | |
} | |
} | |
////////////////// | |
void stomp_login(ASocket *as, struct stomp_connection *conn) { | |
STOMP_FRAME frame; | |
char *p; | |
frame.cmd = CONNECT; | |
frame.content = (char *)malloc(1 * sizeof(char)); | |
strcpy(frame.content,""); | |
frame.header = (char *)malloc( sizeof(char) * ( strlen(conn->username) + | |
strlen(conn->password) + | |
(2 * strlen("\n")) + | |
strlen("login:") + | |
strlen("passcode:") + 1 | |
) | |
); | |
p = stpcpy(frame.header,"login:"); | |
p = stpcpy(p,conn->username); | |
p = stpcpy(p,"\n"); | |
p = stpcpy(p,"passcode:"); | |
p = stpcpy(p,conn->password); | |
p = stpcpy(p,"\n"); | |
send_frame(as,&frame); | |
receive_frame(as,&frame); | |
Serial.print("First header: "); | |
Serial.println(frame.headers[0]->key); | |
Serial.print("First header value: "); | |
Serial.println(frame.headers[0]->value); | |
as->close(); | |
loop_forever(); | |
} | |
void send_frame(ASocket *as, struct stomp_frame *frame) { | |
time = micros(); | |
as->beginPacketTCP(); | |
switch (frame->cmd) { | |
case CONNECT: | |
as->write("CONNECT\n"); | |
break; | |
} | |
as->write(frame->header); | |
as->write("\n"); // end of header | |
as->write(frame->content); | |
as->write((byte *) "\0",1); // end of content | |
as->send(); | |
} | |
/* | |
* Read ONE frame from the STOMP connection by reading character by character | |
until the entire frame has been read. | |
1. Until one of the conditions below is met after reading a character, if | |
it fails to meet criteria for 2-6 add it to a buffer. Once the character | |
being read meets the criteria read out of the buffer, assign | |
appropriately, tack on the single-character buffer, and reset the buffer. | |
2. read characters one at a time until a newline is detected; this is the | |
command. (read_command) | |
3. read characters one at a time until two newlines in a row are detected; | |
this marks the end of headers. Headers are defined by a series of | |
matches to /(\w+):(\w*)\n/ | |
4. if one of the headers is 'content-length' read $2 number of characters | |
following the second newline terminating header block; this is the | |
content of the frame. | |
5. if the 'content-length' header is absent read until a NULL character | |
following the second newline terminating header block; this is the | |
content of the frame. | |
6. Stop reading after the content of the frame is assembled. | |
*/ | |
void receive_frame(ASocket *as,struct stomp_frame *frame) { | |
char buffer[BUFFER_LEN]; | |
uint8 i = 0; /* Index to buffer */ | |
uint8 j = 0; /* Generic looping var */ | |
uint8 c; /* One character from the ASocket connection */ | |
int header_idx = -1; | |
receive_frame_state framestate = COMMAND_READ_WAIT; | |
signed int content_length = -1; | |
uint8 l = 0; /* Bytes read of content_length */ | |
bool seen_content_length = false; | |
frame->headers = NULL; | |
while ( framestate != FRAME_DONE ) { | |
// Serial.print("framestate = "); | |
// Serial.println(framestate,DEC); | |
switch(framestate) { | |
// Set us up to read the command | |
case COMMAND_READ_WAIT: | |
framestate = COMMAND_READ; | |
break; // COMMAND_READ_WAIT | |
// Read the Command | |
case COMMAND_READ: | |
c = read_char(as); | |
switch (c) { | |
case 'C': | |
frame->cmd = CONNECTED; | |
j = 8; /* Read off 8 characters from the packet CONNECTED\0 - 1 */ | |
break; | |
case 'R': | |
frame->cmd = RECEIPT; | |
j = 5; | |
break; | |
case 'M': | |
frame->cmd = MESSAGE; | |
j = 5; | |
break; | |
case 'E': | |
frame->cmd = ERROR; | |
j = 4; | |
break; | |
default: | |
frame->cmd = UNKNOWN; | |
j = 0; | |
break; | |
} /* end switch (c) */ | |
j += 1; /* ...and the newline too */ | |
while(j-- > 0) | |
read_char(as); | |
framestate = HEADER_KEY_READ_WAIT; | |
break; // COMMAND_READ | |
/* Set us up to read some headers */ | |
case HEADER_KEY_READ_WAIT: | |
/* Initialize frame->headers with enough space for one header. */ | |
frame->headers = (struct hash **)realloc(frame->headers, ++header_idx * | |
sizeof(struct hash *) | |
); | |
/* Here, we'll initialize these to NULL for our own sanity later on */ | |
frame->headers[header_idx]->key = NULL; | |
frame->headers[header_idx]->value = NULL; | |
framestate = HEADER_KEY_READ; | |
break; // HEADER_KEY_READ_WAIT | |
case HEADER_KEY_READ: | |
c = read_char(as); | |
switch(c) { | |
case ':': | |
/* | |
Serial.print("HEADER_KEY_READ: encounted : with "); | |
Serial.print(as->available(),DEC); | |
Serial.print(" bytes to read"); | |
Serial.print(" i = "); | |
Serial.print(i,DEC); | |
Serial.print(" header_idx = "); | |
Serial.println(header_idx,DEC); | |
*/ | |
// Encountered the :, we're done reading value | |
frame->headers[header_idx]->key = (char *)malloc((1 + i) * sizeof(char)); | |
for(j = 0; j < i; j++) { | |
/* | |
Serial.print("HEADER_KEY_READ: in (before) loop "); | |
Serial.print(as->available(),DEC); | |
Serial.print(" bytes to read"); | |
Serial.print(" i = "); | |
Serial.print(i,DEC); | |
Serial.print(" j = "); | |
Serial.print(j,DEC); | |
Serial.print(" header_idx = "); | |
Serial.println(header_idx,DEC); | |
*/ | |
frame->headers[header_idx]->key[j] = buffer[j]; | |
/* | |
Serial.print("HEADER_KEY_READ: in (after) loop "); | |
Serial.print(as->available(),DEC); | |
Serial.print(" bytes to read"); | |
Serial.print(" i = "); | |
Serial.print(i,DEC); | |
Serial.print(" j = "); | |
Serial.print(j,DEC); | |
Serial.print(" header_idx = "); | |
Serial.println(header_idx,DEC); | |
*/ | |
} | |
frame->headers[header_idx]->key[i] = '\0'; | |
i = j = 0; | |
framestate = HEADER_VAL_READ_WAIT; | |
break; // : | |
// when reading a \n here we're done reading headers. | |
case '\n': | |
if (i == 0) | |
framestate = BODY_READ_WAIT; | |
break; // \n | |
default: | |
if (i < BUFFER_LEN) | |
buffer[i++] = c; | |
else | |
loop_forever(); | |
break; //default | |
} | |
break; // HEADER_KEY_READ | |
case HEADER_VAL_READ_WAIT: | |
framestate = HEADER_VAL_READ; | |
break; // HEADER_VAL_READ_WAIT | |
case HEADER_VAL_READ: | |
c = read_char(as); | |
// Serial.print("in HEADER_VAL_READ - read a character: "); | |
// Serial.println(c); | |
switch (c) { | |
case '\n': | |
frame->headers[header_idx]->value = (char *)malloc((1 + i) * sizeof(char)); | |
for(j = 0; j < i; j++) | |
frame->headers[header_idx]->value[j] = buffer[j]; | |
frame->headers[header_idx]->value[i] = '\0'; | |
i = j = 0; | |
framestate = HEADER_KEY_READ_WAIT; | |
break; // \n | |
default: | |
if (i < BUFFER_LEN) | |
buffer[i++] = c; | |
else | |
loop_forever(); | |
break; //default | |
} | |
//framestate = HEADER_KEY_READ_WAIT; | |
break; // HEADER_VAL_READ | |
case BODY_READ_WAIT: | |
frame->content = NULL; // I'll handle the memory... (* gulp *) | |
l = 0; /* read 0 bytes of body content */ | |
for(j = header_idx; j > 0; j--) { | |
if ( (strcmp(frame->headers[j]->key,"content-length") == 0)) { | |
content_length = atoi(frame->headers[j]->value); | |
frame->content = (char *)malloc(content_length * sizeof(char)); | |
} | |
} | |
framestate = BODY_READ; | |
break; // BODY_READ_WAIT | |
case BODY_READ: | |
c = read_char(as); | |
l += 1; | |
switch(c) { | |
case '\0': | |
// if content_length > 0 read that many bytes | |
if (content_length > 0) { | |
frame->content[l - 1] = c; | |
content_length -= 1; | |
/* UGLY - FIXME. I shouldn't need this otherwise I read a byte of the NEXT FRAME!! */ | |
if (content_length == 0) | |
framestate = FRAME_DONE_CLEANUP; | |
} | |
/* End of the body/frame */ | |
else { | |
for (j = 0; j < i; j++) | |
frame->content[j] = buffer[i]; | |
frame->content[i + 1] = '\0'; | |
framestate = FRAME_DONE_CLEANUP; | |
} | |
break; // \0 | |
default: | |
if (i < BUFFER_LEN - 1) | |
buffer[i++] = c; | |
else // FIXME: I should fix this and be smart about BUFFER_LEN and l | |
loop_forever(); | |
break; // default | |
} // switch(c) | |
framestate = FRAME_DONE_CLEANUP; | |
break; // BODY_READ | |
case FRAME_DONE_CLEANUP: | |
/* Check to see if the last-created header has value, else realloc -1 | |
if (frame->headers[header_idx]->key == NULL && | |
frame->headers[header_idx]->value == NULL) { | |
frame->headers = (struct hash **)realloc(frame->headers, --header_idx * | |
sizeof(struct hash *)); | |
} */ | |
framestate = FRAME_DONE; | |
break; // FRAME_DONE_CLEANUP | |
} /* end switch(framestate) */ | |
} | |
} | |
uint8 read_char(ASocket *as) { | |
uint8 c; | |
/* | |
Serial.print("read_char(): isConnectedTCP = "); | |
Serial.print(as->isConnectedTCP(),HEX); | |
Serial.print(" available = "); | |
Serial.print(as->available(),DEC); | |
Serial.print(" Time = "); | |
Serial.println(micros() - time); | |
*/ | |
while( ! as->isConnectedTCP() || as->available() <= 0) { | |
/* | |
Serial.print("read_char(): isConnectedTCP = "); | |
Serial.print(as->isConnectedTCP(),HEX); | |
Serial.print(" available = "); | |
Serial.print(as->available(),DEC); | |
Serial.print(" Time = "); | |
Serial.println(micros() - time); | |
*/ | |
Serial.println("read_char: Waiting for data"); | |
delay(2000); | |
} | |
as->read(&c,1); | |
// Serial.print("Read char: "); | |
// Serial.println(c); | |
return c; | |
} | |
void loop_forever(void) { | |
for (;;) { | |
delay(1000); | |
Serial.println("loop_forever()"); | |
} | |
} | |
void read_mac_address() { | |
for (int i = MACADDR_START; i < 6; i++) { | |
*mac = EEPROM.read(i); // Assign value (pointed to by `mac`) to contents | |
// of EEPROM. | |
mac++; // Move to next memory space | |
} | |
mac -= 6; // Set pointer location back to the start of | |
// the allocated memory space. | |
} | |
char * stpcpy(char *dst, const char *src) { | |
const size_t len = strlen (src); | |
return (char *) memcpy (dst, src, len + 1) + len; | |
} | |
/* | |
as->beginPacketTCP(); | |
as->write("CONNECT\n"); | |
as->write("login:"); | |
as->write(conn->username); | |
as->write("\npasscode:"); | |
as->write(conn->password); | |
as->write( (byte *) "\n\n\0",3); | |
// as.write( (byte *) "CONNECT\nlogin:\npasscode:\n\n\0",27); | |
as->send(); | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment