/*
control sercalo fiber-optic switches sw 1x24
This sketch uses the Serial.read() function in order to control a MEMS-Switch
Circuit:
- digital pins 2 to 12
- GND
- LEDs: digital pins 14 to 17
Command set:
:ROUT:CLOS <D>,<Input>,<Output>
:ROUT:CLOS? <D>,<Input> -> <Output>
:SYST:ERR? -> <error number>, <error message>
*STB -> <status byte>
*IDN? -> <Manufacturer>,<Model>,<ID>,<FW>
:CONF? -> <Device>,<Type>,<Maxin>,<Maxout>,<Latching>,<max Trim level>,<Opm>,<Fiber Mode >,
<Fiber Core Size>,<Connector Type>
:INFO? -> <Serial Number>,<Part Number>,<Card Firmware Rev>,<OSW Module Firmware Rev>,
<OPM Module firmware Rev>,<HW Rev>,<Assembly Date>,<Description>
:DEV:INFO? <D> -> <switch serial number>,<switch model number>
:COMPILED? -> <filename>, <date> <time>
created 16 May 2022
by Gregor Popp
modified 14 May 2024
by Gregor Popp
*/
#include <ctype.h>
// device specific constants
const String MANUFACTURER = "SERCALO";
const String PARTNO = "SW1x24-9N";
const String SERIALNO = "1240-1241";
const String MODEL = "SW1x24";
const String SERIALNOLONG[] = {"2008-38-1240","2008-38-1241"};
const String CONNECTOR_TYPE = "E2000/APC";
const String ASSEMBLY_DATE = "20140101";
const String CARD_FIRMWARE_REV = "2.4";
// parameters of optical switches
const int firstPins[] = {2, 7, 12};
const int nPins = firstPins[1] - firstPins[0];
const int firstLEDPin = 14; //
const int nLEDs = 4;
const int nSwitches = (sizeof (firstPins) / sizeof (*firstPins)) -1;
const int oswPattern[] = {20, 12, 28, 4, 21, 13, 29, 5, 19, 11, 27, 3, 7, 31, 15, 23, 1, 25, 9, 17, 0, 24, 8, 16};
const int len = (sizeof (oswPattern) / sizeof (*oswPattern));
const int minimumValue = 1;
const int maximumValue = len;
const String IDN = MANUFACTURER + "," + MODEL + "," + SERIALNO + "," + CARD_FIRMWARE_REV;
// Manufacturer,Model,ID,FW
const String CONFIG0 = " OSW 1 "+String(maximumValue)+" NO 0 NO 0 9.0 "+CONNECTOR_TYPE;
const String INFO = SERIALNO + ","+PARTNO + "," + CARD_FIRMWARE_REV +
",n/a," + ASSEMBLY_DATE + ",USB OPTICAL SWITCH";
// <Serial Number>,<Part Number>,<Card Firmware Rev>,<OSW Module Firmware Rev>,
// <OPM Module firmware Rev>,<HW Rev>,<Assembly Date>,<Description>
// 29374743,OSC-A1 1234 ,2.00,3.00,3.00,3.00, 20010815, Custom for IE MRR
String config = "1" + CONFIG0;
String DEVINFO[nSwitches];
const int errN[] = {0, -102, -224, -227, -222, -227, -240};
char *errText[] = {"\"no error\"", "\"Syntax error\"", "\"Illegal parameter value\"",
"\"Invalid device address\"", "\"Data out of range\"",
"\"Invalid address or no card inserted\"",
"\"Hardware error\""
};
const byte numChars = 32;
int errornumber = 0;
byte nTries = 3; // Anzahl Versuche falls digitale Ausgänge abweichend zu Schalterwunsch
struct parsedDataS {
char command[numChars] = {0};
int device = 0;
int inputChannel = 0;
int outputChannel = 0;
};
struct recvDataS {
char receivedChars[numChars] = {0};
boolean newData = false;
};
//============
void setup() {
Serial.begin(9600);
Serial.println();
for (int i = 1; i < nSwitches; i++) {
config = config + ',' + String(i+1) + CONFIG0;
}
for (int j = 0; j < nSwitches; j++) {
DEVINFO[j] = SERIALNOLONG[j] + "," + MODEL;
// <switch serial number>,<switch model number>
//123456677,OSW5205
for (int i = 0; i < nPins; i++) {
pinMode(i + firstPins[j], OUTPUT);
}
}
for (int i = 0; i < nLEDs; i++) {
pinMode(i + firstLEDPin, OUTPUT);
}
}
//============
void loop() {
char receivedChars[numChars];
recvDataS rB;
parsedDataS rP;
char tempChars[numChars]; // temporary array for use when parsing
rB = recvWithStartEndMarkers();
if (rB.newData == true) {
strcpy(tempChars, rB.receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
rP = parseData(tempChars);
selectCommand(rP);
}
else {
delay(200);
}
}
recvDataS recvWithStartEndMarkers() {
boolean newDataRecv = false; // newDataRecv = newData
char receivedChars[numChars];
recvDataS rbuf;
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarkerCCQH = '*';
//Common Command and Query Headers
char startMarkerIQH = ':';
// Instrument-Control Headers
char endMarker = '\n';
char rc;
while (Serial.available() > 0 && newDataRecv == false) {
rc = Serial.read();
rc = toupper(rc);
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newDataRecv = true;
}
}
else if (rc == startMarkerCCQH || rc == startMarkerIQH) {
recvInProgress = true;
}
}
strcpy(rbuf.receivedChars, receivedChars);
rbuf.newData = newDataRecv;
return rbuf;
}
//============
parsedDataS parseData(char tempChars[numChars]) { // split the data into its parts
parsedDataS pbuf;
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars, " "); // get the first part - the string
strcpy(pbuf.command, strtokIndx); // copy it to command
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
pbuf.device = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
pbuf.inputChannel = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
pbuf.outputChannel = atoi(strtokIndx); // convert this part to an integer
return pbuf;
}
//============
void selectCommand(parsedDataS pBuf) {
if (strcmp(pBuf.command, "IDN?") == 0) {
// *idn?
Serial.println(IDN);
}
else if (strcmp(pBuf.command, "ROUT:CLOS") == 0) {
// :ROUTe:CLOSe <D>,<Input>,<Output>
// :ROUT:CLOS 1,1,20
routeClose(pBuf);
}
else if (strcmp(pBuf.command, "ROUT:CLOS?") == 0) {
// :ROUTe:CLOSe? <D>,<Input>
// <Output>
queryRouteClose(pBuf);
}
else if (strcmp(pBuf.command, "CONF?") == 0) {
// :CONF?
// :CONFig?
Serial.println(config);
}
else if (strcmp(pBuf.command, "INFO?") == 0) {
// :INFOrmation?
// :INFO?
Serial.println(INFO);
}
else if (strcmp(pBuf.command, "DEV:INFO?") == 0) {
// :DEVice:INFOrmation? <D>
if ( inRange(pBuf.device, 1, nSwitches) ) {
// check <D> in range
Serial.println(DEVINFO[pBuf.device - 1]);
}
else {
errornumber = 2;
}
}
else if (strcmp(pBuf.command, "SYST:ERR?") == 0) {
querySysErr();
}
else if (strcmp(pBuf.command, "STB?") == 0) {
// *STB?
Serial.println("*STB?");
Serial.println((errornumber > 0) << 5);
}
else if (strcmp(pBuf.command, "COMPILED?") == 0) {
Serial.print(__FILE__);
Serial.print(",");
Serial.print (F(__DATE__));
Serial.print (" ") ;
Serial.println (F(__TIME__));
}
else {
errornumber = 1;
}
}
//============
bool inRange(int val, int minimum, int maximum)
{
return ((minimum <= val) && (val <= maximum));
}
//============
void routeClose(parsedDataS pBuf) {
if ( inRange(pBuf.device, 1, nSwitches) ) {
// Erweiterung device == 0 -> alle Schalter schalten
if ( inRange(pBuf.inputChannel, 1, 1) ) {
// vorerst haben alle Schalter nur einen inputChannel
if ( inRange(pBuf.outputChannel, minimumValue, maximumValue) ) {
// check D, input, output in range
setdigitalout(pBuf.device, pBuf.outputChannel);
}
else {
errornumber = 2;
}
}
else {
errornumber = 2;
}
}
else {
errornumber = 3;
}
}
//============
void queryRouteClose(parsedDataS pBuf) {
// check D,Input in range
// check Output plausible
int readOutputChannel = -1;
if ( inRange(pBuf.device, 1, nSwitches) ) {
if ( inRange(pBuf.inputChannel, 1, 1) ) {
readOutputChannel = getdigitalout(pBuf.device);
// Plausibilitätsprüfung
if (!inRange(readOutputChannel, minimumValue, maximumValue) ) {
readOutputChannel = -1;
errornumber = 4;
}
}
else {
errornumber = 2;
}
}
else {
errornumber = 3;
}
Serial.println(readOutputChannel);
}
//============
void querySysErr() {
errornumber = abs(errornumber); // Vorbeugung
Serial.print(errN[errornumber]);
Serial.print(", ");
Serial.println(errText[errornumber]);
errornumber = 0;
}
//============
void setdigitalout(int device, int switch_pos) {
int l;
int g;
int a = oswPattern[switch_pos - 1];
for (int j = 0; j < nTries; j++) {
for (int i = 0; i < nPins; i++) {
digitalWrite(i + firstPins[device - 1], (a & 1 << i));
}
g = getdigitalout(device);
if (switch_pos == g) {
break;
}
}
if (switch_pos != g) {
errornumber = 6; // Abweichung Soll/Ist digitale Ausgänge
}
for (int i = 0; i < nLEDs; i++) {
if ((i+1) == ((g-1) % (nLEDs)+1)) { // falls switch_pos > 4 wiederholt sich das Muster
l = 1;
}
else {
l = 0;
}
digitalWrite(firstLEDPin+i,l);
}
}
//============
int getdigitalout(int device) {
int val = 0;
int i = 0;
for (i = 0; i < nPins; i++) {
val = val + (digitalRead(firstPins[device - 1] + i) << (i));
}
for (i = 0; i < len; i++) {
if (oswPattern[i] == val) {
break;
}
}
if (i+1>len) { // kein oswPattern == val -> return -1
i = -2;
}
return i + 1;
}