Skip to content
Snippets Groups Projects

Arduino networking code

Merged Kyrre Ness Sjobaek requested to merge arduinoServer into master
Compare and
2 files
+ 529
0
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 442
0
/*
* Server for controling 4D robot for moving samples in/out of the beam at CLEAR.
* Access via telnet-like interface at port 23.
*
* Note: Expects UNIX line-endings, '\n', not '\r\n' ala Windows.
* Best client: Netcat (nc) -- this is a clean TCP client, no attempts to negotiate anything.
* Example:
* nc 192.168.1.31 23
*
* Kyrre Ness Sjobak, 24 April 2021
* University of Oslo / CERN
*
* Inspired by PositionGaugeServer,
* also in use at CLEAR
*/
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>
// ***** NETWORK CONFIG ************************
// *** MAC ADDRESS FOR THE ARDUINO ON THE PLASMA-LENS ***
//byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x84, 0xED};
// *** MAC ADDRESS FOR THE ARDUINO ON THE CLIC STRUCTURE ***
//byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x84, 0xEE};
// *** MAC ADDRESS FOR KYRRE'S TEST BOARD ***
byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x72, 0xF7};
//To use DHCP or not to use DHCP, that's the question.
// Comment out this line to NOT use DHCP.
#define USE_DHCP
#ifndef USE_DHCP
IPAddress ip(192, 168, 1, 177);
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);
#endif
const size_t telnet_numConnections = MAX_SOCK_NUM;
const size_t telnet_bufflen = 256; //This quickly takes a LOT of memory! Total for MEGA is 8k!
const int telnet_port = 23;
const size_t output_bufflen = 2048; // Output buffer
//Robot arm configuration
// ***** GLOBAL VARIABLES - DO NOT TOUCH ********
EthernetServer telnet_socket(telnet_port);
EthernetClient telnet_connection[telnet_numConnections];
bool telnet_connectionInUse[telnet_numConnections];
//Buffers & buffCount (number of elements = idx of first free element)
char telnet_buff[telnet_numConnections][telnet_bufflen];
size_t telnet_buffCount[telnet_numConnections];
char serial_buff[telnet_bufflen];
size_t serial_buffCount;
char output_buff[output_bufflen];
size_t output_buffCount;
void setup() {
//Serial setup, for debugging
Serial.begin(9600);
// wait for serial port to connect. Needed for native USB port only
int serialCounter=0;
while (!Serial) {
//However only wait for 1 second before giving up,
// so that it can work also when not connected to USB
delay(100);
serialCounter++;
if (serialCounter > 10) break;
}
Serial.println();
Serial.println("4DrobotServer initializing...");
//Network setup
Serial.println("Starting networking...");
#ifdef USE_DHCP
Ethernet.begin(mac);
#else
Ethernet.begin(mac, ip, myDns, gateway, subnet);
#endif
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
while(true)
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected");
}
else {
Serial.println("Ethernet cable connected / status unknown");
break;
}
byte buffMAC[6];
Serial.print("MAC address: ");
Ethernet.MACAddress(buffMAC);
for (byte octet=0; octet<6;octet++) {
Serial.print(buffMAC[octet],HEX);
if(octet < 5) {
Serial.print(":");
}
}
Serial.println();
Serial.print("IP address: "); Serial.println(Ethernet.localIP());
Serial.print("DNS address: "); Serial.println(Ethernet.dnsServerIP());
Serial.print("Gateway address: "); Serial.println(Ethernet.gatewayIP());
Serial.print("Subnet: "); Serial.println(Ethernet.subnetMask());
//Server setup
telnet_socket.begin();
for (uint8_t i=0; i<telnet_numConnections; i++) {
memset(telnet_buff[i], '\0', sizeof(telnet_buff[i]));
telnet_buffCount[i] = 0;
telnet_connectionInUse[i] = false;
}
memset(serial_buff, '\0', sizeof(serial_buff));
serial_buffCount = 0;
memset(output_buff, '\0', sizeof(output_buff));
output_buffCount = 0;
//Serial is ready for input!
Serial.println("$");
}
void loop() {
//Run all the "programs" in order
//delay(1000);
telnet_server();
// ** Housekeeping **
#ifdef USE_DHCP
DHCPhousekeeping();
#endif
}
void telnet_server() {
// Program for running the telnet- and serial communication
//1. Check for new connections
while (true) {
//Loop in case there are multiple new connections
EthernetClient newConnection = telnet_socket.accept();
if (newConnection) {
//Find a new connection slot
for (size_t i=0; i<telnet_numConnections; i++) {
if (!telnet_connectionInUse[i]) {
telnet_connection[i] = newConnection;
telnet_connectionInUse[i] = true;
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, "< INFO CONN MAKE: ");
IPAddress IP = telnet_connection[i].remoteIP();
char IP_buff[16];
snprintf(IP_buff, sizeof(IP_buff), "%d.%d.%d.%d", IP[0],IP[1],IP[2],IP[3]);
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, IP_buff);
snprintf(IP_buff, sizeof(IP_buff), " ON #%-2d\n", i);
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, IP_buff);
telnet_connection[i].println("$");
break;
}
}
}
else {
break;
}
}
//2. Check for incoming data (telnet & serial) and buffer it
for (size_t i = 0; i < telnet_numConnections; i++) {
while (telnet_connectionInUse[i] &&
telnet_connection[i].connected() &&
telnet_connection[i].available() > 0 ) {
bufferWriteChar(telnet_buff[i], sizeof(telnet_buff[i]), telnet_buffCount[i], telnet_connection[i].read());
}
}
while (Serial.available() > 0) {
bufferWriteChar(serial_buff, sizeof(serial_buff), serial_buffCount, Serial.read());
}
//3. Feed the clients & serial with the content of the output buffer
if(output_buffCount) {
for (size_t i = 0; i < telnet_numConnections; i++) {
if (telnet_connectionInUse[i] &&
telnet_connection[i].connected() ) {
telnet_connection[i].print(output_buff);
}
}
Serial.print(output_buff);
memset(output_buff, '\0', sizeof(output_buff));
output_buffCount = 0;
}
//4. Check for telnet disconnects & handle
for (size_t i = 0; i < telnet_numConnections; i++) {
if ( telnet_connectionInUse[i] &&
!telnet_connection[i].connected() ) {
telnet_connectionInUse[i] = false;
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, "< INFO CONN BREAK: ");
IPAddress IP = telnet_connection[i].remoteIP();
char IP_buff[16];
snprintf(IP_buff, sizeof(IP_buff), "%d.%d.%d.%d", IP[0],IP[1],IP[2],IP[3]);
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, IP_buff);
snprintf(IP_buff, sizeof(IP_buff), " ON #%-2d\n", i);
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, IP_buff);
telnet_connection[i].stop();
memset(telnet_buff[i],'\0',sizeof(telnet_buff[i]));
telnet_buffCount[i]=0;
}
}
//5. Parse input buffers & notify that we are ready for more
for (size_t i = 0; i < telnet_numConnections; i++) {
input_parser(telnet_buff[i], sizeof(telnet_buff[i]), telnet_buffCount[i], &(telnet_connection[i]));
}
input_parser(serial_buff, sizeof(serial_buff), serial_buffCount, &(Serial));
}
void input_parser(char* in, const size_t buffSize, size_t& buffCount, Stream* originatingStream) {
// Attempt to parse the commands in the buffer.
// It will only attempt to parse commands that end with a '\n', '\r', or a combination thereof
//1. Sanity-check the buffer size
// (Note: Last entry is always a \0, buffSize cannot point to it)
if (buffCount == buffSize-1) {
originatingStream->println("@ INFO BUFFER FULL");
originatingStream->println("@ INFO BUFFER RESET");
memset(in,'\0', buffSize);
buffCount=0;
// Ready for more
originatingStream->println("$");
return;
}
//2. Look for whole lines & parse each of them in order
char line_buff[buffSize];
memset(line_buff,'\0', sizeof(line_buff));
size_t line_next = 0; //Index of the first valid character
bool lastchar_was_newline = true;
bool gotCommand = false; //Print a `$` when done, after having run commands
for (size_t i = 0; i<buffCount; i++) {
if(in[i] == '\0') {
//Sanity check: There should be no NULL before the end of the buffer `in`
originatingStream->println("@ INFO BUFFER NULL");
originatingStream->println("@ INFO BUFFER RESET");
memset(line_buff,'\0', sizeof(line_buff));
memset(in,'\0', buffSize);
line_next = 0;
buffCount = 0;
// Ready for more
originatingStream->println("$");
return;
}
else if (lastchar_was_newline) {
//Skip repeated newlines, and place line_next correctly
line_next = i;
if(in[i] != '\n' and in[i] != '\r') {
//Double newline or newline at start
lastchar_was_newline = false;
}
}
else if (in[i] == '\n' or in[i] == '\r') {
//Newline not following start or another newline
memcpy(line_buff, in+line_next, i-line_next);
line_buff[i-line_next]='\0';
parse_line(line_buff);
lastchar_was_newline = true;
//Note: line_next is valid before lastchar_was_newline is false
line_next = i;
memset(line_buff,'\0', sizeof(line_buff));
gotCommand = true;
}
//Else: Just keep counting chars
}
//3. Prepare buffers for next round
if (lastchar_was_newline && buffCount > 0) {
// All data was parsed; reset buffer and continue
if(not in[line_next+1] == '\0') {
//Internal error!
originatingStream->println("@ INFO BUFFER UNTERMINATED");
originatingStream->println("@ INFO BUFFER RESET");
//TODO:
// This may indicate a serious bug (array overrun)
// consider resetting the CPU!
}
//All treated, let's reset
memset(line_buff,'\0', sizeof(line_buff));
memset(in,'\0', buffSize);
buffCount = 0;
line_next = 0;
// Ready for more
originatingStream->println("$");
}
else if (not lastchar_was_newline && line_next>0) {
// Shift remaining content of `in` to start of `in`
size_t j = 0;
for(size_t i = line_next; i < buffCount; i++) {
in[j] = in[i];
j += 1;
}
memset(in+j,'\0', buffSize-j);
buffCount = j;
}
}
void parse_line(char* line_buff) {
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, "> ");
bufferWrite(output_buff, sizeof(output_buff), output_buffCount, line_buff);
bufferWriteChar(output_buff, sizeof(output_buff), output_buffCount, '\n');
}
int bufferWrite(char* buff, const size_t buffSize, size_t& buffCount, const char* in) {
/* Add a zero-terminated string to a buffer,
* checking for buffer overruns and
* (SIDE EFFECT!) incrementing the buffCount.
* If the buffer will overrun, truncate the input string.
* Return number of chars written, or return <0 on error.
* If buffer overrun, chars may still be written -> positive return.
*/
size_t len = strlen(in);
int ret = snprintf(buff+buffCount, buffSize-buffCount, in);
buffCount += ret;
return ret;
}
int bufferWriteChar(char* buff, const size_t buffSize, size_t& buffCount, const char in) {
/* Add a single char to a buffer,
* checking that there is space for it and
* (SIDE EFFECT!) incrementing the buffCount.
* Returns the number of chars written (0 or 1).
*/
if (buffCount == buffSize-1) {
//Pointing at terminating \0,
// no more space!
return 0;
}
else {
buff[buffCount] = in;
buffCount++;
return 1;
}
}
#ifdef USE_DHCP
void DHCPhousekeeping() {
switch (Ethernet.maintain()) {
case 1:
//renewed fail
Serial.println("Error: renewed fail");
break;
case 2:
//renewed success
Serial.println("Renewed success");
//print your local IP address:
Serial.print("My IP address: ");
Serial.println(Ethernet.localIP());
break;
case 3:
//rebind fail
Serial.println("Error: rebind fail");
break;
case 4:
//rebind success
Serial.println("Rebind success");
//print your local IP address:
Serial.print("My IP address: ");
Serial.println(Ethernet.localIP());
break;
default:
//nothing happened
break;
}
}
#endif
Loading