Created
July 23, 2018 08:52
-
-
Save Mictronics/be511fde25391e3bbacd5878f44d3b6d to your computer and use it in GitHub Desktop.
Controls and reads actual measured distance from LCD bus of a Sndway SW-A40 laser distance meter. Improved version not using Arduino delay that causes random problems with serial interface.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Controls and reads actual measured distance from LCD bus of a Sndway SW-A40 laser distance meter. | |
* Improved version not using Arduino delay that causes random problems with serial interface. | |
* | |
* Serial control characters: | |
* | |
* ? Return identifier string. | |
* c Clears measurement results and turns off laser. | |
* Can be used as keep alive command to prevent auto power off after 150s. | |
* M Turns on device when off and triggers a measurement. | |
* S Start continuous measurement mode. Device must be turned on prior to this command. | |
* Command c or M will stop that mode. | |
* L Turns on laser but not triggering measurement. | |
* o Turns off device. | |
* u Cycles distance unit. | |
* r Cycle reference of measurement. | |
* R Get reference of measurement. | |
* f = front | |
* m = middle | |
* b = back (default) | |
* | |
* Make sure your Arduino Mini board is 3V compatible! By default it is not! | |
* | |
* Developed and tested on Arduino Mini 328P@16MHz. | |
* Mictronics 2018 @ www.mictronics.de | |
*/ | |
#include <avr/wdt.h> | |
/** | |
* Pin connection to LCD SPI bus. | |
*/ | |
#define CLKPIN 2 // LCD clock signal = IO2 | |
#define DATAPIN 4 // LCD data signal = IO4 | |
#define CSPIN 3 // LCD chip select signal = IO3 | |
/** | |
* Pin connection for device control. | |
* Key signals are low active. | |
*/ | |
#define UNITKEY 5 // Key changing units = IO5 | |
#define MEASUREKEY 6 // Key switching device ON and taking measurement = IO6 | |
#define CLEARKEY 7 // Key switching device OFF and clearing measurement = IO7 | |
#define REFKEY 8 // Key changing the measurement reference = IO8 | |
const word lutNumber[] = {'8', '0', '9', '6', '5', '3', 'E', '2', '4', '7', '1', 'r', '-'}; | |
const word lutFrame0[] = {0x0006, 0x0006, 0x0004, 0x0006, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0004, 0x0002, 0x0000}; | |
const word lutFrame1[] = {0x0006, 0x0004, 0x0006, 0x0002, 0x0002, 0x0006, 0x0002, 0x0006, 0x0006, 0x0004, 0x0004, 0x0002, 0x0002}; | |
const word lutFrame2[] = {0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; | |
const word lutFrame3[] = {0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0004, 0x0006, 0x0004, 0x0002, 0x0004, 0x0000, 0x0000, 0x0000}; | |
#define DIGIT5 0x0002 | |
#define SIGN 0x0001 | |
#define UNIT_ft 0x1000 | |
#define UNIT_in 0x4800 | |
#define UNIT_m 0xC000 | |
#define REF 0x04 | |
#define LASERON 0x08 | |
byte lcdFrameBuffer[70]; | |
byte data = 0; | |
byte bitCount = 8; | |
byte byteCount = 0; | |
char serialOutBuffer[20]; | |
byte serialOutIndex = 0; | |
bool takeMeasurement = false; | |
volatile bool laserIsOn = false; | |
volatile bool deviceIsOn = false; | |
unsigned long lcdTimeout; | |
char reference = 'b'; | |
unsigned long keyTimeout = 0; | |
unsigned long measureKeyDelay = 0; | |
void setup() { | |
pinMode(CLKPIN, INPUT); | |
pinMode(DATAPIN, INPUT); | |
pinMode(CSPIN, INPUT); | |
digitalWrite(UNITKEY, HIGH); | |
digitalWrite(MEASUREKEY, HIGH); | |
digitalWrite(CLEARKEY, HIGH); | |
digitalWrite(REFKEY, HIGH); | |
pinMode(UNITKEY, OUTPUT); | |
pinMode(MEASUREKEY, OUTPUT); | |
pinMode(CLEARKEY, OUTPUT); | |
pinMode(REFKEY, OUTPUT); | |
Serial.begin(38400); | |
Serial.setTimeout(100); | |
lcdTimeout = millis(); | |
wdt_enable(WDTO_4S); | |
/* External interrupt 0&1 enable on rising edge. */ | |
EICRA = _BV(ISC11) | _BV(ISC10) | _BV(ISC01) | _BV(ISC00) ; | |
EIMSK = _BV(INT1) | _BV(INT0); | |
} | |
static void clickKey(byte pin, unsigned long t){ | |
digitalWrite(pin, LOW); | |
keyTimeout = millis() + t; | |
} | |
void loop() { | |
word frame0 = 0, frame1 = 0, frame2 = 0, frame3 = 0; | |
word mask0 = 0, mask1 = 0, mask2 = 0, mask3 = 0; | |
/* Skip LCD init frame and start reception inbetween two frames */ | |
if((byteCount > 2) && (lcdFrameBuffer[0] != 0x10) && (lcdFrameBuffer[1] != 0x00)){ | |
delay(50); | |
byteCount = bitCount =0; | |
} | |
/* Process a complete LCD frame */ | |
if(byteCount == 68){ | |
noInterrupts(); | |
// Check for correct frame order | |
if((lcdFrameBuffer[0] == 0x10) &&(lcdFrameBuffer[1] == 0x00) &&(lcdFrameBuffer[2] == 0xC0)){ | |
// Save last LCD line data to work variables | |
frame0 = lcdFrameBuffer[10]; | |
frame0 <<= 8; | |
frame0 += lcdFrameBuffer[11]; | |
frame1 = lcdFrameBuffer[27]; | |
frame1 <<= 8; | |
frame1 += lcdFrameBuffer[28]; | |
frame2 = lcdFrameBuffer[44]; | |
frame2 <<= 8; | |
frame2 += lcdFrameBuffer[45]; | |
frame3 = lcdFrameBuffer[61]; | |
frame3 <<= 8; | |
frame3 += lcdFrameBuffer[62]; | |
// Get measurement reference | |
if((lcdFrameBuffer[5] & REF)){ | |
reference = 'm'; | |
} else if((lcdFrameBuffer[22] & REF)){ | |
reference = 'b'; | |
} else if((lcdFrameBuffer[39] & REF)){ | |
reference = 'f'; | |
} else { | |
reference = '?'; | |
} | |
} | |
/* Get ready to receive new frame */ | |
byteCount = 0; | |
interrupts(); | |
// Process last frame, get distance and unit from LCD data | |
// Sign | |
if((frame0 & SIGN) == SIGN){ serialOutBuffer[serialOutIndex++] = '-';} | |
// Digit 5 | |
if((frame2 & DIGIT5) == DIGIT5){ serialOutBuffer[serialOutIndex++] = '1';} | |
// Remaining 5 digits | |
for(byte d = 0; d < 5; d++){ | |
for(byte n = 0; n < 13; n++){ | |
mask0 = lutFrame0[n]; | |
mask1 = lutFrame1[n]; | |
mask2 = lutFrame2[n]; | |
mask3 = lutFrame3[n]; | |
mask0 = (lutFrame0[n] << (d*2)); | |
mask1 = (lutFrame1[n] << (d*2)); | |
mask2 = (lutFrame2[n] << (d*2)); | |
mask3 = (lutFrame3[n] << (d*2)); | |
if(((frame0 & mask0) == mask0) && ((frame1 & mask1) == mask1) && ((frame2 & mask2) == mask2) && ((frame3 & mask3) == mask3)){ | |
serialOutBuffer[serialOutIndex++] = lutNumber[n]; | |
break; | |
} | |
} | |
// Decimal points | |
if((d == 1) && ((frame2 & 0x0020) == 0x0020)){ serialOutBuffer[serialOutIndex++] = '.'; } | |
else if((d == 2) && ((frame2 & 0x0080) == 0x0080)){ serialOutBuffer[serialOutIndex++] = '.'; } | |
else if((d == 3) && ((frame2 & 0x0200) == 0x0200)){ serialOutBuffer[serialOutIndex++] = '.'; } | |
} | |
// Unit | |
if((serialOutBuffer[0] != 'E') && (serialOutBuffer[0] != '-')){ | |
if((frame2 & UNIT_ft) == UNIT_ft){ | |
serialOutBuffer[serialOutIndex++] = 'f'; | |
serialOutBuffer[serialOutIndex++] = 't'; | |
} else if((frame2 & UNIT_in) == UNIT_in){ | |
serialOutBuffer[serialOutIndex++] = 'i'; | |
serialOutBuffer[serialOutIndex++] = 'n'; | |
} else if((frame2 & UNIT_m) == UNIT_m){ | |
serialOutBuffer[serialOutIndex++] = 'm'; | |
} | |
} | |
serialOutBuffer[serialOutIndex++] = '\n'; | |
if(serialOutIndex > 2){ | |
Serial.write(serialOutBuffer, serialOutIndex); | |
} | |
serialOutIndex = 0; | |
deviceIsOn = true; | |
} | |
// Device auto turns off after 150s | |
if(deviceIsOn && ((millis()-lcdTimeout) > 150000)){ | |
laserIsOn = false; | |
deviceIsOn = false; | |
Serial.println("off"); | |
} | |
if(keyTimeout && (millis() > keyTimeout)){ | |
keyTimeout = 0; | |
digitalWrite(UNITKEY, HIGH); | |
digitalWrite(MEASUREKEY, HIGH); | |
digitalWrite(CLEARKEY, HIGH); | |
digitalWrite(REFKEY, HIGH); | |
} | |
if(measureKeyDelay && (millis() > measureKeyDelay)){ | |
measureKeyDelay = 0; | |
clickKey(MEASUREKEY, 30); | |
} | |
wdt_reset(); | |
} | |
void serialEvent(){ | |
/* Get and process serial input */ | |
char c = (char)Serial.read(); | |
switch(c){ | |
// Return device id | |
case '?': | |
Serial.println("Sndway Rangefinder v1.1"); | |
break; | |
// Clear measurement result and switch off laser | |
case 'c': | |
clickKey(CLEARKEY, 30); | |
laserIsOn = false; | |
lcdTimeout = millis(); | |
break; | |
// Switch on device and take measurement | |
case 'M': | |
// Switch on device | |
if(deviceIsOn == false){ | |
laserIsOn = false; | |
clickKey(MEASUREKEY, 30); | |
measureKeyDelay = millis() + 2000; | |
break; | |
} | |
// Trigger laser on | |
if(laserIsOn == false){ | |
clickKey(MEASUREKEY, 30); | |
measureKeyDelay = millis() + 800; | |
break; | |
} | |
clickKey(MEASUREKEY, 30); | |
lcdTimeout = millis(); | |
laserIsOn = false; | |
break; | |
case 'S': | |
clickKey(MEASUREKEY, 1500); | |
laserIsOn = true; | |
break; | |
// Switch on laser | |
case 'L': | |
if(laserIsOn == false){ | |
clickKey(MEASUREKEY, 30); | |
laserIsOn = true; | |
lcdTimeout = millis(); | |
} | |
break; | |
// Switch off device | |
case 'o': | |
Serial.println("off"); | |
clickKey(CLEARKEY, 2000); | |
laserIsOn = false; | |
deviceIsOn = false; | |
break; | |
// Cycle unit | |
case 'u': | |
clickKey(UNITKEY, 30); | |
lcdTimeout = millis(); | |
break; | |
case 'r': | |
clickKey(REFKEY, 30); | |
lcdTimeout = millis(); | |
break; | |
case 'R': | |
Serial.println(reference); | |
break; | |
default: | |
break; | |
} | |
} | |
/** | |
* Reads the data pin on CLK rising edge. | |
*/ | |
ISR(INT0_vect) { | |
if(digitalRead(CSPIN) == LOW) { | |
if(bitCount == 8) { | |
// Skip data/command identifier bit | |
--bitCount; | |
return; | |
} | |
asm volatile("nop;"); | |
data |= (digitalRead(DATAPIN) << bitCount); | |
--bitCount; | |
} | |
} | |
/** | |
* Save a single data byte to LCD frame buffer on CS rising edge. | |
*/ | |
ISR(INT1_vect) { | |
lcdFrameBuffer[byteCount] = data; | |
++byteCount; | |
data = 0; | |
bitCount = 8; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment