5585d72c86
functions for converting between host and network byte order. These names are the de facto standard names for this functionality because of the original BSD TCP/IP implementation. But they cause problems for uIP/Contiki: some platforms define these names themselves (Mac OS, most notably), causing compilation problems for Contiki on those platforms. This commit changes all htons to uip_htons instead. Same goes for htonl, ntohs, and ntohl. All-caps versions as well.
518 lines
13 KiB
C
518 lines
13 KiB
C
#include <stepper-process.h>
|
|
#include <stepper-steps.h>
|
|
#include <stepper.h>
|
|
#include <stepper-move.h>
|
|
#include <string.h>
|
|
#include <interrupt-utils.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <net/uip.h>
|
|
#include <dev/cc2420.h>
|
|
|
|
|
|
#undef putchar
|
|
|
|
|
|
static unsigned int
|
|
parse_uint_hex(const char **pp, const char *end)
|
|
{
|
|
unsigned int v = 0;
|
|
while(*pp < end) {
|
|
char ch;
|
|
if ((ch = **pp) >= '0' && ch <= '9') {
|
|
v = v* 16 + (ch - '0');
|
|
} else if (ch >= 'A' && ch <= 'F') {
|
|
v = v* 16 + (ch - 'A') + 10;
|
|
} else break;
|
|
(*pp)++;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static int
|
|
parse_int_hex(const char **pp, const char *end)
|
|
{
|
|
if (*pp == end) return 0;
|
|
if (**pp == '-') {
|
|
(*pp)++;
|
|
return -parse_uint_hex(pp, end);
|
|
} else {
|
|
return parse_uint_hex(pp, end);
|
|
}
|
|
}
|
|
|
|
static void
|
|
skip_white(const char **pp, const char *end)
|
|
{
|
|
char ch;
|
|
while(*pp < end && ((ch = **pp) == ' ' || ch == '\t')) (*pp)++;
|
|
}
|
|
|
|
static const char hex_chars[] =
|
|
{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
|
|
|
|
static void
|
|
format_uint_hex(char **str, char *end, unsigned int v)
|
|
{
|
|
char buffer[10];
|
|
char *p = buffer+10;
|
|
if (*str == end) return;
|
|
if (v == 0) {
|
|
*(*str)++ = '0';
|
|
return;
|
|
}
|
|
while(v > 0) {
|
|
*--p = hex_chars[v&0xf];
|
|
v >>= 4;
|
|
}
|
|
while((p < buffer+10) && (*str < end)) {
|
|
*(*str)++ = *p++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
format_int_hex(char **str, char *end, int v)
|
|
{
|
|
if (v < 0) {
|
|
if (*str == end) return;
|
|
*(*str)++ = '-';
|
|
v = -v;
|
|
}
|
|
format_uint_hex(str, end, v);
|
|
}
|
|
|
|
static void
|
|
format_ull_hex(char **str, char *end, unsigned long long int v)
|
|
{
|
|
char buffer[16];
|
|
char *p = buffer+10;
|
|
if (*str == end) return;
|
|
if (v == 0) {
|
|
*(*str)++ = '0';
|
|
return;
|
|
}
|
|
while(v > 0) {
|
|
*--p = hex_chars[v&0xf];
|
|
v >>= 4;
|
|
}
|
|
while((p < buffer+10) && (*str < end)) {
|
|
*(*str)++ = *p++;
|
|
}
|
|
}
|
|
static void
|
|
format_ll_hex(char **str, char *end, long long v)
|
|
{
|
|
if (v < 0) {
|
|
if (*str == end) return;
|
|
*(*str)++ = '-';
|
|
v = -v;
|
|
}
|
|
format_ull_hex(str, end, v);
|
|
}
|
|
|
|
typedef struct _ReplyBuffer ReplyBuffer;
|
|
|
|
struct _ReplyBuffer
|
|
{
|
|
char buffer[70]; /* Should be small enough to fit in one packet */
|
|
char *write;
|
|
};
|
|
|
|
static ReplyBuffer tcp_reply;
|
|
static ReplyBuffer udp_reply;
|
|
|
|
#define REPLY_BUFFER_END(reply) ((reply)->buffer+sizeof((reply)->buffer))
|
|
#define REPLY_BUFFER_LEFT(reply) \
|
|
((reply)->buffer+sizeof((reply)->buffer) - (reply)->write)
|
|
|
|
static void
|
|
reply_char(ReplyBuffer *reply, char c)
|
|
{
|
|
if (REPLY_BUFFER_LEFT(reply) > 0) {
|
|
*reply->write++ = c;
|
|
}
|
|
}
|
|
|
|
static void
|
|
reply_str(ReplyBuffer *reply, char *str)
|
|
{
|
|
while(reply->write < REPLY_BUFFER_END(reply) && *str != '\0')
|
|
*reply->write++ = *str++;
|
|
}
|
|
|
|
static void
|
|
stepper_reply(ReplyBuffer *reply, StepperResult res)
|
|
{
|
|
switch(res) {
|
|
case STEPPER_OK:
|
|
reply_str(reply, "OK");
|
|
break;
|
|
case STEPPER_ERR_MEM:
|
|
reply_str(reply, "ERR MEM");
|
|
break;
|
|
case STEPPER_ERR_TOO_LATE:
|
|
reply_str(reply, "ERR LATE");
|
|
break;
|
|
case STEPPER_ERR_INDEX: /* Sholdn't happen here */
|
|
reply_str(reply, "ERR INDEX");
|
|
break;
|
|
default:
|
|
reply_str(reply, "ERR");
|
|
}
|
|
reply_char(reply, '\n');
|
|
}
|
|
|
|
#define CHECK_INPUT_LEFT(x) \
|
|
do {\
|
|
if ((x) > inend - input_line) {reply_str(reply, "ERR\n");return 0;}\
|
|
} while(0)
|
|
|
|
static int
|
|
handle_line(const char *input_line, const char *inend, ReplyBuffer *reply)
|
|
{
|
|
unsigned long when;
|
|
#if 0
|
|
{
|
|
const char *p = input_line;
|
|
printf("Got line: '");
|
|
while(p < inend) {
|
|
putchar(*p++);
|
|
}
|
|
printf("'\n");
|
|
fsync(1);
|
|
}
|
|
#endif
|
|
skip_white(&input_line, inend);
|
|
CHECK_INPUT_LEFT(1);
|
|
if (*input_line == '#') {
|
|
input_line++;
|
|
reply_char(reply, '#');
|
|
while (input_line < inend &&*input_line != ' ') {
|
|
reply_char(reply, *input_line++);
|
|
}
|
|
reply_char(reply, ' ');
|
|
}
|
|
skip_white(&input_line, inend);
|
|
|
|
if (*input_line == '@') {
|
|
input_line++;
|
|
when = parse_uint_hex(&input_line, inend);
|
|
} else {
|
|
when = stepper_current_period() + 3;
|
|
}
|
|
skip_white(&input_line, inend);
|
|
CHECK_INPUT_LEFT(1);
|
|
if (input_line[0] == 'L' || input_line[0] == 'R') {
|
|
unsigned int stepper_index = (input_line[0] == 'R' ? 1 : 0);
|
|
CHECK_INPUT_LEFT(1);
|
|
input_line++;
|
|
if (input_line[0] == 'S') {
|
|
int speed;
|
|
input_line++;
|
|
if (input_line == inend) {
|
|
/* printf("Speed: %ld\n",
|
|
stepper_current_velocity(stepper_index)/VEL_SCALE);*/
|
|
reply_char(reply, input_line[-2]);
|
|
reply_char(reply, 'S');
|
|
format_int_hex(&reply->write, REPLY_BUFFER_END(reply),
|
|
stepper_current_velocity(stepper_index)/VEL_SCALE);
|
|
reply_char(reply, '\n');
|
|
} else {
|
|
speed = parse_int_hex(&input_line, inend);
|
|
if (*input_line == ',') {
|
|
StepperResult res;
|
|
unsigned int acc;
|
|
input_line++;
|
|
acc = parse_uint_hex(&input_line, inend);
|
|
/* printf("Speed=%d, Acc=%u\n", speed, acc); */
|
|
res = stepper_set_velocity(stepper_index, &when,
|
|
acc, speed*VEL_SCALE);
|
|
|
|
stepper_reply(reply, res);
|
|
} else {
|
|
reply_str(reply, "ERR\n");
|
|
}
|
|
}
|
|
} else if (input_line[0] == 'C') {
|
|
reply_char(reply, input_line[-1]);
|
|
reply_char(reply, 'C');
|
|
format_ll_hex(&reply->write, REPLY_BUFFER_END(reply),
|
|
stepper_current_step(stepper_index));
|
|
reply_char(reply, '\n');
|
|
} else if (input_line[0] == 'M') {
|
|
unsigned int speed;
|
|
unsigned int acc;
|
|
int move;
|
|
input_line++;
|
|
speed = parse_uint_hex(&input_line, inend);
|
|
CHECK_INPUT_LEFT(1);
|
|
if (*input_line == ',') {
|
|
input_line++;
|
|
acc = parse_uint_hex(&input_line, inend);
|
|
if (*input_line == ',') {
|
|
StepperResult res;
|
|
input_line++;
|
|
move = parse_int_hex(&input_line, inend);
|
|
/*printf("Speed=%u, Acc=%u, Move=%d\n", speed, acc, move);*/
|
|
res = stepper_move(stepper_index, &when,
|
|
acc,speed*VEL_SCALE,move*DIST_SCALE);
|
|
stepper_reply(reply, res);
|
|
} else {
|
|
reply_str(reply, "ERR\n");
|
|
}
|
|
} else {
|
|
reply_str(reply, "ERR\n");
|
|
}
|
|
} else {
|
|
reply_str(reply, "ERR\n");
|
|
}
|
|
} else if (input_line[0] == 'E') {
|
|
STEPPER_ENABLE();
|
|
printf("Stepper enabled\n");
|
|
reply_str(reply, "OK\n");
|
|
} else if (input_line[0] == 'D') {
|
|
STEPPER_DISABLE();
|
|
printf("Stepper disabled\n");
|
|
reply_str(reply, "OK\n");
|
|
} else if (input_line[0] == 'p') {
|
|
reply_char(reply, 'p');
|
|
format_int_hex(&reply->write, REPLY_BUFFER_END(reply),
|
|
cc2420_last_rssi);
|
|
reply_char(reply, ',');
|
|
format_uint_hex(&reply->write, REPLY_BUFFER_END(reply),
|
|
cc2420_last_correlation);
|
|
reply_char(reply, '\n');
|
|
} else if (input_line[0] == 'T') {
|
|
reply_char(reply, 'T');
|
|
format_int_hex(&reply->write, REPLY_BUFFER_END(reply),
|
|
stepper_current_period());
|
|
reply_char(reply, '\n');
|
|
} else if (input_line[0] == 'q') {
|
|
return 1;
|
|
} else {
|
|
reply_str(reply, "ERR\n");
|
|
}
|
|
return 0;
|
|
}
|
|
static unsigned int transmit_len = 0;
|
|
|
|
static void
|
|
send_reply()
|
|
{
|
|
if (transmit_len == 0) {
|
|
transmit_len = tcp_reply.write - tcp_reply.buffer;
|
|
if (transmit_len > 0) {
|
|
/* printf("Sending len = %d\n", transmit_len); */
|
|
uip_send(tcp_reply.buffer, transmit_len);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
handle_connection()
|
|
{
|
|
static char exiting = 0;
|
|
static char line_buffer[100];
|
|
static char *line_end;
|
|
if (uip_connected()) {
|
|
exiting = 0;
|
|
transmit_len = 0;
|
|
line_end = line_buffer;
|
|
tcp_reply.write = tcp_reply.buffer;
|
|
reply_str(&tcp_reply, "Ready\n");
|
|
send_reply();
|
|
}
|
|
if (uip_acked()) {
|
|
if (tcp_reply.write - tcp_reply.buffer > transmit_len) {
|
|
memmove(tcp_reply.buffer, tcp_reply.buffer + transmit_len,
|
|
tcp_reply.write - tcp_reply.buffer - transmit_len);
|
|
}
|
|
tcp_reply.write -= transmit_len;
|
|
/* printf("Acked: %d left\n", reply_buffer.write-reply_buffer.buffer); */
|
|
transmit_len = 0;
|
|
if (exiting && tcp_reply.write == tcp_reply.buffer) {
|
|
uip_close();
|
|
exiting = 0;
|
|
}
|
|
}
|
|
if (uip_newdata()) {
|
|
const char *read_pos = uip_appdata;
|
|
const char *read_end = read_pos + uip_len;
|
|
/* printf("Got data\n"); */
|
|
while(read_pos < read_end) {
|
|
if (line_end == line_buffer+sizeof(line_buffer)) {
|
|
/* Buffer too small, just discard everything */
|
|
line_end = line_buffer;
|
|
}
|
|
*line_end++ = *read_pos++;
|
|
if (line_end[-1] == '\n' || line_end[-1] == '\r' || line_end[-1] == ';'){
|
|
if (line_end - 1 != line_buffer) {
|
|
if (handle_line(line_buffer, line_end - 1, &tcp_reply)) {
|
|
send_reply();
|
|
/* Postpone closing if there's reply data left to be sent. */
|
|
if (transmit_len == 0)
|
|
uip_close();
|
|
else
|
|
exiting = 1;
|
|
break;
|
|
}
|
|
}
|
|
line_end = line_buffer;
|
|
}
|
|
}
|
|
send_reply();
|
|
}
|
|
|
|
if (uip_poll()) {
|
|
send_reply();
|
|
}
|
|
if(uip_rexmit()) {
|
|
printf("Retransmit\n");
|
|
if (transmit_len > 0)
|
|
uip_send(tcp_reply.buffer, transmit_len);
|
|
}
|
|
|
|
}
|
|
|
|
PROCESS(udp_stepper_process, "UDP stepper process");
|
|
|
|
PROCESS_THREAD(udp_stepper_process, ev, data)
|
|
{
|
|
static struct etimer timer;
|
|
static struct uip_udp_conn *conn;
|
|
static char listening = 1; /* Listen for connections from anyone */
|
|
static uip_ipaddr_t any;
|
|
PROCESS_EXITHANDLER(goto exit);
|
|
PROCESS_BEGIN();
|
|
|
|
printf("udp_stepper_process starting\n");
|
|
|
|
uip_ipaddr(&any, 0,0,0,0);
|
|
conn = udp_new(&any, UIP_HTONS(0), NULL);
|
|
if (!conn) goto exit;
|
|
uip_udp_bind(conn, UIP_HTONS(1010));
|
|
etimer_set(&timer, CLOCK_SECOND*2);
|
|
while(1) {
|
|
PROCESS_YIELD();
|
|
|
|
if(ev == tcpip_event) {
|
|
if (uip_newdata()) {
|
|
struct uip_udpip_hdr *header = (struct uip_udpip_hdr *)uip_buf;
|
|
const char *line_start = uip_appdata;
|
|
const char *line_end = line_start;
|
|
const char *packet_end = line_start + uip_len;
|
|
udp_reply.write = udp_reply.buffer;
|
|
while(line_end < packet_end) {
|
|
if (*line_end == '\n' || *line_end == '\r' || *line_end == ';' ) {
|
|
if (line_end != line_start) {
|
|
handle_line(line_start, line_end, &udp_reply);
|
|
}
|
|
line_start = line_end+1;
|
|
}
|
|
line_end++;
|
|
}
|
|
/* Check if we are connected to a client, if not reconnect */
|
|
if (listening) {
|
|
uip_udp_remove(conn);
|
|
conn = udp_new(&header->srcipaddr, header->srcport, &conn);
|
|
if (!conn) goto exit;
|
|
uip_udp_bind(conn, UIP_HTONS(1010));
|
|
listening = 0;
|
|
}
|
|
etimer_reset(&timer);
|
|
tcpip_poll_udp(conn);
|
|
} else if (uip_poll()) {
|
|
if (data == &conn) {
|
|
uip_send(udp_reply.buffer, udp_reply.write - udp_reply.buffer);
|
|
/* printf("sent %ld\n", udp_reply.write - udp_reply.buffer); */
|
|
}
|
|
}
|
|
} else if (ev == PROCESS_EVENT_TIMER) {
|
|
uip_udp_remove(conn);
|
|
conn = udp_new(&any, UIP_HTONS(0), NULL);
|
|
if (!conn) goto exit;
|
|
uip_udp_bind(conn, UIP_HTONS(1010));
|
|
listening = 1;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
/* Contiki does automatic garbage collection of uIP state and we
|
|
* need not worry about that. */
|
|
printf("udprecv_process exiting\n");
|
|
PROCESS_END();
|
|
}
|
|
|
|
static const uint32_t stepper0_steps_acc[] = MICRO_STEP(0,3);
|
|
static const uint32_t stepper0_steps_run[] = MICRO_STEP(0,2);
|
|
static const uint32_t stepper0_steps_hold[] = MICRO_STEP(0,1);
|
|
|
|
static const uint32_t stepper1_steps_acc[] = MICRO_STEP(1,3);
|
|
static const uint32_t stepper1_steps_run[] = MICRO_STEP(1,2);
|
|
static const uint32_t stepper1_steps_hold[] = MICRO_STEP(1,1);
|
|
|
|
static StepperAccSeq seq_heap[40];
|
|
|
|
static void
|
|
init_seq_heap()
|
|
{
|
|
unsigned int i;
|
|
for(i = 0; i < sizeof(seq_heap)/sizeof(seq_heap[0]); i++) {
|
|
seq_heap[i].next = NULL;
|
|
stepper_free_seq(&seq_heap[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
robot_stepper_init()
|
|
{
|
|
disableIRQ();
|
|
init_seq_heap();
|
|
stepper_init(AT91C_BASE_TC0, AT91C_ID_TC0);
|
|
*AT91C_PIOA_OER = STEPPER_INHIBIT;
|
|
*AT91C_PIOA_MDER = STEPPER_INHIBIT; /* | STEPPER0_IOMASK; */
|
|
*AT91C_PIOA_CODR = STEPPER_INHIBIT;
|
|
stepper_init_io(1, STEPPER_IOMASK(0), stepper0_steps_acc,
|
|
stepper0_steps_run, stepper0_steps_hold,
|
|
(sizeof(stepper0_steps_run) / sizeof(stepper0_steps_run[0])));
|
|
stepper_init_io(0, STEPPER_IOMASK(1), stepper1_steps_acc,
|
|
stepper1_steps_run, stepper1_steps_hold,
|
|
(sizeof(stepper1_steps_run) / sizeof(stepper1_steps_run[0])));
|
|
enableIRQ();
|
|
}
|
|
|
|
|
|
PROCESS(stepper_process, "Stepper control process");
|
|
|
|
PROCESS_THREAD(stepper_process, ev, data)
|
|
{
|
|
PROCESS_EXITHANDLER(goto exit);
|
|
PROCESS_BEGIN();
|
|
robot_stepper_init();
|
|
tcp_listen(UIP_HTONS(1010));
|
|
|
|
process_start(&udp_stepper_process, NULL);
|
|
printf("Stepper starting\n");
|
|
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);
|
|
if(uip_connected()) {
|
|
/* printf("connected\n"); */
|
|
handle_connection(); /* Initialise parser */
|
|
while(!(uip_aborted() || uip_closed() || uip_timedout())) {
|
|
PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);
|
|
handle_connection();
|
|
}
|
|
}
|
|
printf("disconnected\n");
|
|
}
|
|
|
|
exit:
|
|
/* Contiki does automatic garbage collection of uIP state and we
|
|
* need not worry about that. */
|
|
printf("Stepper exiting\n");
|
|
PROCESS_END();
|
|
}
|
|
|