I new very cheap laser range finder, low resolution (0.6m) but if that is not a problem maybe worth a look. It seems to have a few different names like my seller calls everything L22A but on the unit it clearly states JC02-1 my on has -10 and I think that is 1000 meter range, it seems to come in 600, 800, 1000, 1200
The documentation is not great as usual for these type of chinese modules
Here is my ESP32 Arduino code, it has one small bug in that it displays xx.x off to one side but xxx and x.x numbers are good. Other wise you could use a TFT type display or something else.
#include <HardwareSerial.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_GFX.h>
#define LASER_RX 16
#define LASER_TX 17
#define MATRIX_PIN 13
#define BAUD_RATE 115200
// JC02-1 continuous measurement command
const uint8_t CONTINUOUS_CMD[] = {0xAE, 0xA7, 0x04, 0x00, 0x0E, 0x12, 0xBC, 0xBE};
HardwareSerial LaserSerial(2);
// YOUR WORKING MATRIX CONFIG (do not change)
#define PANEL_WIDTH 10
#define PANEL_HEIGHT 10
#define MATRIX_WIDTH (PANEL_WIDTH * 2) // 20 pixels
#define MATRIX_HEIGHT (PANEL_HEIGHT * 1) // 10 pixels
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(PANEL_WIDTH, PANEL_HEIGHT, 2, 1, MATRIX_PIN,
NEO_TILE_TOP + NEO_TILE_LEFT +
NEO_TILE_COLUMNS + NEO_MATRIX_ROWS,
NEO_GRB + NEO_KHZ800);
void setup() {
Serial.begin(115200);
LaserSerial.begin(BAUD_RATE, SERIAL_8N1, LASER_RX, LASER_TX);
matrix.begin();
matrix.setRotation(0);
matrix.setBrightness(60);
matrix.setTextWrap(false);
Serial.println("\n=== JC02-1 NeoMatrix Distance Display ===\n");
// Start continuous measurement
LaserSerial.write(CONTINUOUS_CMD, sizeof(CONTINUOUS_CMD));
delay(800);
}
void loop() {
readDistance();
delay(100);
}
void readDistance() {
if (LaserSerial.available() < 27) return;
uint8_t buffer[32];
int len = LaserSerial.readBytes(buffer, 27);
// Validate JC02-1 frame
if (len < 27 || buffer[0] != 0xAE || buffer[1] != 0xA7 || buffer[4] != 0x85) {
while (LaserSerial.available()) LaserSerial.read();
return;
}
// Extract distance (0.1m units)
int16_t distRaw = (buffer[7] << 8) | buffer[8];
float distance = distRaw * 0.1;
Serial.printf("Distance: %.1f m\n", distance);
// ---- FORMAT TEXT TO ALWAYS FIT ----
String text;
if (distance < 100.0) {
// xx.x (3 chars max, e.g. "9.8", "12.3", "99.9")
text = String(distance, 1);
} else {
// xxx (e.g. "100", "123")
text = String((int)distance);
}
// ---- DRAW ----
matrix.fillScreen(0); // hard clear every frame
matrix.setTextSize(1);
matrix.setTextColor(matrix.Color(255, 0, 0));
// Each char is 6px wide at size 1 (5 + 1 spacing)
int16_t textWidth = text.length() * 6;
int16_t x = (20 - textWidth) / 2;
if (x < 0) x = 0;
// Vertically: 8px tall font in 10px height → y = 1 looks good
matrix.setCursor(x, 1);
matrix.print(text);
matrix.show();
}
#include <HardwareSerial.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_GFX.h>
#define LASER_RX 16
#define LASER_TX 17
#define MATRIX_PIN 13
#define BAUD_RATE 115200
// JC02-1 continuous measurement command
const uint8_t CONTINUOUS_CMD[] = {0xAE, 0xA7, 0x04, 0x00, 0x0E, 0x12, 0xBC, 0xBE};
HardwareSerial LaserSerial(2);
// YOUR WORKING MATRIX CONFIG (do not change)
#define PANEL_WIDTH 10
#define PANEL_HEIGHT 10
#define MATRIX_WIDTH (PANEL_WIDTH * 2) // 20 pixels
#define MATRIX_HEIGHT (PANEL_HEIGHT * 1) // 10 pixels
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(PANEL_WIDTH, PANEL_HEIGHT, 2, 1, MATRIX_PIN,
NEO_TILE_TOP + NEO_TILE_LEFT +
NEO_TILE_COLUMNS + NEO_MATRIX_ROWS,
NEO_GRB + NEO_KHZ800);
void setup() {
Serial.begin(115200);
LaserSerial.begin(BAUD_RATE, SERIAL_8N1, LASER_RX, LASER_TX);
matrix.begin();
matrix.setRotation(0);
matrix.setBrightness(60);
matrix.setTextWrap(false);
Serial.println("\n=== JC02-1 NeoMatrix Distance Display ===\n");
// Start continuous measurement
LaserSerial.write(CONTINUOUS_CMD, sizeof(CONTINUOUS_CMD));
delay(800);
}
void loop() {
readDistance();
delay(100);
}
void readDistance() {
if (LaserSerial.available() < 27) return;
uint8_t buffer[32];
int len = LaserSerial.readBytes(buffer, 27);
// Validate JC02-1 frame
if (len < 27 || buffer[0] != 0xAE || buffer[1] != 0xA7 || buffer[4] != 0x85) {
while (LaserSerial.available()) LaserSerial.read();
return;
}
// Extract distance (0.1m units)
int16_t distRaw = (buffer[7] << 8) | buffer[8];
float distance = distRaw * 0.1;
Serial.printf("Distance: %.1f m\n", distance);
// ---- FORMAT TEXT TO ALWAYS FIT ----
String text;
if (distance < 100.0) {
// xx.x (3 chars max, e.g. "9.8", "12.3", "99.9")
text = String(distance, 1);
} else {
// xxx (e.g. "100", "123")
text = String((int)distance);
}
// ---- DRAW ----
matrix.fillScreen(0); // hard clear every frame
matrix.setTextSize(1);
matrix.setTextColor(matrix.Color(255, 0, 0));
// Each char is 6px wide at size 1 (5 + 1 spacing)
int16_t textWidth = text.length() * 6;
int16_t x = (20 - textWidth) / 2;
if (x < 0) x = 0;
// Vertically: 8px tall font in 10px height → y = 1 looks good
matrix.setCursor(x, 1);
matrix.print(text);
matrix.show();
}










