Skip to content

Instantly share code, notes, and snippets.

@Mictronics
Last active April 4, 2023 06:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Mictronics/6cc804749f014d5f73fc659f158ef01a to your computer and use it in GitHub Desktop.
Save Mictronics/6cc804749f014d5f73fc659f158ef01a to your computer and use it in GitHub Desktop.
Readout distance from a Sndway Laser Distance Meter SW-A40 with Arduino Mini
/**
* Reading actual measured distance from LCD of a Sndway SW-A40 laser distance meter.
*
* Developed and tested on Arduino Mini 328P@16MHz.
* See https://www.mictronics.de/2018/02/laser-distance-meter-hack/
* Mictronics 2018 @ www.mictronics.de
*/
/**
* Pin connection to LCD SPI bus.
*/
const byte clkPin = 2; // LCD clock signal = IO2
const byte dataPin = 4; // LCD data signal = IO4
const byte csPin = 3; // LCD chip select signal = IO3
const word lutNumber[] = {'8', '0', '9', '6', '5', '3', '2', '4', '7', '1'};
const word lutFrame0[] = {0x0006, 0x0006, 0x0004, 0x0006, 0x0004, 0x0004, 0x0002, 0x0004, 0x0004, 0x0004};
const word lutFrame1[] = {0x0006, 0x0004, 0x0006, 0x0002, 0x0002, 0x0006, 0x0006, 0x0006, 0x0004, 0x0004};
const word lutFrame2[] = {0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0000, 0x0000, 0x0000};
const word lutFrame3[] = {0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0004, 0x0004, 0x0002, 0x0004, 0x0000};
const word digit5 = 0x0002;
const word sign = 0x0001;
const word unit_ft = 0x1000;
const word unit_in = 0x4800;
const word unit_m = 0xC000;
const byte laserOn = 0x08;
const byte reference = 0x04;
byte lcdFrameBuffer[70]; // Holding a single LCD frame, containing 4 sub-frames
byte data = 0; // Holding a single data byte
byte bitCount = 8; // Counting bits in each data byte
byte byteCount = 0; // Counting bytes in lcdFrame
char serialOutBuffer[20];
byte serialOutIndex = 0;
void setup() {
Serial.begin(115200);
Serial.println("Sndway Rangefinder v1.0");
pinMode(clkPin, INPUT);
pinMode(dataPin, INPUT);
pinMode(csPin, INPUT);
/* External interrupt 0&1 enable on rising edge. */
EICRA = _BV(ISC11) | _BV(ISC10) | _BV(ISC01) | _BV(ISC00) ;
EIMSK = _BV(INT1) | _BV(INT0);
}
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 = 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 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 < 10; 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((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';
Serial.write(serialOutBuffer, serialOutIndex);
serialOutIndex = 0;
}
}
/**
* 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