{"id":2055,"date":"2025-01-05T11:08:46","date_gmt":"2025-01-05T00:08:46","guid":{"rendered":"https:\/\/gonzos.net\/projects\/?page_id=2055"},"modified":"2025-07-27T22:09:55","modified_gmt":"2025-07-27T12:09:55","slug":"lidar-sensor-hls-lfcd2","status":"publish","type":"page","link":"https:\/\/gonzos.net\/projects\/lidar-sensor-hls-lfcd2\/","title":{"rendered":"LIDAR Sensor HLS-LFCD2"},"content":{"rendered":"\n<p class=\"has-text-align-right\">Updated 14 June 2025<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/HLS-LFCD2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"599\" height=\"233\" src=\"https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/HLS-LFCD2.png\" alt=\"\" class=\"wp-image-2056\" srcset=\"https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/HLS-LFCD2.png 599w, https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/HLS-LFCD2-300x117.png 300w\" sizes=\"auto, (max-width: 599px) 100vw, 599px\" \/><\/a><\/figure>\n\n\n\n<p>This is the Hitachi-LG HLS-LFCD2 2-dimensional LIDAR sensor. It is available cheaply on eBay. I wanted to run it using an NodeMCU ESP32-S processor and I ran into a few issues. Hopefully this helps others.<\/p>\n\n\n\n<p>The datasheet for the sensor is <a href=\"https:\/\/gonzos.net\/LIDAR\/HLS-LFCD2%20-LDS_Basic_Specification.pdf\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Connections:<\/h2>\n\n\n\n<p>The connections shown below are from the datasheet above. <\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><a href=\"https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/LIDAR-wiring.png\"><img loading=\"lazy\" decoding=\"async\" width=\"811\" height=\"699\" src=\"https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/LIDAR-wiring.png\" alt=\"\" class=\"wp-image-2064\" style=\"width:500px\" srcset=\"https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/LIDAR-wiring.png 811w, https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/LIDAR-wiring-300x259.png 300w, https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/LIDAR-wiring-768x662.png 768w, https:\/\/gonzos.net\/projects\/wp-content\/uploads\/2025\/01\/LIDAR-wiring-730x629.png 730w\" sizes=\"auto, (max-width: 811px) 100vw, 811px\" \/><\/a><\/figure>\n\n\n\n<p>Points to note:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>There are two cable sets coming out of the unit. One is for the motor, and one set is for the LDS sensor controller. I cut the plugs off (after photographing them) and soldered the wires to the ESP32.<\/li>\n\n\n\n<li>You need to provide 5V to the processor and to the motor (both red wires, pins 6 and 2 above). They can be connected together.<\/li>\n\n\n\n<li>The on-board controller provides the PWM control for the motor speed. You don&#8217;t need to provide an output on the ESP32 to do that. Some other similar sensors need a PWM output from the host &#8211; this one doesn&#8217;t. Just connect the (orange, pin 4) PWM wire from the sensor controller to the (black, pin 1) PWM wire from the motor.<\/li>\n\n\n\n<li>Connect the (black, pin 3) GND wire to the ESP32 ground.<\/li>\n\n\n\n<li>The wire labelled TX (brown, pin 5) sends data from the sensor to the host (ESP32), so you need to connect that to the Rx pin on the host<\/li>\n\n\n\n<li>The wire labelled RX (green, pin 2) receives data from the host to turn the sensor on and off, so you need to connect that to the Tx pin on the host. <\/li>\n\n\n\n<li>The blue wire (pin 1) does nothing and doesn&#8217;t need to be connected.<\/li>\n\n\n\n<li>The host processor needs to be 3V3 serial, not 5V. The ESP32 is 3V3, so that&#8217;s OK. An Arduino Uno, for instance, is 5V, which isn&#8217;t OK.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Programming:<\/h2>\n\n\n\n<p>The serial connection is at 230400BAUD, 8N1.<\/p>\n\n\n\n<p>The data will start to flow a couple of seconds after the sensor has been powered up. You can stop and start the sensor operation by sending &#8220;e&#8221; and &#8220;b&#8221; out the host Tx pin. I assume those commands are short for &#8220;end&#8221; and &#8220;begin&#8221;. If you want to send these codes from the Arduino IDE running on your desktop, you&#8217;ll have to put in some code to redirect the serial monitor input to the host&#8217;s serial output to the LIDAR.<\/p>\n\n\n\n<p>The table in the datasheet shows the format of each data set. Every dataset needs checking. I found that around one in 15 datasets is faulty. I don&#8217;t know why, but you can filter them out easily.<\/p>\n\n\n\n<p>The first thing to find in the data stream is the start flag. Each dataset starts with a &#8220;250&#8221; byte. So, you check each byte as it arrives, and when a 250 is received then that is the start of the next set.<\/p>\n\n\n\n<p>The data is in a 42-byte set, including the start flag and the checksums. The last two bytes are checksums. The idea is you calculate the checksum for each set and compare it to the last two bytes. All three values should be the same. If not, discard the the dataset. Note that occasionally you will be missing the start flag. You need to allow for that in your programming.<\/p>\n\n\n\n<p>The calculation is: Checksum = 255  &#8211; (sum of first 40 bytes) % 256 <\/p>\n\n\n\n<p>A new dataset is produced for each 6 degrees of rotation of the sensor, so there are 60 datasets per 360-degree revolution. The first data byte after the start flag contains the angle that the particular dataset refers to. The value of that byte actually starts at 160 (0xA0), not zero, so you need to subtract 160 to make sense of the angle. Contained within each dataset are six separate values of detected distance and intensity. These are for the angle in the first byte, plus values for the following five angles (one degree of rotation apart). These are referred to in the datasheet as &#8220;angle offsets&#8221;. <\/p>\n\n\n\n<p>Example program below:<\/p>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#e5eaf1;font-size:10px\"><code>\/*\nThis program runs a NodeMCU-32S connected to a Hitachi-LG HLS-LFCD2 2-dimensional LIDAR sensor\n\nIt collects the distance values in a data vector. The data is checked and, if OK,\nthen it adds the data to a data field array with one value for each 6 degrees of rotation.\nThe data is aggregated for each six-degree segment, and is smoothed\n\nNote that in each data vector the sensor produces data for six 1-degree segments of rotation, but for this application that\nlevel of accuracy is not needed\n\nA check is kept on the number of good reads between bad reads and this is displayed on the monitor\n\n*\/\n\n\nconst uint8_t LIDAR_GPIO_RX = 16; \/\/ ESP32 GPIO pin connected to Lidar TX pin\nconst uint8_t LIDAR_GPIO_TX = 17; \/\/ ESP32 GPIO pin connected to Lidar RX pin\n\n\nint SERIAL_BAUD = 230400;       \/\/ baud rate for both the serial monitor and LIDAR comms\nint BYTECOUNT = 0;              \/\/ for counting the number of bytes in each dataset from the LIDAR\nint DATAVECTOR&#91;42];             \/\/ the dataset as sent from the LIDAR\nint DATAFIELD&#91;60];              \/\/ the storage array\nconst int TRIPVALUE = 1000;     \/\/ if objects are closer than this value in mm, then it shows as a visual \"block\" on the monitor\nconst float ALPHA = 0.80;       \/\/ for smoothing the distance values - % of new value \nconst float BETA = 0.01;        \/\/ for smoothing out the number of good reads before a bad one. Needs to be a low value\nint GOODREADCOUNT = 0;          \/\/ the number of good reads between bad reads\nfloat AVGGOODREAD = 10.0;       \/\/ the smoothed number of good reads\nint DISPLAYTIME = 1000;         \/\/ milliseconds beween displays of data on the serial monitor\n\nHardwareSerial LidarSerial(2);  \/\/ set up the ESP32 serial port to the LIDAR\n\nvoid setup() {\n\n  delay(3000);\n  Serial.begin(SERIAL_BAUD);\n  LidarSerial.begin(SERIAL_BAUD,SERIAL_8N1, LIDAR_GPIO_RX, LIDAR_GPIO_TX);\n  Serial.println(\"Starting Lidar...\");\n  LidarSerial.print(\"b\");  \/\/ start the sensor rotation\n  for (int I = 0; I &lt;60 ;I++){ DATAFIELD&#91;I]= 5000;}  \/\/ populate the datafield with a high value\n}\n\nvoid PRINTDATAFIELD(){\n  for (int I= 59; I &gt;-1; I--){ \n    if (DATAFIELD&#91;I] &lt; TRIPVALUE){  \/\/ print the visual representation\n      Serial.print(\"XXXX\");\n    } else {Serial.print(\"    \");}\n    Serial.print(\" \");\n  }\n  Serial.println(\"\");\n  for (int I= 59; I &gt;-1; I--){      \/\/ print the index number \n    Serial.printf(\"%04d\",I);\n    Serial.print(\" \");\n  }\n  Serial.println(\"\");\n  for (int I= 59; I &gt;-1; I--){     \/\/ print the distance value\n    Serial.printf(\"%04d\",DATAFIELD&#91;I]);\n    Serial.print(\" \");\n  }\n  Serial.println(\"\");\n  Serial.print(\"Avg good reads: \");\n  Serial.println(AVGGOODREAD);\n  \n}\n\nvoid loop() {\n  \/\/ Main code\n  if (LidarSerial.available()){\n    BYTECOUNT += 1;\n    int READVALUE = LidarSerial.read();\n    if (READVALUE == 250) {        \/\/ flag for new data vector\n      int CHECKSUM = 0;\n      for  (int I = 0; I &lt; 40 ; I++) {CHECKSUM += DATAVECTOR&#91;I];}\n      CHECKSUM = 255 - (CHECKSUM % 256);\n      if (CHECKSUM == DATAVECTOR&#91;40] and CHECKSUM == DATAVECTOR&#91;41]){ \/\/ good read\n        GOODREADCOUNT += 1;\n        int INDEX = DATAVECTOR&#91;1]-160 ;  \/\/ the data field 0 to 60  (360 degrees of rotation)\n        int NEWVALUE = 0;\n        if (INDEX &gt;= 0 and INDEX &lt; 60){  \/\/ check that the index value is valid\n          for (int I = 0; I &lt; 6; I++){   \n          NEWVALUE += (DATAVECTOR&#91;6 + I * 6] + DATAVECTOR&#91;7 + I * 6]*256);  \/\/ add up all the angle offset values\n          }\n          if (NEWVALUE &gt; 300 ) {  \/\/ not interested in distances less than this\n            DATAFIELD&#91;INDEX] = DATAFIELD&#91;INDEX] * (1.0-ALPHA) + NEWVALUE\/6 * ALPHA; \/\/  smooth the averaged data\n          }  \n        }\n\n      }\n      else {\n        AVGGOODREAD = AVGGOODREAD * (1.0-BETA) + (float)GOODREADCOUNT * BETA;  \/\/ smoothing\n        GOODREADCOUNT = 0;\n      }\n\n      BYTECOUNT =  0;\n    }\n    if (BYTECOUNT &lt; 42){ \/\/ save data to data vector\n      DATAVECTOR&#91;BYTECOUNT] = READVALUE;\n    }\n    }\n  if (Serial.available() &gt; 0) {LidarSerial.write(Serial.read());}  \/\/ transfer any commands from Serial Monitor\n\n  if (millis() % DISPLAYTIME == 0){PRINTDATAFIELD();  }    \/\/ print the data field on the serial monitor\n\n\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Updated 14 June 2025 This is the Hitachi-LG HLS-LFCD2 2-dimensional LIDAR sensor. It is available cheaply on eBay. I wanted to run it using an NodeMCU ESP32-S processor and I ran into a few issues. Hopefully this helps others. The datasheet for the sensor is here. Connections: The connections shown below are from the datasheet [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2055","page","type-page","status-publish","hentry"],"blocksy_meta":{"page_structure_type":"type-3","styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":6}},"_links":{"self":[{"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/pages\/2055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/comments?post=2055"}],"version-history":[{"count":1,"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/pages\/2055\/revisions"}],"predecessor-version":[{"id":2180,"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/pages\/2055\/revisions\/2180"}],"wp:attachment":[{"href":"https:\/\/gonzos.net\/projects\/wp-json\/wp\/v2\/media?parent=2055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}