Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
Arduino4D_Robot
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
CLEAR
Arduino4D_Robot
Merge requests
!1
Arduino networking code
Code
Review changes
Check out branch
Download
Patches
Plain diff
Merged
Arduino networking code
arduinoServer
into
master
Overview
5
Commits
26
Pipelines
0
Changes
4
Merged
Kyrre Ness Sjobaek
requested to merge
arduinoServer
into
master
4 years ago
Overview
2
Commits
26
Pipelines
0
Changes
4
Expand
0
0
Merge request reports
Compare
master
version 24
984fb343
4 years ago
version 23
740275b9
4 years ago
version 22
8db061d2
4 years ago
version 21
072eb009
4 years ago
version 20
5203822d
4 years ago
version 19
f754157a
4 years ago
version 18
3d6115a9
4 years ago
version 17
0da250aa
4 years ago
version 16
117b4e58
4 years ago
version 15
f5f81541
4 years ago
version 14
0039bb16
4 years ago
version 13
36ba83a0
4 years ago
version 12
91ed13bc
4 years ago
version 11
34f799d7
4 years ago
version 10
69f78505
4 years ago
version 9
a35a4c54
4 years ago
version 8
93ca3ee9
4 years ago
version 7
da45d9da
4 years ago
version 6
13508201
4 years ago
version 5
98e572df
4 years ago
version 4
d2efe175
4 years ago
version 3
f3141861
4 years ago
version 2
b2be484a
4 years ago
version 1
f72441a7
4 years ago
master (base)
and
version 16
latest version
c91fb939
26 commits,
4 years ago
version 24
984fb343
25 commits,
4 years ago
version 23
740275b9
24 commits,
4 years ago
version 22
8db061d2
23 commits,
4 years ago
version 21
072eb009
22 commits,
4 years ago
version 20
5203822d
21 commits,
4 years ago
version 19
f754157a
20 commits,
4 years ago
version 18
3d6115a9
19 commits,
4 years ago
version 17
0da250aa
18 commits,
4 years ago
version 16
117b4e58
17 commits,
4 years ago
version 15
f5f81541
16 commits,
4 years ago
version 14
0039bb16
15 commits,
4 years ago
version 13
36ba83a0
14 commits,
4 years ago
version 12
91ed13bc
13 commits,
4 years ago
version 11
34f799d7
12 commits,
4 years ago
version 10
69f78505
11 commits,
4 years ago
version 9
a35a4c54
10 commits,
4 years ago
version 8
93ca3ee9
9 commits,
4 years ago
version 7
da45d9da
8 commits,
4 years ago
version 6
13508201
7 commits,
4 years ago
version 5
98e572df
6 commits,
4 years ago
version 4
d2efe175
4 commits,
4 years ago
version 3
f3141861
3 commits,
4 years ago
version 2
b2be484a
2 commits,
4 years ago
version 1
f72441a7
1 commit,
4 years ago
4 files
+
1215
−
0
Inline
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
Files
4
Search (e.g. *.vue) (Ctrl+P)
4DrobotServer/4DrobotServer.ino
0 → 100644
+
899
−
0
Options
/*
* Server for controling 4D robot for moving samples in/out of the beam at CLEAR.
* Access via telnet-like interface at port 23.
*
* Example for accessing over text interface:
* nc 192.168.1.31 23
* Netcat (nc) is the best client: it is a clean TCP client,
* which makes no attempts to negotiate anything.
*
* 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>
#include
<OneWire.h>
#include
<DallasTemperature.h>
#include
<Servo.h>
//This file holds all the configurations
// to be modified for deployment
#include
"4DrobotServer_config.h"
// ***** GLOBAL VARIABLES ***********************
// Sockets for TCP communications
EthernetServer
telnet_socket
(
telnet_port
);
EthernetClient
telnet_connection
[
telnet_numConnections
];
// ASCII IO Buffers
// buffCount is the 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
;
// Temperature sensor access & buffers
OneWire
temp_oneWire
(
temp_oneWire_pin
);
DallasTemperature
temp_sensors
(
&
temp_oneWire
);
float
temp_data
[
temp_numSensors
];
//[degC]
unsigned
long
temp_update_prev
=
0
;
//[ms]
// Sample grabber servo
Servo
grabber_servo
;
bool
grabber_go
=
false
;
int
grabber_pos
=
grabber_closed
;
//Assumed initial position
int
grabber_goto
=
grabber_closed
;
// Stepper motors
bool
stepper_go
=
false
;
//Move a stepper on the next loop?
uint8_t
stepper_go_zero
=
false
;
//Move-to-position (false) or
// move-to-switchLOW (true)?
uint8_t
stepper_go_axis
=
0
;
//Which stepper to move?
int
stepper_goto
=
0
;
//[steps] Target position
// (used if stepper_go_zero==false)
int
stepper_pos
[]
=
{
0
,
0
,
0
};
//Current stepper position
bool
stepper_mode_absolute
[]
=
{
false
,
false
,
false
};
//Do we have a valid zeroing?
// If true, pos represents the absolute
// position of that axis
// ***** CODE ***********************************
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
.
print
(
'\n'
);
Serial
.
print
(
"4DrobotServer initializing...
\n
"
);
//Network setup
Serial
.
print
(
"Starting networking...
\n
"
);
#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
)
{
// Do nothing, no point running without Ethernet hardware
// TODO: Not actually true, we could also run with just serial...
delay
(
1
);
}
}
while
(
true
)
if
(
Ethernet
.
linkStatus
()
==
LinkOFF
)
{
Serial
.
print
(
"Ethernet cable is not connected
\n
"
);
}
else
{
Serial
.
print
(
"Ethernet cable connected / status unknown
\n
"
);
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
.
print
(
'\n'
);
Serial
.
print
(
"IP address: "
);
Serial
.
print
(
Ethernet
.
localIP
());
Serial
.
print
(
'\n'
);
Serial
.
print
(
"DNS address: "
);
Serial
.
print
(
Ethernet
.
dnsServerIP
());
Serial
.
print
(
'\n'
);
Serial
.
print
(
"Gateway address: "
);
Serial
.
print
(
Ethernet
.
gatewayIP
());
Serial
.
print
(
'\n'
);
Serial
.
print
(
"Subnet: "
);
Serial
.
print
(
Ethernet
.
subnetMask
());
Serial
.
print
(
'\n'
);
//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
;
}
memset
(
serial_buff
,
'\0'
,
sizeof
(
serial_buff
));
serial_buffCount
=
0
;
memset
(
output_buff
,
'\0'
,
sizeof
(
output_buff
));
output_buffCount
=
0
;
//Initialize temperature sensors to something nonsensical
for
(
size_t
i
=
0
;
i
<
temp_numSensors
;
i
++
)
{
temp_data
[
i
]
=
-
500.0
f
;
//(it's float not Farenheit)
}
//Sanity check grabber config
if
(
not
(
grabber_open
>=
grabber_min
&&
grabber_open
<=
grabber_max
))
{
Serial
.
print
(
F
(
"Grabber config error; grabber_open outside of legal range
\n
"
));
while
(
true
)
{
delay
(
1
);
}
}
if
(
not
(
grabber_closed
>=
grabber_min
&&
grabber_closed
<=
grabber_max
))
{
Serial
.
print
(
F
(
"Grabber config error; grabber_closed outside of legal range
\n
"
));
while
(
true
)
{
delay
(
1
);
}
}
//Serial is ready for input!
Serial
.
print
(
F
(
"@ INFO MSG Say 'HELP' to get started
\n
"
));
Serial
.
print
(
"$
\n
"
);
}
//Run all the "programs" in order
void
loop
()
{
// Run actuators (one at a time)
servo_control
();
stepper_control
();
// Update sensor readings
temperatures_update
();
// Communicate
telnet_server
();
// Housekeeping
#ifdef USE_DHCP
DHCPhousekeeping
();
#endif
}
//Program for refreshing the temperatures
void
temperatures_update
()
{
//Poll temperature sensors no faster than the given interval
//If too short, some sensors may heat up and give inaccurate results
unsigned
long
thisUpdateTime
=
millis
();
if
(
not
(
thisUpdateTime
-
temp_update_prev
>=
temp_update_interval
))
{
//Not yet
return
;
}
temp_update_prev
=
thisUpdateTime
;
//Refresh temp_data
#ifndef DUMMY_TEMP
temp_sensors
.
requestTemperatures
();
for
(
size_t
i
=
0
;
i
<
temp_numSensors
;
i
++
)
{
temp_data
[
i
]
=
temp_sensors
.
getTempCByIndex
(
i
);
}
#else
for
(
size_t
i
=
0
;
i
<
temp_numSensors
;
i
++
)
{
temp_data
[
i
]
=
random
(
-
1000L
,
15000L
)
/
100.0
f
;
}
#endif
}
//Program for controlling the servomotor for the grabber
void
servo_control
()
{
if
(
grabber_go
)
{
do
{
if
(
grabber_pos
<
grabber_goto
)
{
grabber_pos
++
;
}
else
{
grabber_pos
--
;
}
#ifndef DUMMY_SERVO
grabber_servo
.
write
(
grabber_pos
);
#else
Serial
.
print
(
"Grabber to: "
);
Serial
.
print
(
grabber_pos
);
Serial
.
print
(
'\n'
);
#endif
delay
(
grabber_stepWait
);
}
while
(
grabber_pos
!=
grabber_goto
);
grabber_go
=
false
;
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< GRABBER GO FINISHED
\n
"
));
}
}
void
stepper_control
()
{
if
(
not
stepper_go
)
{
return
;
}
//Check XY interlock
// error if we try to move locked axis
if
(
(
stepper_go_axis
!=
STEPPER_Z
)
and
(
(
not
stepper_mode_absolute
[
STEPPER_Z
])
or
(
stepper_pos
[
STEPPER_Z
]
<=
stepper_Zpos_unlockXY
)
)
)
{
//We are trying to move a locked axis
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< STEPPER ERROR XY INTERLOCK
\n
"
));
goto
resetGO
;
}
//Move!
//Task: Zero an axis
if
(
stepper_go_zero
)
{
//Always move in negative direction when zeroing
digitalWrite
(
stepper_go_axis
,
stepper_backward
[
stepper_go_axis
]);
//Move until we trigger the low interlock switch
bool
onMAX
=
(
digitalRead
(
stepper_switchMAX_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
bool
onMIN
=
(
digitalRead
(
stepper_switchMIN_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
int
onLimitCount
=
0
;
while
(
not
onMIN
)
{
//Check the other-end switch that we aren't moving in the wrong direction or stuck
if
(
onMAX
)
{
onLimitCount
++
;
}
if
(
onLimitCount
>
stepper_onlimit_steps
)
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< STEPPER ERROR LIMIT SWITCH
\n
"
));
goto
resetGO
;
}
//Move (slowly)
digitalWrite
(
stepper_go_axis
,
HIGH
);
delayMicroseconds
(
stepper_onTime
);
digitalWrite
(
stepper_go_axis
,
LOW
);
delayMicroseconds
(
stepper_delay_slow
);
stepper_pos
[
stepper_go_axis
]
-=
1
;
//Poll switches
onMAX
=
(
digitalRead
(
stepper_switchMAX_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
onMIN
=
(
digitalRead
(
stepper_switchMIN_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
}
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< STEPPER ZERO MOVED "
));
char
buff
[
9
];
snprintf
(
buff
,
sizeof
(
buff
),
"%-07d
\n
"
,
stepper_pos
[
stepper_go_axis
]);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
buff
);
//Movement to switch completed successfully,
stepper_pos
[
stepper_go_axis
]
=
0
;
stepper_mode_absolute
[
stepper_go_axis
]
=
true
;
}
//Task: Move to position (either relative or absolute)
else
{
int
stepping
=
0
;
if
(
stepper_goto
>
stepper_pos
[
stepper_go_axis
])
{
digitalWrite
(
stepper_go_axis
,
stepper_forward
[
stepper_go_axis
]);
stepping
=
1
;
}
else
if
(
stepper_goto
<
stepper_pos
[
stepper_go_axis
])
{
digitalWrite
(
stepper_go_axis
,
stepper_backward
[
stepper_go_axis
]);
stepping
=
-
1
;
}
// Note: No 'else' needed; if they are equal, the while loop doesn't iterate.
//Move until goto==pos or we trigger an interlock switch stepper_onlimit_steps times
bool
onMAX
=
(
digitalRead
(
stepper_switchMAX_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
bool
onMIN
=
(
digitalRead
(
stepper_switchMIN_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
int
onLimitCount
=
0
;
while
(
stepper_pos
[
stepper_go_axis
]
!=
stepper_goto
)
{
//Check limit switches
if
(
onMAX
or
onMIN
)
{
onLimitCount
++
;
}
if
(
onLimitCount
>
stepper_onlimit_steps
)
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< STEPPER ERROR LIMIT SWITCH
\n
"
));
goto
resetGO
;
}
//Move (slowly)
//TODO: Implement acceleration
digitalWrite
(
stepper_go_axis
,
HIGH
);
delayMicroseconds
(
stepper_onTime
);
digitalWrite
(
stepper_go_axis
,
LOW
);
delayMicroseconds
(
stepper_delay_slow
);
stepper_pos
[
stepper_go_axis
]
+=
stepping
;
//Poll switches
onMAX
=
(
digitalRead
(
stepper_switchMAX_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
onMIN
=
(
digitalRead
(
stepper_switchMIN_pin
[
stepper_go_axis
])
==
stepper_switch_active
);
}
}
//Reset the flags which tells it where to go
//Can also jump (GOTO) here directly on error
resetGO
:
//Yep this is a GOTO label.
// Please read XKCD297 & XKCD292
stepper_go
=
false
;
stepper_go_zero
=
false
;
stepper_go_axis
=
0
;
stepper_goto
=
0
;
//Message '< STEPPER GO FINISHED AXIS <X/Y/Z/?> POS <POSITION>\n'
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< STEPPER GO FINISHED AXIS "
));
if
(
stepper_go_axis
==
STEPPER_X
)
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"X"
));
}
else
if
(
stepper_go_axis
==
STEPPER_Y
)
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"Y"
));
}
else
if
(
stepper_go_axis
==
STEPPER_Z
)
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"Z"
));
}
else
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"?"
));
}
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
" POS "
));
char
buff
[
9
];
snprintf
(
buff
,
sizeof
(
buff
),
"%-07d
\n
"
,
stepper_pos
[
stepper_go_axis
]);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
buff
);
return
;
}
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_connection
[
i
])
{
telnet_connection
[
i
]
=
newConnection
;
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 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
].
print
(
F
(
"@ INFO MSG Say 'HELP' to get started
\n
"
));
telnet_connection
[
i
].
print
(
"$
\n
"
);
break
;
}
}
}
else
{
break
;
}
}
//2. Check for incoming data (telnet & serial) and buffer it
for
(
size_t
i
=
0
;
i
<
telnet_numConnections
;
i
++
)
{
while
(
telnet_connection
[
i
]
&&
telnet_connection
[
i
].
connected
()
&&
telnet_connection
[
i
].
available
()
>
0
)
{
bufferWrite
(
telnet_buff
[
i
],
sizeof
(
telnet_buff
[
i
]),
telnet_buffCount
[
i
],
telnet_connection
[
i
].
read
());
#ifdef PARSER_DEBUG
Serial
.
print
(
"Got '"
);
Serial
.
print
(
telnet_buff
[
i
][
telnet_buffCount
[
i
]
-
1
]);
Serial
.
print
(
"' = "
);
Serial
.
print
((
byte
)
telnet_buff
[
i
][
telnet_buffCount
[
i
]
-
1
]);
Serial
.
print
(
", pos = "
);
Serial
.
print
(
telnet_buffCount
[
i
]
-
1
);
Serial
.
print
(
", connection = "
);
Serial
.
println
(
i
);
#endif
}
}
while
(
Serial
.
available
()
>
0
)
{
bufferWrite
(
serial_buff
,
sizeof
(
serial_buff
),
serial_buffCount
,
Serial
.
read
());
#ifdef PARSER_DEBUG
Serial
.
print
(
"Got '"
);
Serial
.
print
(
serial_buff
[
serial_buffCount
-
1
]);
Serial
.
print
(
"' = "
);
Serial
.
print
((
byte
)
serial_buff
[
serial_buffCount
-
1
]);
Serial
.
print
(
", pos = "
);
Serial
.
print
(
serial_buffCount
-
1
);
Serial
.
print
(
", connection = "
);
Serial
.
println
(
"SERIAL"
);
#endif
}
//3. Feed the clients & serial with the content of the output buffer
output_buff_flush
();
//4. Parse input buffers & notify that we are ready for more
bool
keepParsing
=
true
;
for
(
size_t
i
=
0
;
(
i
<
telnet_numConnections
)
&&
keepParsing
;
i
++
)
{
keepParsing
=
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
));
//5. Feed the clients & serial with the content of the output buffer generated during parsing
output_buff_flush
();
//6. Check for telnet disconnects & handle
for
(
size_t
i
=
0
;
i
<
telnet_numConnections
;
i
++
)
{
if
(
telnet_connection
[
i
]
&&
!
telnet_connection
[
i
].
connected
()
)
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 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
;
}
}
//7. Feed the clients & serial with disconnect messages
output_buff_flush
();
}
void
output_buff_flush
()
{
if
(
output_buffCount
)
{
for
(
size_t
i
=
0
;
i
<
telnet_numConnections
;
i
++
)
{
if
(
telnet_connection
[
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
;
}
}
bool
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
// Return true to continue parsing lines from the next input buffer,
// or false to tell telnet_parser() to stop parsing now,
// do the cleanup, and let loop() iterate to act on the command that was recieved.
//1. Sanity-check the buffer size
// (Note: Last entry is always a \0, buffSize cannot point to it)
if
(
buffCount
==
buffSize
-
1
)
{
output_buff_flush
();
originatingStream
->
print
(
F
(
"@ INFO BUFFER FULL
\n
"
));
originatingStream
->
print
(
F
(
"@ INFO BUFFER RESET
\n
"
));
memset
(
in
,
'\0'
,
buffSize
);
buffCount
=
0
;
// Ready for more
originatingStream
->
print
(
"$
\n
"
);
return
true
;
}
//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
bool
keepParsing
=
true
;
//Switches to false if a command we need to treat
// *right now* comes through (i.e. a motor movement)
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`
output_buff_flush
();
#ifdef PARSER_DEBUG
Serial
.
println
(
"Buffer contains NULL"
);
#endif
originatingStream
->
print
(
F
(
"@ INFO BUFFER NULL"
));
originatingStream
->
print
(
F
(
"@ INFO BUFFER RESET"
));
memset
(
line_buff
,
'\0'
,
sizeof
(
line_buff
));
memset
(
in
,
'\0'
,
buffSize
);
line_next
=
0
;
buffCount
=
0
;
// Ready for more
originatingStream
->
print
(
"$
\n
"
);
return
keepParsing
;
}
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
;
}
//If needed, break the loop here,
// with line_next set corectly for the array shift
if
(
not
keepParsing
)
{
break
;
}
}
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'
;
keepParsing
=
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
}
#ifdef PARSER_DEBUG
if
(
buffCount
>
0
)
{
Serial
.
println
();
Serial
.
print
(
"buffCount = "
);
Serial
.
println
(
buffCount
);
Serial
.
print
(
"line_next = "
);
Serial
.
println
(
line_next
);
Serial
.
print
(
"lastchar_was_newline = "
);
Serial
.
println
(
lastchar_was_newline
);
Serial
.
print
(
"keepParsing = "
);
Serial
.
println
(
keepParsing
);
for
(
size_t
i
=
0
;
i
<
((
buffCount
+
5
)
<
buffSize
?
(
buffCount
+
5
)
:
buffSize
);
i
++
)
{
Serial
.
print
(
i
);
Serial
.
print
(
i
<
buffCount
?
" '"
:
" X '"
);
Serial
.
print
(
in
[
i
]);
Serial
.
print
(
"' = "
);
Serial
.
print
((
byte
)
in
[
i
]);
Serial
.
print
(
i
==
line_next
?
" <-
\n
"
:
"
\n
"
);
}
}
#endif
//3. Prepare buffers for next round
if
(
lastchar_was_newline
&&
buffCount
==
line_next
+
1
)
{
//All data was parsed;
// reset buffer and continue
#ifdef PARSER_DEBUG
Serial
.
println
();
Serial
.
println
(
"Buffer reset"
);
#endif
if
(
not
in
[
line_next
+
1
]
==
'\0'
)
{
//Internal error!
output_buff_flush
();
originatingStream
->
print
(
F
(
"@ INFO BUFFER UNTERMINATED
\n
"
));
originatingStream
->
print
(
F
(
"@ INFO BUFFER RESET
\n
"
));
//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
->
print
(
"$
\n
"
);
}
else
if
(
line_next
>
0
)
{
//Some data remains in buffer (whole or partial lines);
// shift the remaining content of `in` to start of `in`
#ifdef PARSER_DEBUG
Serial
.
println
();
Serial
.
println
(
"Shifting:"
);
#endif
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
;
line_next
=
0
;
#ifdef PARSER_DEBUG
for
(
size_t
i
=
0
;
i
<
((
buffCount
+
5
)
<
buffSize
?
(
buffCount
+
5
)
:
buffSize
);
i
++
)
{
Serial
.
print
(
i
);
Serial
.
print
(
i
<
buffCount
?
" '"
:
" X '"
);
Serial
.
print
(
in
[
i
]);
Serial
.
print
(
"' = "
);
Serial
.
print
((
byte
)
in
[
i
]);
Serial
.
print
(
i
==
line_next
?
" <-
\n
"
:
"
\n
"
);
}
#endif
}
return
keepParsing
;
}
bool
parse_line
(
char
*
line_buff
)
{
//Parse one command line, given in "line_buff".
// Return true to continue parsing lines from the input buffer,
// or false to tell input_parser() to stop parsing lines now,
// do the cleanup, and let loop() iterate to act on the command that was recieved.
// This function assumes that line_buff is correctly NULL terminated.
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
"> "
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
line_buff
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
'\n'
);
size_t
len
=
strlen
(
line_buff
);
if
(
len
>=
4
and
strncmp
(
line_buff
,
"HELP"
,
4
)
==
0
)
{
char
buff
[
7
];
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< USAGE:
\n
"
));
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 'HELP' : Get help
\n
"
));
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 'TEMP' : Get temperature(s)
\n
"
));
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 'GRABBER STATUS' : Get grabber status
\n
"
));
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 'GRABBER POS int' : Goto the given position (integer)
\n
"
));
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 'GRABBER CLOSE' : Close the grabber (goto POS = "
));
snprintf
(
buff
,
sizeof
(
buff
),
"%03d )
\n
"
,
grabber_closed
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
buff
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< 'GRABBER OPEN' : Open the grabber (goto POS = "
));
snprintf
(
buff
,
sizeof
(
buff
),
"%03d )
\n
"
,
grabber_open
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
buff
);
}
else
if
(
len
>=
4
and
strncmp
(
line_buff
,
"TEMP"
,
4
)
==
0
)
{
for
(
size_t
i
=
0
;
i
<
temp_numSensors
;
i
++
)
{
char
buff
[
23
];
float
f
=
temp_data
[
i
];
int
tmp_int
=
(
int
)
(
f
*
100
);
int
tmp_dec
=
abs
(
tmp_int
-
tmp_int
/
100
*
100
);
tmp_int
=
tmp_int
/
100
;
snprintf
(
buff
,
sizeof
(
buff
),
"< TEMP #%03d %+04d.%02d C
\n
"
,
i
,
tmp_int
,
tmp_dec
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
buff
);
}
}
else
if
(
len
>=
7
and
strncmp
(
line_buff
,
"GRABBER"
,
7
)
==
0
)
{
//Note: Not writing out the sub-options to save RAM
if
(
len
>=
14
and
strncmp
(
line_buff
+
7
,
" STATUS"
,
7
)
==
0
)
{
//'GRABBER STATUS'
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< POS = "
));
char
buff
[
5
];
snprintf
(
buff
,
sizeof
(
buff
),
"%03d
\n
"
,
grabber_pos
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
buff
);
}
else
if
(
len
>=
13
and
strncmp
(
line_buff
+
7
,
" CLOSE"
,
6
)
==
0
)
{
//'GRABBER CLOSE'
if
(
grabber_go
)
{
// This should not be possible!
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< ERROR GRABBER ALREADY SET"
));
return
true
;
}
grabber_goto
=
grabber_closed
;
grabber_go
=
true
;
return
false
;
}
else
if
(
len
>=
12
and
strncmp
(
line_buff
+
7
,
" OPEN"
,
5
)
==
0
)
{
//'GRABBER OPEN'
if
(
grabber_go
)
{
// This should not be possible!
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< ERROR GRABBER ALREADY SET"
));
return
true
;
}
grabber_goto
=
grabber_open
;
grabber_go
=
true
;
return
false
;
}
else
if
(
len
>=
11
and
strncmp
(
line_buff
+
7
,
" POS"
,
4
)
==
0
)
{
//'GRABBER POS'
int
scanStatus
=
0
;
if
(
len
<
13
)
{
//no new POS integer given?
scanStatus
=
0
;
}
else
if
(
len
>
15
)
{
//More than three digits given?
scanStatus
=
0
;
}
else
if
(
line_buff
[
11
]
!=
' '
)
{
//No separating space?
scanStatus
=
0
;
}
else
{
//Passed sanity checks -> scan!
scanStatus
=
sscanf
(
line_buff
+
12
,
" %d"
,
&
grabber_goto
);
}
if
(
scanStatus
==
1
){
//Got one thing from sscanf()!
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< GRABBER GOTO "
));
char
buff
[
5
];
snprintf
(
buff
,
sizeof
(
buff
),
"%03d
\n
"
,
grabber_goto
);
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
buff
);
if
(
grabber_goto
>=
grabber_min
&&
grabber_goto
<=
grabber_max
)
{
grabber_go
=
true
;
return
false
;
}
else
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< ERROR GRABBER POS OUTSIDE LEGAL RANGE
\n
"
));
grabber_goto
=
grabber_pos
;
}
}
else
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< ERROR GRABBER POS PARSE FAILURE
\n
"
));
}
}
else
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< ERROR GRABBER COMMAND NOT RECOGNIZED
\n
"
));
}
}
else
{
bufferWrite
(
output_buff
,
sizeof
(
output_buff
),
output_buffCount
,
F
(
"< ERROR COMMAND NOT RECOGNIZED
\n
"
));
}
//Default: Don't halt the parsing
return
true
;
}
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
bufferWrite
(
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
;
}
}
int
bufferWrite
(
char
*
buff
,
const
size_t
buffSize
,
size_t
&
buffCount
,
const
__FlashStringHelper
*
ifsh
)
{
//Copy flash contents into buffer, otherwise functionally identical to the const char* version.
// Used to save RAM from big hardcoded strings.
//
// Based on ArduinoCore-avr/cores/arduino/Print.cpp->Print::Print(const __FlashStringHelper *ifsh)
// If it doesn't work (future arch), try removing the 'F()' around the strings and comment out this function.
PGM_P
p
=
reinterpret_cast
<
PGM_P
>
(
ifsh
);
size_t
n
=
0
;
while
(
1
)
{
char
c
=
pgm_read_byte
(
p
++
);
if
(
c
==
0
)
break
;
if
(
bufferWrite
(
buff
,
buffSize
,
buffCount
,
c
))
n
++
;
else
break
;
}
return
n
;
}
#ifdef USE_DHCP
void
DHCPhousekeeping
()
{
switch
(
Ethernet
.
maintain
())
{
//output_buff_flush();
case
1
:
//renewed fail
Serial
.
print
(
F
(
"@ DHCP Error: renewed fail
\n
"
));
break
;
case
2
:
//renewed success
Serial
.
print
(
F
(
"@ DHCP Renewed success
\n
"
));
//print your local IP address:
Serial
.
print
(
F
(
"@ DHCP My IP address: "
));
Serial
.
print
(
Ethernet
.
localIP
());
Serial
.
print
(
'\n'
);
break
;
case
3
:
//rebind fail
Serial
.
print
(
F
(
"@ DHCP Error: rebind fail"
));
break
;
case
4
:
//rebind success
Serial
.
print
(
F
(
"@ DHCP Rebind success
\n
"
));
//print your local IP address:
Serial
.
print
(
F
(
"@ DHCP My IP address: "
));
Serial
.
print
(
Ethernet
.
localIP
());
Serial
.
print
(
'\n'
);
break
;
default:
//nothing happened
break
;
}
}
#endif
Loading