Skip to content

Instantly share code, notes, and snippets.

@lisa
Created December 31, 2009 02:21
Show Gist options
  • Save lisa/266573 to your computer and use it in GitHub Desktop.
Save lisa/266573 to your computer and use it in GitHub Desktop.
#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