Skip to content
Snippets Groups Projects
Commit 77618cd9 authored by Kyrre Ness Sjobaek's avatar Kyrre Ness Sjobaek
Browse files

Merge branch 'dev' into 'master'

Fixes output formatting and variable initialization, moving config to separate file, project folder correctly named, hopefully fixes network crashes

Closes #5, #3, and #1

See merge request !1
parents f2abe1a3 b25e1990
No related branches found
No related tags found
1 merge request!1Fixes output formatting and variable initialization, moving config to separate file, project folder correctly named, hopefully fixes network crashes
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
* Server for transmitting multiple Mitutoyo position gauge measurements over ethernet. * Server for transmitting multiple Mitutoyo position gauge measurements over ethernet.
* Access via HTTP on TCP port 80, or very simple text protocol (telnet-like), one port per device. * Access via HTTP on TCP port 80, or very simple text protocol (telnet-like), one port per device.
* *
* Example for accessing over text interface (best for machine clients):
* nc 192.168.1.31 21
* or
* nc 192.168.1.31 22
* Netcat (nc) is the best client: it is a clean TCP client,
* which makes no attempts to negotiate anything.
*
* Kyrre Ness Sjobak, 15 Feb 2021 * Kyrre Ness Sjobak, 15 Feb 2021
* University of Oslo / CERN * University of Oslo / CERN
* *
...@@ -18,54 +25,13 @@ ...@@ -18,54 +25,13 @@
#include <SPI.h> #include <SPI.h>
#include <Ethernet.h> #include <Ethernet.h>
// ***** Mitutoyo instrument configuration ***** //This file holds all the configurations
// to be modified for deployment
const uint8_t numCh = 2; //How many instruments connected #include "PositionGaugeServer_config.h"
// Instruments are configured by arrays
// pinREQ / pinDAT / pinCLK / srvTXT_port / srvTXT
// Remember to update if changing the config!
//Arduino pins
// Used for Ethernet shield -- AVOID:
// 4/10/11/12/13 (UNO)
// 50/51/52/10/4 (Mega)
const uint8_t pinREQ[numCh] = {5,7};
const uint8_t pinDAT[numCh] = {2,8};
const uint8_t pinCLK[numCh] = {3,9};
//Uncomment this to enable the "dummy reader" code instead of the SPC protocol interface
// This disables the actual reading of the instrument,
// however pins are still configured on start
// This enables debugging of the network code if no Mitutoyo instrument is connected.
//#define DUMMY_READER
// ***** 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};
//To use DHCP or not to use DHCP, that's the question.
#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
// ***** OTHER CONFIG ***************************
const unsigned long update_interval = 1000; //[ms] How often to loop
// (poll sensors, check for new network clients,
// feed data to network clients, DHCP housekeeping)
const unsigned long WEB_update_interval = 5; //[s] WEB requests are heavy, not too often is good.
const unsigned long SPC_timeout = 200; //[ms] Experimentally verified to be OK.
const unsigned long WEB_timeout = 700; //[ms] Max time allowed waiting for web client
// ***** GLOBAL VARIABLES *********************** // ***** GLOBAL VARIABLES ***********************
unsigned long prevUpdateTime; unsigned long prevUpdateTime = 0;
float measData[numCh]; float measData[numCh];
bool measDataFresh[numCh]; bool measDataFresh[numCh];
...@@ -83,16 +49,9 @@ EthernetServer srvTXT[numCh] = { ...@@ -83,16 +49,9 @@ EthernetServer srvTXT[numCh] = {
EthernetClient clients[MAX_SOCK_NUM]; EthernetClient clients[MAX_SOCK_NUM];
uint8_t clients_ch[MAX_SOCK_NUM]; //which channel does a particular client belong to? uint8_t clients_ch[MAX_SOCK_NUM]; //which channel does a particular client belong to?
unsigned long srvTXT_prevUpdateTime[numCh];
// ***** CODE ************************************
// ***** CODE -- DO NOT CHANGE *******************
void setup() { void setup() {
...@@ -178,6 +137,7 @@ void setup() { ...@@ -178,6 +137,7 @@ void setup() {
srvWEB.begin(); srvWEB.begin();
for (uint8_t ch = 0; ch<numCh; ch++) { for (uint8_t ch = 0; ch<numCh; ch++) {
srvTXT[ch].begin(); srvTXT[ch].begin();
srvTXT_prevUpdateTime[ch] = 0;
} }
//General //General
...@@ -192,7 +152,8 @@ void setup() { ...@@ -192,7 +152,8 @@ void setup() {
} }
Serial.print("Update interval "); Serial.print(update_interval); Serial.println(" [ms]"); Serial.print("Poll interval "); Serial.print(update_interval); Serial.println(" [ms]");
Serial.print("Max. interval "); Serial.print(srvTXT_max_interval); Serial.println(" [ms]");
Serial.println(); Serial.println();
} }
...@@ -453,8 +414,9 @@ void HTTPserver () { ...@@ -453,8 +414,9 @@ void HTTPserver () {
client.println("<p>"); client.println("<p>");
client.print("IP address: "); client.print(Ethernet.localIP()); client.println("<br>"); client.print("IP address: "); client.print(Ethernet.localIP()); client.println("<br>");
client.print("No. channels: "); client.print(numCh); client.println("<br>"); client.print("No. channels: "); client.print(numCh); client.println("<br>");
client.print("Update interval "); client.print(update_interval); client.println(" [ms]<br>"); client.print("Poll interval: "); client.print(update_interval); client.println(" [ms]<br>");
client.print("WEB update interval "); client.print(WEB_update_interval); client.println(" [s]<br>"); client.print("WEB update interval:"); client.print(WEB_update_interval); client.println(" [s]<br>");
client.print("Max. interval: "); client.print(srvTXT_max_interval); client.println(" [ms]");
client.print("Internal time: "); client.print(millis()); client.println(" [ms]<br>"); client.print("Internal time: "); client.print(millis()); client.println(" [ms]<br>");
client.println("</p>"); client.println("</p>");
...@@ -557,6 +519,10 @@ void TXTserver (uint8_t ch) { ...@@ -557,6 +519,10 @@ void TXTserver (uint8_t ch) {
//clients[i].println(ch); //clients[i].println(ch);
//Feed the new client some data right away //Feed the new client some data right away
if(measData[ch] >= 0) {
//Add the '+' so that the string is always the same length
clients[i].print('+');
}
clients[i].print(measData[ch],3); clients[i].print(measData[ch],3);
if (measDataIsValid[ch]) { if (measDataIsValid[ch]) {
if (measDataIsMm[ch]) { if (measDataIsMm[ch]) {
...@@ -591,10 +557,16 @@ void TXTserver (uint8_t ch) { ...@@ -591,10 +557,16 @@ void TXTserver (uint8_t ch) {
} }
} }
//3. If fresh data is available, send it! //3. If fresh data is available or too long since last update, send it!
if (measDataFresh[ch]) { unsigned long timeSinceLastUpdate = millis() - srvTXT_prevUpdateTime[ch];
if (measDataFresh[ch] or (timeSinceLastUpdate >= srvTXT_max_interval)) {
srvTXT_prevUpdateTime[ch] = millis();
for (byte i = 0; i < MAX_SOCK_NUM; i++) { for (byte i = 0; i < MAX_SOCK_NUM; i++) {
if (clients[i] && (clients_ch[i]==ch)) { if (clients[i] && (clients_ch[i]==ch)) {
if(measData[ch] >= 0) {
//Add the '+' so that the string is always the same length
clients[i].print('+');
}
clients[i].print(measData[ch],3); clients[i].print(measData[ch],3);
if (measDataIsValid[ch]) { if (measDataIsValid[ch]) {
if (measDataIsMm[ch]) { if (measDataIsMm[ch]) {
...@@ -615,12 +587,11 @@ void TXTserver (uint8_t ch) { ...@@ -615,12 +587,11 @@ void TXTserver (uint8_t ch) {
for (byte i = 0; i < MAX_SOCK_NUM; i++) { for (byte i = 0; i < MAX_SOCK_NUM; i++) {
if ( clients[i] && (clients_ch[i]==ch) && !clients[i].connected() ) { if ( clients[i] && (clients_ch[i]==ch) && !clients[i].connected() ) {
clients[i].stop(); clients[i].stop();
clients_ch[i]=0;
Serial.print("Closed connection for ch="); Serial.print("Closed connection for ch=");
Serial.print(ch); Serial.print(ch);
Serial.print(" slot "); Serial.print(" slot ");
Serial.println(i); Serial.println(i);
} }
} }
} }
...@@ -669,4 +640,3 @@ void DHCPhousekeeping() { ...@@ -669,4 +640,3 @@ void DHCPhousekeeping() {
} }
} }
#endif #endif
#ifndef __PositionGaugeServer_config_h__
#define __PositionGaugeServer_config_h__
// ***** Mitutoyo instrument configuration *****
//Uncomment this to enable the "dummy reader" code instead of the SPC protocol interface
// This disables the actual reading of the instrument,
// however pins are still configured on start
// This enables debugging of the network code if no Mitutoyo instrument is connected.
//#define DUMMY_READER
const uint8_t numCh = 2; //How many instruments connected
// Instruments are configured by arrays
// pinREQ / pinDAT / pinCLK / srvTXT_port / srvTXT
// Remember to update if changing the config!
//Arduino pins
// Used for Ethernet shield -- AVOID:
// 4/10/11/12/13 (UNO)
// 50/51/52/10/4; 53 unused but MUST be output (Mega)
const uint8_t pinREQ[numCh] = {5,7};
const uint8_t pinDAT[numCh] = {2,8};
const uint8_t pinCLK[numCh] = {3,9};
// ***** 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 to use static IP configuration
#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
// ***** OTHER CONFIG ***************************
const unsigned long update_interval = 1000; //[ms] How often to loop
// (poll sensors, check for new network clients,
// feed data to network clients, DHCP housekeeping)
const unsigned long WEB_update_interval = 5; //[s] WEB requests are heavy, not too often is good.
const unsigned long SPC_timeout = 200; //[ms] Experimentally verified to be OK.
const unsigned long WEB_timeout = 700; //[ms] Max time allowed waiting for web client
const unsigned long srvTXT_max_interval = 10000;//[ms] At least try to give an update this often
#endif
#record (stringin, "$(SYSDEV)MSG")
#{
#}
record (ai, "$(SYSDEV):POS")
{
field(DESC, "Gauge position")
field(DTYP, "stream")
field( INP, "@positionGauge.proto read_pos $(PORT)")
field(PREC, ".001") #display precision
field( EGU, "mm")
#TODO: This one is the value, and :UNIT is the unit.
# If unit is XX, then the value may be stale
# (indicates an unsuccessful instrument read)
# field(EGU , $(SYSDEV):UNIT)
field(SCAN, "I/O Intr")
}
record (stringin, "$(SYSDEV):UNIT")
{
field(DESC, "Gauge position")
field(DTYP, "stream")
field( INP, "@positionGauge.proto read_unit $(PORT)")
# field( EGU, "")
field(SCAN, "I/O Intr")
}
# EPICS StreamDevice driver for
# CLEAR's positionGaugeServer
# https://gitlab.cern.ch/CLEAR/arduino-positiongaugeserver/
# Developed using ESS E3 EPICS
#
# Kyrre Sjobak, May 2021
#
# Ouput format:
# I.e.
# '+0.000XX\r\n'
#
Terminator = CR LF;
ReplyTimeout = 15000;
ReadTimeout = 300;
read_pos
{
in "%!6f%*2s";
}
read_unit
{
in "%!*6f%2s";
}
require stream
#require iocstats
epicsEnvSet("TOP" "$(E3_CMD_TOP)")
epicsEnvSet("STREAM_PROTOCOL_PATH", ".:$(TOP)/db")
#CONNECTION CONFIGURATION
epicsEnvSet("IOCNAME", "TESTGAUGES")
epicsEnvSet("ARDUINO_IP", "192.168.1.31")
epicsEnvSet("PORT_X", "21")
epicsEnvSet("PORT_Y", "22")
drvAsynIPPortConfigure("CONN_X", "$(ARDUINO_IP):$(PORT_X)", 0, 0, 0)
drvAsynIPPortConfigure("CONN_Y", "$(ARDUINO_IP):$(PORT_Y)", 0, 0, 0)
dbLoadRecords("$(TOP)/db/positionGauge.db", "SYSDEV=$(IOCNAME):X,PORT=CONN_X")
dbLoadRecords("$(TOP)/db/positionGauge.db", "SYSDEV=$(IOCNAME):Y,PORT=CONN_Y")
dbLoadRecords("iocAdminSoft.db","IOC=$(IOCNAME):IocStat")
iocInit()
# Arduino Position gauge server # Arduino Position gauge server
By K.Sjobak, A.Gilardi, P.Korysko, and W.Farabolini By K. Sjobak, A. Gilardi, P. Korysko, and W. Farabolini
This code allows connecting multiple Mitutoyo position gauges to an Ethernet IP network, and transmit new readings to the connected clients. This code allows connecting multiple Mitutoyo position gauges to an Ethernet IP network, and transmit new readings to the connected clients.
The transmission is done over one TCP channel pr. gauge, per default running on port 21 and 22. The transmission is done over one TCP channel pr. gauge, per default running on port 21 and 22.
Additionally, a simple web/HTTP server is provided in order to check the status of the system. Additionally, a simple web/HTTP server is provided in order to check the status of the system.
The code is fully contained in the file `PositionGaugeServer.ino`. To connect to standard readout method, use netcat as follows (assuming the IP address is `192.168.1.31`; hostname can also be used here):
It depends on only the standard Arduino libraries and the Ethernet library in order to support the Ethernet shield . * `nc 192.168.1.31 21` For the horizontal channel
It is tested and deployed with an Arduino Uno and a Ethernet Shield v2, connected to two gauges (model nr?). * `nc 192.168.1.31 22` For the vertical channel
The code can be configured to support a variable number of gauges, network configurations, Arduino pinouts, and update intervals and timeouts by editing the **top** part of `PositionGaugeServer.ino`; the bottom part will reconfigure automatically.
Note that for any deployment you **must** set the correct MAC address in the code file. To connect to web interface, point your web browser at (note http, not https):
* `http://192.168.1.31/`
The code logic and global variable definition is contained in the file `PositionGaugeServer.ino`.
The configuration which must be changed by the user needs for each deployment is contained in the header file `PostionGaugeServer_config.h`, both in the `PositionGaugeServer` folder, and includes:
* Ethernet MAC address (**must** be set differently for each deployment)
* DHCP or fixed IP configuration
* Refresh intervals
* Timeouts
* Number of gauges to connect
* Pins to use for communication with gauges
The project only depends on the standard Arduino libraries and the Ethernet library in order to support the Ethernet shield .
It is tested and deployed with an Arduino Uno and a Ethernet Shield v2, connected to two gauges, as shown in the picture below.
The schematic for the connection to the gauge (provided by Roger Cheng) looks like: The schematic for the connection to the gauge (provided by Roger Cheng) looks like:
![Simple schematic](/schematic.png) ![Simple schematic](/schematic.png)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment